MVC = Model View Controller

MVP = Model View Presenter

MVP & MVC




0. MVC, MVP 패턴의 등장 이유

1. MVC 패턴은 무엇인가? 

2. MVP 패턴은 무엇인가? 

3. 결론



0. MVC, MVP 패턴의 등장 이유

GUI 의 등장. 보다 효율적으로 사용자와 상호작용 할 수 있는 구현의 필요성.  




1. MVC 패턴은 무엇인가? 

MVC 패턴은 MVC = Model View Controller, 3가지 요소로 나누고자 하는 의도에서 나왔다.

과거 OS에서 GUI를 구성할 때 View가 Event를 handle하지 못했고 때문에 Controller가 사용자와의 상호작용을 전담하게 되면서 나온 구도라고 한다.


MVC 패턴이 뭐길래? 

MVC 패턴은 Model View Controller를 분리하고자 나온 개념이다. 그 이유는 각 요소를 분리하여 보다 '잘' 만들고자 하는데 있다. 객체지향은, 기본적으로 객체간의 상호 의존성을 줄여 재사용성과 유지보수를 최적화 하는데 목적이 있다.


 하지만 MVC 패턴은 View와 Model 을 분리할 수 없는 의존성의 한계가 있다. 

 

MVC Model 1



MVC Model 1 은 극단적으로 Model과 View가 상호 의존이 심해, 거의 구분이 불가능한 상태를 가지고 있다. 이를 말로 표현하자면 사용자가 요청에 따라 Controller가 하나의 DAO를 가져오며 ( Bean 같은거 ) 이 DAO 를 보여주기 위해 View를 만든다. 라고 할 수 있다.


즉, DAO가 바뀌면 View는 당연히 새로 만들어야 하는 구조였다. 


이와 같은 문제에 MVC Model 2이 등장하게 되었는데, 

골자는 View를 갱신시켜주는 Observer를 통해 의존성을 감소시키는 것이었다.


MVC Model 2


얘 기를 진행하기 전에 MVC Model 2에서 Controller 에 Servlet이 온 점을 먼저 집고 넘어가고자 한다. 기본적으로 Controller는 Controller의 역할을 할 수 있으면 된다. 그러므로 Controller가 JSP로 구현되었어도 같은 역할을 수행한다면 Servlet과의 차이는 없다. 


MVC Model 2 Diagram

MVC Model 2 에 새로 등장한 Observer interface는 Model과 View의 의존성을 어느 정도 경감시켜준다고 한다. ( 본인은 잘 모르겠지만... ) MVC Model 2에는 Flow SynchronizationObserver Synchronization 패턴이 적용됐다고 한다.


Web을 예로 든다면, 

Model 1  : controller 가 요청을 받음 -> Model 갱신 -> Model 에 따라 View 만듬

Model 2  : controller 가 요청을 받음 -> Model 갱신 -> Observer에게 갱신 요청함 -> View에서 갱신이 됨. 


Android를 예로 든다면, 

Model 1  : onItemClickEvent 받음 -> Model 갱신 -> 새 ListView 만듬. 

Model 2  : onItemClickEvent 받음 -> Model 갱신 -> Adapter에게 Notify함 -> View 갱신



MVC 의 패턴 한계 

MVC 패턴에 대한 한계점은 엄청 많은거 같은데, 몇 가지만 얘기해보고자 한다. 

첫 째는, MVC 패턴 자체로서가 아닌 타 패턴( 특히 Observer의 등장 )과 함께 사용해야 본 의도인 MVC의 삼권 분리를 이룰 수 있다는 점으로 사실상 패턴으로서의 한계점을 드러내는 점이라고 할 수 있다. 


둘 째는, Observer의 등장으로 인해 신경써야 할 일이 더 많아졌다는 것이다. Model1에서는 하나의 Model에 하나의 View을 만들었기에 현재 어떤 View가 보여지고 있는지 신경쓸 이유가 없었다. Controller가 명령하면 보여질 Model에 보여질 View를 사용하면 됐으니까. 하지만 Observer를 사용하게 되면서 현재 보여지는 View 뿐 아니라 동시에 View의 교체 혹은 갱신 시에 Flow의 Synchronize 까지 신경써야 된다.


셋 째는, Android를 개발하는 입장에서 가끔 답답함을 느끼게 되는 Model의 갱신 문제인데, 어떤 블로그에서는 이를 이렇게 표현합니다.  - "예를 들어 사용자가 체크 버튼을 눌렀을 때, 해당 갱신 기록을 어디서 처리해야 할까요?" 


