지난 포스팅에선 여러 패턴을 섞어서 강력한 객체지향 디자인을 만드는 컴파운드 패턴에 대해 알아보았다. 이번에는 매우 강력한 컴파운드 패턴 중 하나인 MVC 패턴에 대해 복습한다.

다이어그램

View

Model을 표현하는 방법을 제공하는 사용자 인터페이스 계층. View는 화면에 표시하기 위해 필요한 상태 및 데이터를 Model에서 직접 가져온다.

Model

모든 데이터, 상태 및 어플리케이션 로직이 들어있는 계층. VIew와 Controller에서 Model의 상태를 조작하거나 가져오기 위한 인터페이스를 제공한다. Model은 자신의 상태 변화에 대해서 View, Controller 옵저버들에게 알려주긴 하지만, View와 Controller에 대한 의존성이 없다.

Controller

View와 Model 사이에서 위치하여 사용자로부터 들어온 요청이 Model에게 어떤 의미가 있는지 파악한다. Model로 요청을 전달하는 역할만 하는것이 아니라, 사용자의 요청을 해석하여 Model을 조작하는 역할을 맡고있다.

View에서 Controller의 역할을 직접 맡아도 되지만, 다음과 같은 두 가지 이유 때문에 그렇게 하지 않는 것이 좋다.

  • View가 두 가지 역할을 맡기 때문에 코드가 복잡해지고 유지보수에 좋지 않다.
  • View를 Model에 너무 밀접하게 연관시켜야 한다는 문제가 있다.

위 두 가지 이유 때문에 View를 다른 Model들과 연결해서 재사용하기가 어려워진다. Controller는 View와 Model의 결합을 끊어주는 역할을 한다. View와 Controller를 느슨하게 결합하면 더 유연하고 확장하기 좋은 디자인을 만들 수 있다.

사용자는 View하고만 접촉할 수 있다.

View는 Model에 대한 정보를 사용자에게 보여주는 계층이다. 재생 버튼을 누르는 것 처럼 사용자가 View에 대해서 어떠한 행동을 하면 Controller에게 사용자가 어떤 일을 했는지 알려준다.

Controller에서 Model에겍 상태를 변경하라는 요청을 한다.

Controller는 사용자의 요청을 받아서 이를 분석한다. 사용자에 행동에 따른 요청이 전다되면 Controller는 해당 요청이 무엇을 의미하는지 분석하고 분석 결과에 따라 Model을 어떤식으로 조작해야 하는지 결정한다.

Controller는 View를 변경하라고 요청할 수 있다.

Controller에서 View로부터 어떤 요청을 받았을 때, 그 요청의 결과로 View를 갱신하라고 응답 할 수 있다.

상태가 변경되면 Model은 View에게 변경 사실을 알린다.

사용자의 요청 때문이든 다른 내부적인 변화 때문이든, Model에서 무언가가 변경되면 View한테 상태가 변경되었음을 알린다.

View는 Model에게 상태를 요청한다.

View는 화면에 표시할 상태를 Model로부터 직접 가져온다. 예를 들어, Model이 View에게 새로운 곡이 재생되기 시작했다고 알려주면 View는 Model에게 곡 제목을 요청하고, 그것을 받아서 화면에 표시한다. Controller가 View를 갱신하라는 요청을 하는 경우에, View는 Model에게 상태를 알려달라고 요청할 수 있다.

MVC 패턴은 여러 패턴이 함께 적용되어서 완성된 하나의 디자인으로 봐야한다. Model은 옵저버 패턴이 적용되어 상태가 바뀔 때마다 View와 Controller에게 연락한다. Controller는 View의 행동에 해당하기 떄문에 스트래티지 패턴을 적용해서 다른 행동을 원할 시 다른 Controller로 교환하도록 한다. View는 컴포지트 패턴을 적용하여 윈도우, 버튼 같은 다양한 구성요소를 관리하도록 한다.

예시

View와 Controller

View와 Controller는 고전적인 스트래티지 패턴으로 구현되어 있다. View에서는 애플리케이션의 겉모습에만 신경쓰고, 인터페이스의 행동에 대한 결정은 모두 Controller에게 맡긴다. 스트래티지 패턴을 이용하므로 View를 Model로부터 분리시키는 데에도 도움이 된다.

Controller는 사용자가 요청한 내역을 처리하기 위해서 Model과 대화를 나눈다. View는 이러한 방법이나 과정을 전혀 알지 못한다.

View

View 디스플레이는 여러 단계로 겹쳐져 있는 일련의 윈도우, 패널, 버튼, 텍스트 레이블 등으로 구성된다. 각 디스플레이 항목은 복합 객체(윈도우) 또는 잎(버튼)이 될 수 있다. Controller에서 View에게 갱신을 요청하면 최상위 View 구성요소에게 갱신을 요청한다. 나머지는 컴포지트 패턴에 의해 알아서 처리된다.

Model

