TIP게시판

제목 템플릿 메소드 패턴을 이용한 레이아웃
글쓴이 이현석 작성시각 2013/12/24 21:31:12
댓글 : 5 추천 : 0 스크랩 : 0 조회수 : 14942   RSS
안녕하세요. 요즘 디자인 패턴을 공부 중입니다.
공부 중에 템플릿 메소드 패턴이라는 것을 알게 되었는데,
어디다 써먹으면 좋을까 생각해보니 레이아웃에 적용하면 좋을 것 같더군요.
 
실험 삼아 조금 써보고 있는데, 그냥 저냥 쓸만도 한 것 같습니다. 
그래서 템플릿 메소드 패턴을 이용해서 레이아웃을 구성하는 방법을 공유해볼까 합니다.
아직 본격적으로 써본건 아니라서 단점도 꽤나 있을거라 생각됩니다만 ㅎㅎ 그런 것들은 댓글로 논의가 이뤄져도 좋을 것 같아요.
 
일반적으로 top 과 bottom 그리고 본문으로 뷰를 분리해서 사용하실 거라고 생각됩니다.
그래서, 이번 예제 코드도 top,본문, bottom 으로 구성하는 예제를 사용하겠습니다.
 
우선 레이아웃 컨트롤러를 만듭니다.
 
abstract class Layout extends CI_Controller

{
    function __construct()
    {
        parent::__construct();
    }
    
    function template_method()
    {
        $data['title'] = $this->title;
        $this->load->view('top',$data);
        $this->load_body();
        $this->load->view('bottom');
    }

    abstract function load_body();
}

그리고 실제 뷰를 불러올 컨트롤러를 만듭니다.
 
레이아웃 컨트롤러를 상속받아서, load_body() 만 오버라이딩해서 사용합니다.
 
페이지마다 달라질 수 있는 페이지 타이틀 같은 것은 아래 처럼 public 으로 선언해서 부모 컨트롤러에서 바로 접근해서 사용하던가, 아니면 private 으로 선언한 후 get 함수를 만들어서 사용합니다.
 
require('./application/controllers/layout.php');

class Login_form extends Layout
{
    public $title = '로그인';

    private $private_title = '로그인'; 

    function __construct()
    {
        parent::__construct();
    }

    function index()
    {
        $this->template_method();
    }

    function load_body()
    {
        $this->load->view('login_form_v');
    }

    function get($var)
    {
        return isset($this->{$var}) ? $this->{$var} : '';
    }
}
 
요렇게 하고나서 YOURDOMAIN/login_form 하면 로그인 폼이 보이게 됩니다. register_form 이 필요하면 아래처럼 Layout 을 상속받는 클래스를 하나 더 만들어주면 되겠죠.
  
require('./application/controllers/layout.php');

class Register_form extends Layout
{
    public $title = '회원가입';

    function __construct()
    {
        parent::__construct();
    }

    function index()
    {
         $this->template_method();
    }

    function load_body()
    {
        $this->load->view('register_form_v');
    }
}
 
너무 간단하지요? ㅎㅎ  
너무 간단해서 올리지 말까 하는 생각도 듭니다 ㅠㅠ 
 
너무 간단하니까 다른 용례를 하나 더 다뤄보겠습니다. ^^
 이번에 볼 용례는 기본적으로 반복적으로 사용하는 코드이지만 특정한 상황에서는 해당 코드를 쓰지 않거나, 다른 코드를 쓸 필요가 있을 때의 예제입니다.
 
abstract class Layout2 extends CI_Controller

{
    function __construct()
    {
        parent::__construct();
    }

    function template_method()
    {
        $data['title'] = $this->title;
        $this->load->view('top',$data);
        $this->load_top_menu();           //Layout과 다른 부분.
        $this->load_body();
        $this->load->view('bottom');
    }

    abstract function load_body();
    
    function load_top_menu()
    {
        $this->load->view('top_menu');
    }
}

 
Layout 과의 차이는 top 밑에 top_menu 를 부르는 부분이 추가된 것입니다.
  
require('./application/controllers/layout2.php');

class Login_form2 extends Layout2
{
    public $title = '로그인';

    function __construct()
    {
        parent::__construct();
    }

    function index()
    {
        $this->template_method();
    }

    function load_body()
    {
        $this->load->view('login_form_v');
    }
}
위와 같이 Layout2를 상속받는 Login_form2 를 만들고
http://YOURDOMAIN/Login_form2 로 접속하면 

top
top_menu
login_form_v
bottom
가 불려올 것입니다.


