상황
상당수 컴퓨터 시스템의 용도는 데이터 저장소에서 데이터를 검색하여 사용자에게 보여주는 것입니다. 사용자가 데이터를 변경하면 변경된 데이터는 시스템의 데이터 저장소에 저장됩니다. 정보가 주로 데이터 저장소와 사용자 인터페이스 사이에 전달되므로 이 두 개체를 서로 연결하여 코딩의 양을 줄이고 응용 프로그램 성능을 향상시키려고 할 것입니다. 하지만 이 자연스러워 보이는 방법에도 몇 가지 중요한 문제점이 있습니다. 한 가지 문제점은 사용자 인터페이스가 데이터 저장소 시스템보다 훨씬 더 자주 변하기 쉽다는 것입니다. 또 다른 문제점은 데이터 저장소와 사용자 인터페이스를 연결할 때 비즈니스 응용 프로그램에는 데이터 전송을 훨씬 넘어서는 비즈니스 논리를 통합하기 쉽다는 것입니다.
문제점
사용자가 쉽게 개별 파트를 수정할 수 있도록 웹 응용 프로그램의 사용자 인터페이스 기능을 모듈화할 수 있는 방법은 무엇일까요?
강제 요인
이러한 상황에서 다음과 같은 강제 요인이 시스템에 작용하며 위의 문제를 해결할 때 이러한 강제 요인을 조정해야 합니다.
사용자 인터페이스 논리는 특히 웹 기반 응용 프로그램에서 비즈니스 논리보다 더 자주 변하는 경향이 있습니다. 예를 들어, 새로운 사용자 인터페이스 페이지가 추가되거나 기존 페이지의 레이아웃이 바뀔 수 있습니다. 무엇보다 웹 기반 씬(thin) 클라이언트 응용 프로그램의 이점 중 하나는 응용 프로그램을 재배포하지 않고도 언제든지 사용자 인터페이스를 변경할 수 있다는 점입니다. 프레젠테이션 코드와 비즈니스 논리가 단일 개체에 연결된 경우, 사용자 인터페이스가 변경될 때마다 비즈니스 논리가 포함된 개체를 수정해야 합니다. 이 경우 오류가 발생하기 쉬우며 사소한 사용자 인터페이스 변경이 있은 후에도 모든 비즈니스 논리를 다시 테스트해야 합니다.
일부 경우에는 응용 프로그램이 동일한 데이터를 다른 식으로 표시합니다. 예를 들어, 분석가는 데이터를 스프레드시트로 보기 원하지만 경영진은 이 데이터를 원 그래프로 보기를 원합니다. 일부 리치(rich) 클라이언트 인터페이스에서는 동일한 데이터를 여러 개의 뷰로 동시에 표시할 수 있습니다. 사용자가 하나의 뷰에서 데이터를 변경하면 시스템이 데이터의 모든 다른 뷰를 업데이트해야 합니다.
보기에 좋고 효율적인 HTML 페이지를 디자인하기 위해서는 대개 복잡한 비즈니스 논리를 개발할 때와는 다른 기술이 필요합니다. 두 가지 기술을 모두 가지고 있는 사람은 흔하지 않습니다. 따라서 이 두 파트에 대한 개발 노력을 구분하는 것이 바람직합니다.
사용자 인터페이스 작업은 대개 프레젠테이션과 업데이트의 두 부분으로 구성됩니다. 프레젠테이션 파트에서는 데이터 소스에서 데이터를 검색하고 서식을 지정한 후 표시합니다. 사용자가 데이터 기반의 작업을 수행하는 경우, 업데이트 파트에서는 데이터를 업데이트하기 위해 제어권을 비즈니스 논리로 넘겨줍니다.
웹 응용 프로그램에서의 단일 페이지 요청은 사용자가 선택한 링크의 작업 처리와 대상 페이지의 렌더링을 결합합니다. 상당수의 경우 대상 페이지가 해당 작업에 직접 연결되지 않을 수 있습니다. 예를 들어, 항목의 목록을 표시하는 간단한 웹 응용 프로그램을 생각해 보겠습니다. 사용자가 목록에 항목을 추가하거나 목록에서 항목을 삭제한 후 주 목록 페이지로 돌아갑니다. 따라서 응용 프로그램이 동일한 HTTP 요청 내에서 상당히 다른 두 명령(추가 또는 삭제)을 실행한 후 동일한 페이지(목록)를 렌더링해야 합니다.
사용자 인터페이스 코드는 비즈니스 논리보다도 더 장치의 영향을 받는 경향이 있습니다. PDA(Personal Digital Assistant) 또는 웹 가능 휴대폰을 지원하기 위해 브라우저 기반 응용 프로그램을 마이그레이션하려는 경우, 사용자 인터페이스 코드는 상당수 교체되어야 하는 반면 비즈니스 논리는 영향을 받지 않을 수 있습니다. 이 두 파트를 명확하게 구분하면 마이그레이션이 촉진되고 비즈니스 논리에 오류가 발생할 위험이 최소화됩니다.
자동화된 사용자 인터페이스용 테스트를 만드는 것은 비즈니스 논리용 테스트를 만드는 것보다 어렵고 많은 시간이 소요됩니다. 따라서 사용자 인터페이스에 직접 연결된 코드의 양을 줄이는 것이 응용 프로그램의 테스트 성능을 향상시킬 수 있습니다.
솔루션
MVC(Model-View-Controller) 패턴은 도메인의 모델링과 프레젠테이션 그리고 사용자 입력 기반의 작업을 세 개의 별도 클래스로 구분해 줍니다[Burbeck92].
모델. 모델에서는 응용 프로그램 도메인의 동작(behavior)과 데이터를 관리하고, 대개 뷰로부터의 상태 정보 요청에 응답하고, 대개 컨트롤러로부터의 상태 변경 명령에 응답합니다.
뷰. 뷰에서는 정보 표시를 관리합니다.
컨트롤러. 컨트롤러에서는 사용자의 마우스 및 키보드 입력을 해석하여 적절하게 변경하도록 모델과 뷰에서 알려줍니다.
그림 1은 세 가지 개체의 구조적 관계를 나타냅니다.