Model은 옵저버 패턴을 이용하여 상태가 변경되었을 때 옵저버 객체들에게 연락을 한다. 옵저버 패턴을 이용해서 View와 Controller로부터 완전히 독립시킬 수 있다. 하나의 Model에서 서로 다른 View를 사용할 수도 있고, 여러 개의 View를 동시에 사용하는 것도 가능하다.

MVC와 웹 환경

웹의 등장으로 여러 개발자들이 MVC를 브라우저/서버 Model에 맞게 변형시켜서 사용하기 시작했다. Model2 라는 방법이 가장 많이 쓰이는데, 이를 이용하면 Servlet과 JSP 기술을 사용하여 Model, View, Controller를 분리해서 디자인할 수 있다.

사용자가 HTTP 요청을 하면 Servlet에서 그 요청을 수신한다.

사용자가 웹 브라우저를 이용해 HTTP 요청을 한다. 보통 사용자 IT와 비밀번호와 같은 폼 데이터가 함께 전달된다. Servlet에서는 이런 폼 데이터를 받아서 파싱한다.

Servlet이 Controller 역할을 한다.

Servlet은 Controller 역할을 맡아서 사용자 요청을 처리하고 대부분의 경우, Model(DB)에 어떤 요청을 하게 된다. 요청을 처리한 결과는 자바빈 형태로 포장된다.

Controller에서는 컨트롤을 View에게 넘긴다.

View는 JSP로 표현된다. JSP 파일에는 자바빈을 통해 얻은 Model을 이용해 View 페이지를 작성한다. JSP 페이지를 만드는 과정에서 다음 단계의 작업을 위해 몇 가지 더 제어해야할 일이 있을 수도 있다.

View에서 HTTP를 통해서 브라우저에게 페이지를 전달한다.

페이지가 브라우저에게 전달되면, 그 웹 페이지는 사용자의 화면에 표시된다. 해당 페이지 안에서 사용자는 또 다른 요청을 할 수도 있으며, 새로운 요청도 지금까지의 과정과 같은 방식으로 처리한다.

Model2의 장점

Model2의 장점은 단순히 디자인적인 면에서 각 구성요소를 분리해주는 것에 그치지 않는다. Model2 방식은 제작 책임까지도 분리시켜준다. 과거에는 웹 콘텐츠 제작자들은 콘텐츠와 HTML에 대해서는 잘 알지만 자바 소프트웨어까지 다루기는 어려웠기 때문에, 협업에 문제가 있었다. Model2 방식이 등장하면서 개발자들은 Servlet에만 전념하고, 웹 콘텐츠 제작자들은 간단한 Model2 스타일의 JSP만 다루면 되는 환경이 조성되었다.

옵저버 패턴

고전적인 의미에서 본다면 View는 더 이상 Model의 옵저버라고 할 수 없다. Model의 옵저버로 등록해서 Model의 상태가 바뀌었다는 연락을 수신하지 않기 때문이다. 하지만 Model의 상태가 바뀔 떄 Controller를 통해서 간접적으로나마 연락을 받는다. Controller는 View에게 자바빈을 전달 한다. View는 전달받은 Model의 상태를 알아낼 수 있다.

스트래티지 패턴

Controller Servlet은 전략 객체로 쓰인다. 하지만 고전적인 MVC 패턴과는 달리 View 객체에 Controller 객체에 대한 래퍼런스가 존재하지 않는다. 그럼에도 불구하고 Controller Servlet이 View의 행동을 구현하는 객체라는 점과 다른 행동을 원하는 경우, 다른 Controller로 바꿀 수 있다는 점에서 보면 여전히 스트래티지 패턴을 따른다고 볼 수 있다.

컴포지트 패턴

GUI와 마찬가지로 웹의 View도 결국은 중첩된 그래픽 구성요소로 이루어진다. 웹은 HTML 코드를 통해서 웹 브라우저에서 렌더링된다는 차이점이 있긴 하지만 그 밑에는 결국 복합 객체 형태의 객체 시스템이 있을 가능성이 매우 높다.

의문점

Controller에서 애플리케이션의 로직을 구현하는 경우가 있는가?

Controller에서는 View를 위한 행동을 구현하기만 한다. 사용자가 View에 대해 취한 행동을 Model에 대한 요청으로 바꿔주는 것이 바로 Controller의 역할이다. Model에서는 그러한 요청을 받아서 필요한 작업을 처리하는 애플리케이션의 로직을 구현한다. Controller에서 Model의 어떤 메소드를 호출할 지 결정하기 위해 분기문과 같이 어느 정도 간단한 작업을 처리할 수는 있지만, 그런 부분은 데이터를 관리하고 조작하기 위한 애플리케이션 로직이라고 할 수 없다.

푸시 방식을 사용해서 Model이 갱신되었다는 연락을 할 때 Model의 상태도 같이 전달하면 되지 않나?

JSP/HTML View에서는 실제로 그런 방식을 사용하고 있다. Model 자체를 빈으로 보내면 빈 속성을 사용해서 필요한 상태를 알아낸다. 옵저버 패턴의 푸시 방식에는 재사용성, 불필요한 정보 전달등의 단점이 있다.