[Design pattern - 실무편] Controller에서 Facade 패턴 사용하기


Study/Design Pattern  2019. 6. 14. 22:37

안녕하세요. 명월입니다.


이 글은 실무에서 MVC모델 안에서 사용하는 디자인 패턴에 대해서 설명하는 글입니다.


최근 웹 프로그램 설계의 추세는 역시 MVC가 가장 인기있는 모델이지 않을까 생각됩니다.

MVC 모델은 Model과 View와 Controller를 분리해서 작업하는 모델입니다. Model은 데이터 베이스 혹은 프로그램 안에서 사용되는 데이터 객체를 뜻하는 것이고 Controller은 그 모델 데이터를 가공하거나 계산하는 식이 Controller이고 View는 최종적으로 Controller안에서 만들어진 Model데이터를 화면 혹은 출력을 하는 부분이 View부분입니다. 이것까진 많은 사람들이 잘 알고 있는 부분입니다만 이런 좋은 모델들을 실무에서 엄청 어렵게 혹은 복잡하게 만드는 분들이 많습니다.


그럼 어떻게 사용하는지 설명하겠습니다. 저의 경우는 C# MVC까지 설명하면 글이 매우 길어지기 때문에 간단하게 Console 예제로 설명하겠습니다.

class DB
{
  public List<Model> Select()
  {
    var ret = new List<Model>();
    for (int i = 1; i <= 1000; i++)
    {
      ret.Add(new Model() { Idx = i });
    }
    return ret;
  }
}

class Model
{
  public int Idx { get; set; }
}

class Controller
{
  public View Run()
  {
    var db = new DB();
    var list = db.Select();
    return new View(list);
  }
}

class View
{
  private List<Model> model;
  public View(List<Model> model)
  {
    this.model = model;
  }
  public void Print()
  {
    foreach (var m in model)
    {
      Console.WriteLine(m.Idx);
    }
  }
}

class Program
{
  static void Main(string[] args)
  {
    var controller = new Controller();
    var view = controller.Run();
    // 결과는 1 ~ 1000
    view.Print();

    Console.WriteLine("Press any key...");
    Console.ReadKey();
  }
}

위 소스는 정말 기본적인 MVC모델을 구현한 것입니다. controller를 호출해서 view를 리턴하면 view에서는 최종적으로 Print함수를 호출해서 출력하는 형태입니다.

예제로 데이터를 데이터베이스에서 가져와서 Model에 데이터를 넣은 다음 view를 넣는 형태입니다.


첫번째 사양으로 list에서 홀수만 계산해서 list2라는 변수를 만들겠습니다.

두번째 사양으로 list에서 3의 배수만 계산해서 list3의 변수를 만들겠습니다.

세번째 사양으로 list에서 5의 배수만 계산해서 list4의 변수를 만들겠습니다.


최종적으로 list2, list3, list4에 모두 포함된 데이터를 view로 출력하겠습니다.

패턴 소개하기 위한 예제이니 C# 내부 함수나 Linq식은 사용하지 않고 for문으로만 만들겠습니다.

//DB, Model, View는 위와 같으니 생략하겠습니다.
class Controller
{
  public View Run()
  {
    var db = new DB();
    var list = db.Select();
	// 홀수를 취득한다.
    var list2 = new List<Model>();
    foreach (var m in list)
    {
      if (m.Idx % 2 == 1)
      {
        list2.Add(m);
      }
    }
	// 3의 배수를 취득한다.
    var list3 = new List<Model>();
    foreach (var m in list)
    {
      if (m.Idx % 3 == 0)
      {
        list3.Add(m);
      }
    }
	// 5의 배수를 취득한다.
    var list4 = new List<Model>();
    foreach (var m in list)
    {
      if (m.Idx % 5 == 0)
      {
        list4.Add(m);
      }
    }
	// 공통을 계산한다.
    var ret = new List<Model>();
    int idx3 = 0;
    int idx4 = 0;
    for (int i = 0; i < list2.Count; i++)
    {
      for (int j = idx3; j < list3.Count; j++)
      {
        // list2의 수보다 작은 수는 계산할 필요없다.
        if (list2[i].Idx > list3[j].Idx)
        {
          idx3 = j;
        }
        for (int z = idx4; z < list4.Count; z++)
        {
          // list2의 수보다 작은 수는 계산할 필요없다.
          if (list2[i].Idx > list4[z].Idx)
          {
            idx4 = z;
          }
          if (list2[i].Idx == list3[j].Idx && list2[i].Idx == list4[z].Idx)
          {
            ret.Add(list2[i]);
          }
        }
      }
    }
    return new View(ret);
  }
}