그림 1: MVC 클래스 구조
중요한 점은 뷰와 컨트롤러는 모델에 의존하지만 모델은 뷰와 컨트롤러에 의존하지 않는다는 것입니다. 이것이 바로 구분의 가장 큰 이점입니다. 이 구분에서는 시각적 프레젠테이션의 영향을 받지 않으면서 모델을 만들고 테스트할 수 있습니다. 상당수의 리치(rich) 클라이언트 응용 프로그램에서는 뷰와 컨트롤러 사이의 구분이 그다지 중요하지 않으며 실제로 상당수 사용자 인터페이스 프레임워크에서는 두 역할을 하나의 개체로 구현합니다. 반대로 웹 응용 프로그램에서는 뷰(브라우저)와 컨트롤러(HTTP 요청을 처리하는 서버측 구성 요소) 사이의 구분이 명확하게 정의됩니다.
MVC(Model-View-Controller) 는 사용자 인터페이스 논리와 비즈니스 논리를 구분하기 위한 기본적인 설계 패턴입니다. 불행히도 이 패턴의 인기로 여러 잘못된 설명이 나타났습니다. 특히 "컨트롤러"라는 말은 다양한 여러 상황마다 다른 의미를 가지게 되었습니다. 하지만 다행히 웹 응용 프로그램의 등장으로 뷰와 컨트롤러 사이의 구분이 명확해짐에 따라 어느 정도의 모호함이 해결되고 있습니다.
변형
Application Programming in Smalltalk-80: How to use Model-View-Controller (MVC)[Burbeck92]에서 Steve Burbeck는 MVC의 두 가지 변형인 수동 모델과 능동 모델에 대해 설명합니다.
수동 모델은 하나의 컨트롤러가 모델을 독자적으로 처리할 때에 사용됩니다. 모델을 수정한 컨트롤러는 해당 모델이 변경되었으며 새로 고쳐야 함을 뷰에 알려줍니다(그림 2 참조). 이 시나리오에서는 모델이 뷰와 컨트롤러로부터 완전히 독립되어 있기 때문에 모델이 상태 변경 사항을 보고할 방법이 없습니다. HTTP 프로토콜이 바로 그 예입니다. 브라우저에는 서버로부터 비동기 업데이트를 받을 수 있는 간단한 방법이 없습니다. 브라우저는 뷰를 표시하고 사용자 입력에 응답하지만 서버의 데이터가 변경되는 것을 감지하지 못합니다. 사용자가 명시적으로 새로 고침을 요청해야만 서버에 변경 사항이 있는지 물어봅니다.