여러 분의 생각은 어떠한지 궁금하다. 

현재 Android 에서 adapterView를 상속하는 View들은 모두 Observer 패턴이 적용되며, 이는 Adapter로 표현된다. MVC Model 2 에서는 앞의 질문에 대한 답을 이렇게 한다.


[ 사용자의 요청 -> Controller -> Model 갱신 -> Observer에게 갱신 명령 -> Observer에 의한 View 갱신(체크) ] 이 된다. 딱 봐도 뭔가 느낌이 미려하지가 않다. 


-기타

그 외에 Observer 패턴 자체 문제로 디버깅이 어렵다 등이 있다고 합니다. 



2. MVP 패턴은 무엇인가? 


MVP 는 Model View Presenter 로 구성한 GUI 용 패턴이다.


앞서 MVC 패턴 등장할 당시의 OS 환경에서 GUI를 구성할 때 View가 Event를 handle하지 못했고 때문에 Controller가 사용자와의 상호작용을 전담하게 되면서 나온 구도라고 했다.


현 재의 OS는 View에서 Event의 Handle 을 할 수 있으므로, View 가 Event를 Handling 한다. 그리고 View 는 Presenter와 약속된 interface로서 Event 를 전송하고 Presenter는 해당 Event를 통해 Model을 갱신하는 구조이다.


MVP 패턴 예 


 앞서 예를 든, Check box 의 선택을 비교해서 생각해보자.

 MVC : onItemClickEvent 받음 -> Model 갱신 -> Adapter에게 Notify함 -> View 갱신

 MVP : onItemClickEvent 받음 -> View 갱신 -> Presenter에게 apply 함 -> Model 갱신



 View와 Presenter는 interface로서 통신한다. 

MVP 패턴은 Model과 View의 의존성을 경감시킴으로 진정한 View와 Model의 재사용성을 보장할 수 있다.


"예를 들어 사용자가 체크 버튼을 눌렀을 때, 해당 갱신 기록을 어디서 처리해야 할까요?"


라는 의문에 대해서 MVP 패턴은 이렇게 대답할 수 있다. 

[ 사용자의 요청 -> 사용자와 상호작용하는 View에서 Handle -> View 갱신(체크) -> Presenter 에 apply -> Presenter 에서 Model 갱신 ]


너무나 직관직이다. 

이것은 개발하는 사람에게도, 코드를 보는 사람에게도 직관적으로 느껴질 것이다. 


다음에 기회가 된다면 두 코드가 얼마나 다르게 느껴지는지 비교해보도록 하겠습니다.



3. 결론


MVP 가 좋음. MVP 를 씁시다. ^0^/



References

http://www.martinfowler.com/eaaDev/PresentationModel.html

http://www.martinfowler.com/eaaDev/uiArchs.html

http://aspiringcraftsman.com/2007/08/25/interactive-application-architecture/

http://blog.daum.net/picus2/186

http://msdn.microsoft.com/en-us/magazine/cc188690.aspx



이 글은 MVC 패턴? MVP 패턴? 무엇에 쓰는 무엇인고? 연장선에 있습니다.


지난 글에서 MVP 패턴에 대해서 알아봤지만,
아무리 말을 많이해도 한번 해보는만 못한 법.

이번에는 직접 MVP 패턴을 구현한 Android 예제와 함께 하겠습니다.

Android 의 widget 군에 AdapterView를 상속하는 View 개체들은 모두 MVC Model 2 패턴을 내부에 구현하고 있습니다. 때문에 MVP 패턴을 구현하기 위해서는 ViewGroup 을 활용할 수 밖에 없습니다. 때문에 예제는 LinearLayout 을 통해 일종의 Custom Listview 처럼 보이지만 그 정도의 기능을 갖고 있진 않은, 완전한 LinearLayout을 사용한 예제입니다. 이 점에 착오없으시길 바랍니다.


example CustomView

예제는 위와 같은 모양새를 갖고 있습니다.
MVP 패턴에서 중요한 것은, User 와 View간의 상호작용이 View단에서 빠르게 처리되고 데이터 관리 측면에서는 Presenter 와 Interface를 통해 통신해서 서로 간에 의존성이 없는

Model ( 데이터 ) - Presenter ( 데이터바인딩과 Model 갱신 ) - View ( 사용자와의 상호작용 )

삼권분리를 이루는 점에 있습니다.

이 점이 매우 중요하므로, 간단한 예를 하나 들어보고 넘어가겠습니다.