정말 의외로 주위를 둘러보면 요렇게 짜는 사람 엄청 많습니다. 그리고 3중 for지만 나름 알고리즘을 만들어서 성능 개선도 했습니다.

값은 틀리지 않습니다만, 일단 보기에 무지 않좋습니다. 이런 간단한 프로그램도 이리 복잡한데 실제 프로그램은 어떨까요? 예전에 저는 함수 하나의 코드 길이가 1000줄이 넘는 것도 봤습니다.


이걸 디자인 패턴에서 Facade를 쓰면 보기가 달라집니다.

/// 주석달면 더 좋고..
private List<Model> GetList2(List<Model> list)
{
  var list2 = new List<Model>();
  foreach (var m in list)
  {
    if (m.Idx % 2 == 1)
    {
      list2.Add(m);
    }
  }
  return list2;
}
/// 주석달면 더 좋고..
private List<Model> GetList3(List<Model> list)
{
  var list3 = new List<Model>();
  foreach (var m in list)
  {
    if (m.Idx % 3 == 0)
    {
      list3.Add(m);
    }
  }
  return list3;
}
/// 주석달면 더 좋고..
private List<Model> GetList4(List<Model> list)
{
  var list4 = new List<Model>();
  foreach (var m in list)
  {
    if (m.Idx % 5 == 0)
    {
      list4.Add(m);
    }
  }
  return list4;
}
/// 주석달면 더 좋고..
private List<Model> GetCalc(List<Model> list2, List<Model> list3, List<Model> list4)
{
  var ret = new List<Model>();
  int idx3 = 0;
  int idx4 = 0;
  for (int i = 0; i < list2.Count; i++)
  {
    for (int j = idx3; j < list3.Count; j++)
    {
      if (list2[i].Idx > list3[j].Idx)
      {
        idx3 = j;
      }
      for (int z = idx4; z < list4.Count; z++)
      {
        if (list2[i].Idx > list4[z].Idx)
        {
          idx4 = z;
        }
        if (list2[i].Idx == list3[j].Idx && list2[i].Idx == list4[z].Idx)
        {
          ret.Add(list2[i]);
        }
      }
    }
  }
  return ret;
}
public View Run()
{
  var db = new DB();
  var list = db.Select();

  //홀수를 취득한다.
  var list2 = GetList2(list);
  //3의 배수를 취득한다.
  var list3 = GetList3(list);
  //5의 배수를 취득한다.
  var list4 = GetList4(list);

  // 공통을 계산한다.
  var ret = GetCalc(list2, list3, list4);
  return new View(ret);
}

이번에는 Run함수를 보면 어떻습니까? 정말 깔끔하게 정리되어 있어서 Controller에서 Run이 호출되면 어떻게 처리되는지 한눈에 보입니다.

사실 GetList2,GetList3,GetList4도 한 개의 함수로 합칠수 있습니다만 제가 예시로 만든 것입니다.


결과는 같습니다. 사실 성능도 위 아래는 차이가 없네요. Calc도 사실 정렬 알고리즘 쓰면 더 심플하게 되고 Linq 사용하면 더더욱 심플하게 될 것입니다.

또 이렇게 작성하다 보면 함수도 재사용할 수 있는게 보입니다. 수학의 f(x) = x + 1 처럼 말입니다.


여기까지 MVC Contoller에서의 Facade 패턴 사용하기에 대한 설명이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.