그림 2: 수동 모델의 동작(behavior)
능동 모델은 컨트롤러의 개입 없이 모델이 상태를 변경할 때에 사용됩니다. 이러한 경우는 다른 소스가 데이터를 변경 중일 때 이 변경 사항을 보기에 반영해야 하는 경우에 발생할 수 있습니다. 증권 시세 표시기를 생각해 보겠습니다. 주식 데이터가 변경되면 외부 소스에서 주식 데이터를 받아 뷰(예: 시세 표시기 및 경보 창)를 업데이트하고 싶습니다. 오직 모델만이 변경된 내부 상태를 감지할 수 있기 때문에 변경이 발생할 경우, 모델은 표시기를 새로 고치도록 뷰에게 통보해야 합니다.
하지만 MVC 패턴을 사용하는 이유 중 하나는 모델이 뷰의 영향을 받지 않기 때문입니다. 모델이 뷰에 변경 사항을 통보해야 한다면 지금까지 피하려고 했던 의존성이 재현되는 것입니다. 다행히도 옵서버 패턴[Gamma95]은 의존성을 재현하지 않고도 상태 변경 사항을 다른 개체에 알려줄 수 있는 메커니즘을 제공합니다. 개별 뷰가 옵서버 인터페이스를 구현하고 모델에 등록합니다. 이 모델은 변경 사항을 구독하는 모든 옵서버의 목록을 추적합니다. 모델이 변경되면 이 모델은 등록된 모든 옵서버를 검색한 후 옵서버에게 변경 사항을 통보합니다. 이 방법을 흔히 "게시-구독"이라고 합니다. 모델에는 뷰에 관한 특정 정보가 전혀 필요 없습니다. 실제로 모델 변경 사항(예: 메뉴 옵션 설정 또는 해제)을 컨트롤러에 알려야 하는 시나리오에서는 모든 컨트롤러가 옵서버 인터페이스를 구현하고 모델 변경 사항을 구독해야 합니다. 많은 뷰가 있는 상황에서는 여러 개의 주제를 정의하고 각 주제로 특정 유형의 모델 변경 사항을 설명하는 것이 좋습니다. 각각의 뷰는 해당 뷰에 관련된 유형의 변경 사항만을 구독할 수 있습니다.
그림 3은 옵서버를 사용하는 능동 MVC의 구조와 모델이 뷰를 직접 참조하지 못하도록 옵서버가 모델을 격리시키는 방법을 보여줍니다.

그림 3: 능동 모델에서 옵서버를 사용하여 모델과 뷰를 분리
그림 4는 모델이 변경될 때 옵서버가 뷰에 어떻게 변경 사항을 통보하는지 그 방법을 보여줍니다. 불행히도 모델과 뷰의 구분을 UML(Unified Modeling Language) 시퀀스 다이어그램에 적절하게 표시할 수 있는 방법이 없는데 그 이유는 이 다이어그램은 클래스와 인터페이스가 아니라 개체 인스턴스를 표시하기 때문입니다.