그림판 어플이 사용자가 사용할 때, 기존 MVC 패턴의 한계점은 Controller가 사용자와의 상호작용을 담당하고 이를 통해 Model을 갱신한 후에 View에 뿌려준다는 점에 있습니다. 전 시간에 다뤘든 이는 구 시대의 유물입니다.

그렇다면 사용자 입장에서 가장 좋은 구현 방법은 어떤 것인가요?
그것은 사용자가 보고 있는 화면이 그대로 사용자와 상호작용하며, 백단에서 상호작용 내용을 처리하는 방법입니다. 이런 구조 하에서 View 단이 사용자와 상호작용 할 때, 시스템은 100프로 사용자에게 집중할 수 있게 됩니다.

또한 프로그램의 구조가 아주 직관적으로 변하게 되는데, 이유는 View->Presenter->Model의흐름이 명확하게 눈에 보이기 때문입니다.

CustomerView는 이와같은 구조 흐름을 예제로 구현하는데 최선을 다했습니다.
미흡한 점이 있다면 제가 고칠 수 있도록 지적해주시면 감사하겠습니다.

CustomerView Class Diagram

CustomerView는 MVP를 각 부분을 아래와 같이 분담하고 있습니다.
M - CustomerModel
V - CustomerView
P - CustomerPresenter 


CustomerView Sequence flow



실행 흐름을 한번 보시죠.
MVP 패턴이 실행 흐름을 직관적으로 만들어 준다는 것은 허언이 아닙니다.


MVP 패턴에서 가장 중요한 부분은,
"Ui 에서 처리되야 할 부분과 Model과 연계되는 부분을 어떻게 나누고 다룰 것인가? " 이며, Presenter의 역할 정의입니다.


제가 만든 예제에서는 CheckBox를 통해 이를 다음과 같이 정의하고 있습니다.

CustomerView 의 요소인 CheckBox가 Click 되는 이벤트를 Ui 요소로 정의하고,
사용자가 Ui를 통해 상호작용한 결과를 confirm 할 때 Presenter를 통해 Model 을 갱신한다.

Checkbox를 선택하고 Del 버튼을 눌렀을 때 동작하는 로직을 한번 보도록 하겠습니다.
참고로, CheckBox가 선택될 때 선택 여부를 저장하는 것은 구현하지 않았습니다.

소스 코드 -
Event를 처리하는 onClick

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
 public void onClick(View view) {
  Log.v("TEST", "onClick" );
  if( view instanceof CheckBox ){
   CheckBox chk = (CheckBox) view;
   chk.setChecked(true);
   Log.v("TEST", "CheckBox ID : " + String.valueOf(chk.getId()) );
  }
  if (view.getId() == R.id.btnAdd) {
   showAddCustomerDialog();
  } else if (view.getId() == R.id.btnDelete) {
   executeCheckPosition( DEL );
    
  }
 }


Delete 버튼이 눌리면 executeCheckPosition 이 실행됩니다. DEL은 단순 구분인자입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void executeCheckPosition( int cmd ){
 int childCount = customerList.getChildCount();
 for (int index=0; index < childCount; index++){
  CustomerRelativeLayout curr =
    (CustomerRelativeLayout)customerList.getChildAt(index);
  if ( curr.isChecked() ){
   if ( cmd == DEL ){
    customerList.removeView(curr);
    DelCustomer( index );
    index--; childCount--;
    curr.clearCheck();
   }
  }
 }
}


Child 목록에서 체크된 녀석들을 View에서 지우고 DelCustomer를 호출하는 역할을 합니다. 

1
2
3
4
@Override
 public void DelCustomer( int position ) {
  presenter.DelCustomer( position );
 }

DelCustomer는 미리 Presenter와 상호작용을 위해 정의된 인터페이스를 따릅니다.
대충 DelCustomer와 AddCustomer 등이 있지요.

CustomPresenter는 View에서 요청한 DelCustomer를 통해 Model을 갱신합니다.

1
2
3
4
5
6
public void DelCustomer( int position ) {
  // TODO Auto-generated method stub
  if ( !mCustomerModel.DelCustomer( getItem(position) ) ) {
   Log.v("MVP", "Del Problem at : " + position );
  }
 }




MVP 모델을 주창한 분은 Presenter에서 Model를 collection형태로 가지고 갱신하는 형태를 만드셨는데 저는 Model을 따로 만들어봤습니다. 뭔가 Adapter처럼 Model을 다루는 것이 어렵게 느껴지더군요.

이상 아주아주 허접하게 MVP 패턴을 다루고, 예제에 대해 다뤄봤습니다.  - FIN


예제 - https://github.com/Bhb2011/MVPpatternExam