register_form2에서는 top_menu가 불려오면 안되는 상황인 경우 아래와 같이 합니다. 
require('./application/controllers/layout2.php');

class Register_form2 extends Layout2
{
    public $title = '로그인';

    function __construct()
    {
        parent::__construct();
    }

    function index()
    {
        $this->template_method();
    }

    function load_body()
    {
        $this->load->view('register_form_v');
    }

    function load_top_menu()
    {
    } 
}

이런식으로 자식에서 load_top_menu()를 비어있는 메소드로 오버라이딩하면 
top_menu 가 생략되어
 
top
register_form_v
bottom
 
이 불려오게 됩니다.
 

만약 register_form2에서는 login_form2에서와 다른 top_menu가 필요하다면 아래와 같이 합니다.
require('./application/controllers/layout2.php');

class Register_form2 extends Layout2
{
    public $title = '로그인';

    function __construct()
    {
        parent::__construct();
    }

    function index()
    {
         $this->template_method();
    }

    function load_body()
    {
        $this->load->view('register_form_v');
    }

    function load_top_menu()
    {
        $this->load-view('top_menu_2');
    } 
}
 
이렇게 load_top_menu()를 다른 뷰를 불러오도록 오버라이딩하면 
 
top
top_menu_2
register_form_v
bottom
 
이 불려오게 되겠죠.
 
 
따라서 별다른 조치를 취하지 않으면 

top
top_menu
자식이 오버라이딩한 load_body()의 결과
bottom

이 불려오고
 
필요에 따라서 load_top_menu() 를 자식 컨트롤러에서 오버라이딩 함으로써 top_menu를 로드하거나 혹은 로드하지 않거나, 혹은 원래의 top_menu 가 아닌 다른 뷰를 로드 할 수 있게 됩니다.
 

여기서 다들 아시겠지만 혹시나 해서 load_body() 와 load_top_menu() 의 차이점에 대해 짚고 넘어가자면
load_body()는 부모 컨트롤러인 Layout 에서 abstract 으로 선언했기 때문에 자식 컨트롤러에서 반드시 구현해야 하지만
load_top_menu()는 자식 컨트롤러에서 오버라이드를 해도 되고 안해도 되는 차이가 있습니다.
따라서 레이아웃 구성에서 바뀌지 않는 내용은 템플릿 메소드에 바로 구현하고
바뀌는 내용이면서 반드시 들어가야하면 abstract 으로
들어가도 되고 안들어가도 되고, 바뀌어도 되고 안바뀌어도 되면 일반 메소드로 한다고 생각하면 될 것 같습니다.
 

PS. 뷰를 구성하는 방식 1가지 추가 와 같은 글과 함께 읽으면 더욱 좋습니다. ^^
 
 다음글 XSS Clean 을 켜놓은 상태에서 POST 한글 텍... (4)
 이전글 윈도우 압타나 스튜디오3 에서 github의 ssh 연... (3)

댓글

이현석 / 2013/12/24 21:56:27 / 추천 0
다 써놓고 보니까 그냥 뷰파일마다 <?php $this->load->view('top');?> <?php $this->load->view('bottom');?> 하는게 더 편한거 같아 ㅠㅠ
헛발이 / 2013/12/25 14:30:14 / 추천 0
누구나 각자 자신에게 맞는 방법을 택하기는 하지만...

저는 개인적으로
$this->load->view('header');
$this->load->view('contents');
$this->load->view('footer');

이런 방식을 선호하지 않습니다.
이유는 header에서

<html>
<head></head>
<body>

까지만 작성되서 하나의 header.php를 보면 닫겨지지 않은 형태를 띄고 있어서
개인적으로는 선호하지 않는 편입니다.

결과적으로는 footer.php에
</body>
</html>

이 있기는 하지만요...

그래서 저는 개인적으로 hooks를 이용한 레이아웃을 선호하는 편이죠...
개인적인 견해차이는 있겠죠? ^^

이현석 / 2013/12/26 00:01:31 / 추천 0
아~ 그래서 hooks 를 사용하는거군요! 
저는 제가 잘 사용할 줄 몰라서 그런지 hooks를 쓰면 생성자 같은거에서 막 꼬이고 그래서 어렵더라구요 ㅠㅠ 
 
한대승(불의회상) / 2013/12/26 08:50:22 / 추천 0
좋은 정보 감사 합니다. ^^


팁게시판으로 게시판 이동 합니다.
/ 2014/02/17 18:28:36 / 추천 0
흥미로운 정보네요.
감사합니다. ^^