그림 4: 능동 모델의 동작(behavior)
예제
ASP.NET에서 MVC(Model-View-Controller 구현을 참조하십시오.
테스트 고려 사항
MVC(Model-View-Controller) 패턴을 사용하면 테스트 성능이 상당히 향상됩니다. 구성 요소가 상호 의존적인 경우 구성 요소(특히 사용자 인터페이스 구성 요소)를 테스트하는 것이 어려워집니다. 이런 구성 요소의 경우는 간단한 기능을 테스트하는 데도 복잡한 설정이 필요합니다. 설상가상으로 오류가 발생한 경우에는 문제를 특정한 구성 요소에 격리시키는 것이 어렵습니다. 바로 이러한 이유 때문에 Architecture를 구성하는 데 있어 구분이 중요해집니다. MVC에서는 데이터의 저장, 표시 및 업데이트 문제를 세 개의 구성 요소로 구분한 후 이 구성 요소를 개별적으로 테스팅할 수 있습니다.
상호 의존성에 의한 문제는 제외하더라도 사용자 인터페이스 프레임워크의 테스트 자체가 어렵습니다. 사용자 인터페이스를 테스트하기 위해서는 지루한 수작업 테스트(오류가 발생하기 쉬운 테스트)나 사용자 동작을 시뮬레이션하는 테스트 스크립트가 필요합니다. 이 스크립트는 개발에 많은 시간이 소요되며 불안정합니다. MVC가 사용자 인터페이스 테스트의 필요성을 없애주지는 않지만 모델과 프레젠테이션 논리를 구분하기 때문에 프레젠테이션의 영향을 받지 않고 모델을 테스트할 수 있으므로 사용자 인터페이스 테스트 사례의 수가 줄어듭니다.
결과
MVC 패턴 주위에 프레젠테이션 레이어를 구성하면 다음과 같은 이점과 단점이 있습니다.
이점
여러 개의 뷰 지원. 뷰와 모델이 구분되어 뷰와 모델이 직접 의존하지 않으므로 사용자 인터페이스에서 동일한 데이터를 여러 개의 뷰로 동시에 표시할 수 있습니다. 예를 들어, 웹 응용 프로그램의 여러 페이지에서 동일한 모델 개체를 사용할 수 있습니다. 또 다른 예는 사용자가 페이지의 모양을 변경할 수 있는 웹 응용 프로그램입니다. 이 페이지는 공유된 모델로부터 동일한 데이터를 표시하지만 다른 방식으로 데이터를 표시합니다.
변경 사항을 수용. 사용자 인터페이스 요구 사항은 비즈니스 규칙보다 더 빨리 변하는 경향이 있습니다. 사용자는 휴대폰 또는 PDA와 같은 신형 장치에 차별화된 색상, 글꼴, 화면 배치 및 지원 수준을 선호할 수 있습니다. 모델은 뷰에 의존하지 않기 때문에 새로운 유형의 뷰를 시스템에 추가하더라도 모델에는 영향을 미치지 않습니다. 따라서 변화의 범위가 뷰로 국한됩니다. 이 패턴은 페이지 컨트롤러 및 프런트 컨트롤러와 같이 이 패턴을 더욱 특수화하기 위한 기반이 됩니다.
단점
복잡성. MVC 패턴에서는 새로운 수준의 간접 참조가 사용하므로 솔루션이 약간 더 복잡해집니다. 또한 사용자 인터페이스 코드의 이벤트 중심적 특성이 강해지므로 디버깅이 더 어려워질 수 있습니다.
빈번한 업데이트의 손실. 모델과 뷰를 분리한다고 해서 모델 개발자가 뷰의 특성을 무시할 수 있다는 의미는 아닙니다. 예를 들어, 모델이 자주 변경되는 경우 뷰에 업데이트 요청이 쇄도할 수 있습니다. 그래픽 디스플레이와 같은 일부 뷰의 경우는 렌더링에 약간의 시간이 소요될 수도 있습니다. 이 결과 뷰가 업데이트 요청을 따라잡지 못할 수 있습니다. 따라서 모델을 코딩할 때도 뷰를 염두에 두는 것이 중요합니다. 예를 들어, 모델은 뷰에 전달되는 단일 통보에 여러 개의 업데이트를 포함할 수 있습니다.
변형
문서-보기 변형 패턴은 MVC(Model-View-Controller) 의 세 가지 역할을 모드 인식하지만 컨트롤러를 보기에 병합합니다. 문서는 MVC의 모델 역할에 해당합니다. 이 변형 패턴은 기존의 상당수 GUI 플랫폼에 나타납니다. 문서-보기의 가장 좋은 예는 Microsoft Visual C++ 환경의 MFC(Microsoft Foundation Class) 라이브러리입니다. 이 변형 패턴을 사용할 때는 뷰와 컨트롤러가 약간 더 긴밀하게 연결됩니다.
관련 패턴
자세한 내용은 다음과 같은 관련 패턴을 참조하십시오.
옵서버. 이 패턴은 뷰와 해당 모델을 동기화시킬 필요성으로 인해 MVC와 관련되어 자주 언급됩니다.
참고 자료
MVC(Model-View-Controller) 는 1970년대 후반 Trygve Reenskaug가 Smalltalk 플랫폼용으로 개발한 프레임워크로 출발했습니다[Fowler03]. 방금 읽은 이 버전에서는 다음과 같은 문서를 참조합니다.
[Burbeck92] Burbeck, Steve. "Application Programming in Smalltalk-80: How to use Model-View-Controller (MVC)."University of Illinois in Urbana-Champaign (UIUC) Smalltalk Archive. 다음 웹 사이트에서 구할 수 있습니다: http://st-www.cs.uiuc.edu/users/smarch/st-docs/mvc.html (영문).
[Fowler03] Fowler, Martin. Patterns of Enterprise Application Architecture. Addison-Wesley, 2003.
[Gamma95] Gamma, Helm, Johnson 및 Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.

'fundamentale > design pattern' 카테고리의 다른 글
| MVC(Model-View-Controller) (0) | 2008/11/11 |
|---|---|
| Factory method pattern의 이해 (1) | 2008/11/11 |
| [펌] Abstract Factory Pattern - Builder Pattern 에 대한 이야기 (0) | 2008/10/21 |
| Observer Pattern - 디자인 패턴 (0) | 2008/01/24 |




댓글을 달아 주세요