이 글은 MVC와 MVP 패턴에 대해서 기술한 글 MVC? MVP? 무엇에 쓰는 패턴인고? 을 보고 보시면 좋습니다.



 0. 개요


 제가 기존 글에는 Android GUI 구현 패턴이 MVC model #2 이고, 많은 개선점이 있다고 기술했었는데, 실수였습니다. 패턴은 구현자가 어떻게 구현하느냐에 많은 부분이 달라집니다. 물론 Framework 단에서 이를 확정적으로 지원하지 않는 것이 아쉽다고 말할 수는 있겠지만, 그건 어디까지나 바람일 뿐이지 이를 잘못됐다고는 말할 수 없는 것 같습니다.

 그래서 이 글은 지난 번에 작성한 MVP Pattern example 에서 현 Android Framework와는 별개로서 MVP 패턴을 구현하려고 했던 시도에서 한보 더 나아가 현재 Android에서 제공하는 Framework 하에서 어떻게 수려한 GUI 패턴을 구현할 수 있는지 알아보고 실질적인 도움이 되는 구현 방법을 시도해보고자 합니다.

 해당 방법론의 필요성을 권위를 담기 위한 링크 글을 통해 강조하고 시작해보겠습니다.
 Model-View-ViewModel 디자인 패턴을 사용한 WPF 응용 프로그램

 내용을 요약하자면, GUI패턴 개선시도를 MS에서도 하고 있다. 정도가 되겠네요.



 0-1.작업에 들어가기 앞서 - 수려한 GUI 패턴의 의미.


 GUI 패턴 중에 잘 알려진 MVC 는 Model과 View를 구분하는 방향으로 발전되었습니다. 그 이유는 OOP의 핵심 개념인 재사용성을 위해서 입니다. 재사용성을 위해서는 서로 간에 연결성이 없어야 하죠. MVC는 Controller가 Model을 통해 View를 갱신시키는 한계로 인해 1 model - 1 view의 깊은 연결성을 가진 것이 문제였습니다. 이렇게 연결된 model과 view는 따로 나누어서 사용하는 것이 어려웠습니다. 또한 Controller가 고정적 위치를 차지해야 할 이유도 없어졌죠.

 현재 GUI에 등장하는 모든 패턴들의 공통점은 controller 가 고정적으로 존재할 필요가 없는 최근 OS의 추세에 맞추고 동시에 model과 view의 연결성을 보다 느슨하게 만드는 것입니다. 이를 직교성 확보한다. 라고 표현합니다.

 이를 위해 OOP의 개념인 추상화가 사용되게 됩니다.
 MVP와 MVVM은 모두 Presenter와 ViewModel이라는 View 추상화 계층이 있습니다. 이를 통해서 View와 Model이 서로 간에 간섭 없이 독립적으로 동작하면서도 View 추상화 계층을 통해통신할 수 있는 것이죠~

 수려한 GUI 패턴의 의미 첫 번째! Model과 View의 연결성 제거, 직교성 확보!

 또한 현 Android의 GUI 구성은 MVC model #2의 구성을 가지고 있는데, 이와 같은 환경에서는 코드가 가독성이 떨어지게 됩니다. 예를 들어 현재 Android 에서 Listview를 사용한다고 할 경우, Listview에 들어가는 model(ArrayAdapter라면 Array)를 직접 참조하여 변경하고 notify하면 observer가 이를 갱신하는 형태이기 때문에 갱신 과정을 직관하기 어렵게 되고, 이는 가독성에 영향을 미칩니다.

 하나의 Position의 model에 대해 하나의 View가 영향을 미치도록 코드를 짜두어야 메소드의 이름과 인자 값만 보고도 어느 포지션의 view가 갱신되는지를 따로 뒤져보지 않아도 바로바로 확인 할 수 있을 것입니다.

 수려한 GUI 패턴의 의미 두 번째! UI 갱신 과정의 코드 가독성 확보!




 1. 보편적인 ListView 사용 예제.


 그럼 이제 검색하면 흔히 나오는 ListView 기초 예제를 가지고 이를 개선시켜 나가는 과정을 통해서 앞서 언급한 1. view와 model의 직교성 2. 가독성 을 확보하는 방법을 모색해 볼 것입니다. 사용하고자 샘플은 Chathura Wijesinghe이 Android ListView example with Image and Text 에서 공개한 소스 입니다. Thanks Chathura Wijesinghe!

 우리는 ListView 를 통해음식점 메뉴판을 만들고자 합니다~!~!

( 굳이 외국 소스를 가져온 이유는 마음껏 퍼가라고 글을 남겨놨기 때문입니다^^ )

원본 소스구동 이미지



해당ListView의 핵심 소스는 아래와 같습니다. 많이들 보셨을 것이므로 설명은 생략하고,

ArrayList<ItemDetails> image_details = GetSearchResults();
final ListView lv1 = (ListView) findViewById(R.id.listV_main);
lv1.setAdapter(new ItemListBaseAdapter(this, image_details)); 

ListView에서는 getView 메소드를 통해 ItemListBaseAdapter에게서 View를 받아와 화면에 뿌려주게 됩니다.

public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    if (convertView == null) {
        convertView = l_Inflater.inflate(R.layout.item_details_view, null);
        holder = new ViewHolder();
        holder.txt_itemName = (TextView) convertView.findViewById(R.id.name);
        holder.txt_itemDescription = (TextView) convertView.findViewById(R.id.itemDescription);
        holder.txt_itemPrice = (TextView) convertView.findViewById(R.id.price);
        holder.itemImage = (ImageView) convertView.findViewById(R.id.photo);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
   
    holder.txt_itemName.setText(itemDetailsrrayList.get(position).getName());
    holder.txt_itemDescription.setText(itemDetailsrrayList.get(position).getItemDescription());
    holder.txt_itemPrice.setText(itemDetailsrrayList.get(position).getPrice());
    holder.itemImage.setImageResource(imgid[itemDetailsrrayList.get(position).getImageNumber() - 1]);

    return convertView;
}



이 부분까지가 MVC Pattern Model #2 로 구현된 ListView가 되겠습니다.
해당 패턴에서는 Model 과 View의 관계를 풀기 위해 View를 Layout화 시키고 각 Layout 에 Model 의 데이터를 Binding 시키는 형태를 취하게 됩니다.


View Layout

이 방식은 Model 에 변동이 있더라도 View라는 추상화 된 계층이 있기 때문에 Model과 View 모두 재사용 할 수 있게 되었지만, 동시에 [ Model의 Data가 View에 Binding된 후에 표현된다. ] 라는 한계를 남기게 됩니다.
 

이 한계점은 족쇄나 다름없는 것이, 곧 OS는 Controller가 필요 없어졌습니다.
(다시 한번 언급하자면 Controller가 반드시 필요했기 때문에 MVC 패턴이 된 것 입니다. )
즉, 사용자의 입력이 Controller에 의해 선 제어될 이유가 없어지고, 이를 OS가 dispatch하여 각 Activity혹은 View의 Listener에게 까지 전달해 줄 수 있으므로 이에 따라 VIew가 능동적으로반응할 수 있게 된 것이죠.

이렇게 환경이 바뀌자 MVC의 구조는 개선의 여지가 생겼습니다.


MVC 패턴의 개선점

그렇다면 이를 어떻게 개선할 수 있을까요?
이는 앞서 언급한 패턴 MVP와 MVVM을 적용함으로서 가능합니다.
해당 패턴들을 구현해가는 과정을 지켜보도록 합시다~


2. 보편적으로 구현된 소스 코드에 MVP 패턴 적용해보기


2-1 작지만 큰 한 걸음. 시도.

@Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
    // Model을 수정하는 Code 예시 #1
    ItemListBaseAdapter adapter = (ItemListBaseAdapter)a.getAdapter();
    synchronized( adapter ){
        Object o = a.getItemAtPosition(position);
        ItemDetails obj_itemDetails = (ItemDetails)o;
        obj_itemDetails.setName("MODIFIED");
        adapter.itemDetailsrrayList.set( position, obj_itemDetails );
        adapter.notifyDataSetChanged();
    }
}    

2-1s click 된 아이템의 Name 필드를 바꾸는 소스

이 코드는 예시 차원에서,
Listview의 아이템을 click 했을 경우 아이템의 이름을 MODIFIED로 변경하는 소스 입니다. 앞선 설명을 다시 되뇌이는 차원에서 소스 설명을 간략히 해보자면, 이벤트가 일어난 item의 position을 통해 item의 model을 가져오고 이를 setName() 메소드를 통해 갱신한 뒤에 model 에 set하고 adapter에 notifyDataSetChanged() 메소드를 호출해서 View에 갱신이 일어나도록 한 것 입니다.

notifyDataSetChanged() 메소드를 역추적하면, ( 4.1.1기준 )AdapterView 내부 AdapterDataSetObserver의 onChanged()를 타게 됩니다. 해당 메소드는 아래와 같은 내용을 가지고 있습니다.

@Override
public void onChanged() {
    mDataChanged = true;
    mOldItemCount = mItemCount;
    mItemCount = getAdapter().getCount();

    // Detect the case where a cursor that was previously invalidated has
    // been repopulated with new data.
    if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
            && mOldItemCount == 0 && mItemCount > 0) {
        AdapterView.this.onRestoreInstanceState(mInstanceState);
        mInstanceState = null;
    } else {
        rememberSyncState();
    }
    checkFocus();
    requestLayout();
}

2-s2 notifyDataSetChange() 호출시 타게 되는 onChanged()


전체 상태를 갱신하는 효율적이지 않아 보이는 코드를 사용하는 것이죠.
이를 해결할 방법은 없을까요?

그럼 갱신할 View와 Data를 알고 있으니 이를 강제로 Update하면 notifyDataSetChanged() 을 호출하지 않아도 되지 않을까요? 이를 바로 실행에 옮겨보겠습니다.

@Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
    // Model을 수정하는 Code 예시 #1
    ItemListBaseAdapter adapter = (ItemListBaseAdapter)a.getAdapter();
    TextView currViewName = (TextView) v.findViewById(R.id.name);
    currViewName.setText("MODIFIED");
   
    Object o = a.getItemAtPosition(position);
    ItemDetails obj_itemDetails = (ItemDetails)o;
    obj_itemDetails.setName("MODIFIED");
    adapter.itemDetailsrrayList.set( position, obj_itemDetails );
   
}

2-s3  notifyDataSetChanged() 호출없이 Model 과 UI 갱신


SUCCESS...?
일리 없겠죠? 하지만 이 소스를 통해 우리는 한 가지를 알 수 있습니다.

그것은 MVC Pattern model#2의 코드 흐름( model update 후에 view를 갱신하는 흐름 )을 타지 않더라도 VIew와 Model을 갱신할 수 있다는 것입니다. 또한, Listener를 통해 이벤트를 자유자재로 다룰 수 있는 환경이라는 점도요. Listener를 통해 정확히 어떤 지점에서 어떤 로직을 사용하면 될 지를 알 수 있습니다.

그렇다면 시행 착오에서 한 발짝 더 나아가 해당 코드를 정립해도록 하겠습니다.


2-2 시행착오 발전시켜나가기

다시 초심으로 돌아가 음식점 메뉴판 앱에서 View가 사용자와 혹은 Model과 상호작용하는 경우를 정리해보도록 하겠습니다.

1. ADD - 메뉴판에 메뉴를 추가한다.
2. DELETE - 메뉴판에 메뉴를 삭제한다.
3. MODIFY - 메뉴판에 메뉴를 수정한다.

불행한 이야기지만, 1번과 2번은 완벽하게 향상된 패턴을 적용하지 못할 것 같습니다. 왜냐하면, Android Framework하에서 ListView는 원칙적으로 외부에서 View를 추가하거나 삭제하는 것을 금하고 있기 때문입니다.

( 사실은 이 제약사항이 이 글을 쓰게된 이유기도 합니다. 처한 상황이 이럴때 최선책을 찾아보는 것이지요. 그리고 private 메소드인 addViewAbove()와 addViewBelow() 가 공개되길 바랍니다. T_T )

하지만 어쨌든, 앞선 예에서 우리는 View와 Model을 갱신시켰고, notifyDataSetChanged()를 호출하지 않음으로 일보 전진 일보 후퇴했습니다. 진보는 MVC 패턴의 제약에서 자유로워져 코드의 가독성이 향상된 반면, 후퇴는 MVC 패턴에선 추상화되어있던 View 가 우리가 Model을 수정하기 위해서 직접 다뤄버리는 바람에 형태가 다소 경직되어 model과 view의 연결성이 생겼고직교성을 떨어트린 것입니다.

그렇다면 우리가 이보 전진하기 위해서는 Model과 View의 수정 과정을 추상화 시키면 되겠군요. 이를 어떻게 추상화 할 수 있을까요? ListView는 각 position의 element들의 추상화를 위해 getView() 메소드에서 반환하는 View를 element로 구성했습니다. 즉, ListView 입장에서는 [ Model의 데이터나 구성은 전혀 신경쓸 필요없이 반환받는 View를 List화 해서 보여주는 본연의 목표에 집중할 수 있었다 ] 는 뜻입니다.

우리는 이 구현 방법의 장점을 따서, Model 과 View의 중간에서 양자를 조율하는 표현 계층을 만들 것 입니다. 이를 이후부터는 Presenter라고 부르겠습니다. 이 Presenter를 통해 View에서 발생하는 이벤트를 다뤄 VIew와 Model을 갱신하고, Model 이 외부 연동을 통해 갱신되는 상황을 가정하여 Model의 갱신 이벤트 또한 Presenter를 통해 View에게 전달되는 과정을 볼 것입니다.



3. Presenter 


3-1 View와 Presenter 간의 관계


Presenter는 Adapter와 비슷한 역할을 맡게 됩니다.
보다 상세히 설명하면, Presenter는 Model과 View에 대해 기술되어 있는 명세를 가지고 View에서 발생된 이벤트를 수신하여 View를 갱신시키고, 또한 그를 Model에 반영시키는 일을 하게 됩니다.


View에서 시작하는 변동사항 적용

lv1.setOnItemClickListener(new myItemClickListener(this));       
lv1.setOnViewChangeListener(new OnViewChangeListener()  {
    @Override
    public void OnAddMenu() {}
    @Override
    public void OnDelMenu() {}
    @Override
    public void OnModMenu(myListView myListView, 
            int position, ItemDetails mItemDetails) {
        // TODO Auto-generated method stub
        
    }          
});

3-s1 Presenter와의 통신을 위한 Listener 등록


 public interface IViewChange {
    public void OnAddMenu();
    public void OnDelMenu();
    public void OnModMenu(myListView myListView, 
            int position, 
            ItemDetails mItemDetails);
   
}
}


3-s2 view의 변동내역을 수신 받기 위한 IViewChange interface


@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
        long id) {
    mParent = parent;
    mView = view;
    mPosition = position;
    mId = id;
    // TODO Auto-generated method stub
    Object o = mParent.getItemAtPosition(position);
    mItemDetails = (ItemDetails)o;
    showModifyMenuDialog(); 
}



private void showModifyMenuDialog() 
{
    LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( mContext.LAYOUT_INFLATER_SERVICE);
    View layout = inflater.inflate(R.layout.dialog, null);
    final EditText edName = (EditText) layout.findViewById(R.id.edName);
    final EditText edDesc = (EditText) layout.findViewById(R.id.edDesc);
    final EditText edPrice = (EditText) layout.findViewById(R.id.edPrice);
    edName.setText( mItemDetails.getName() );
    edDesc.setText( mItemDetails.getItemDescription() );
    edPrice.setText( mItemDetails.getPrice() );
    AlertDialog.Builder aDialog = new AlertDialog.Builder(mContext);
    aDialog.setTitle("Modify Menu");
    aDialog.setView(layout);
    aDialog.setPositiveButton("Mod", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                mItemDetails.setName( edName.getText().toString() );
                mItemDetails.setItemDescription( edDesc.getText().toString() );
                mItemDetails.setPrice( edPrice.getText().toString() );
                ((myListView)mParent).performItemChange( mPosition, mItemDetails );
            }
        });

    AlertDialog ad = aDialog.create();
    ad.show();
    
}



3-s3 OnItemClick 이벤트시 연동되는 Dialog와 performItemChange



 IViewChange mOnViewChangeListener; 

public void setOnViewChangeListener( IViewChange listener ) {
    mOnViewChangeListener = listener;
}

public void performItemChange( View view, int position, long id, ItemDetails mItemDetails ){
    if (mOnViewChangeListener != null) {
        mOnViewChangeListener.OnModMenu(this, position, mItemDetails);

    }


3-s4 ListView의 구현



lv1.setOnItemClickListener(new myItemClickListener(this));        
lv1.setOnViewChangeListener(new OnViewChangeListener()  {
    @Override
    public void OnAddMenu() {}
    @Override
    public void OnDelMenu() {}
    @Override
    public void OnModMenu(myListView myListView, int position, 
            ItemDetails mItemDetails) {
        adapter.itemDetailsrrayList.set(position, mItemDetails);
        int firstPosition = myListView.getFirstVisiblePosition();
        Log.v("TEST", "FP : " + firstPosition + " Position : " + position );
        adapter.getView( position, myListView.getChildAt(position-firstPosition), myListView);
        
    }            
});

}


3-s5 Listener 최종.


다른 코드들은 모두 앞서 설명한 개념 + 그림에서 다룬 내용이므로 사족이 될 수 있는 설명은 줄이도록 하고, Listener에서 최종적으로 구현한 다음 두 메소드에 대해 설명하고자 합니다.

adapter.itemDetailsrrayList.set(position, mItemDetails);
-> Model 갱신
( 해당 소주제는 View-Presenter에 초점을 맞추고 있으므로 Model 갱신은 직접 참조를 통해 갱신했습니다. 이 부분은 3-2에서 수정할 것 입니다. )



adapter.getView( position, myListView.getChildAt(position), myListView);
-> View 갱신


제가 작성하고 첨부할 코드는 Presenter를 Activity 로 잡았지만, 사실 어디라도 상관 없습니다. 해당 Interface를 구현하는 객체가 Presenter를 담당하게 되는 것입니다. 고로 Adapter을 Presenter 역할자로 지정해도 상관 없습니다.

하지만 주의할 점은 Presenter와 Model 혹은 View 간의 통신은 최대한 추상화해서 혹시라도 모를 참조 등을 지양하여 연결성이 생기지 않도록 하는 것 입니다.


이번 단락에서 초점을 맞춰야 할 곳은 [Presenter가 어떻게 View를 갱신시키는 것이 좋은가?] 입니다.

앞서 Android의 Adapter는 getView() 의 반환자인 View를 통해 Model-View의 관계를 추상화했다고 말씀드렸습니다. 그렇다면, Presenter로서 VIew를 갱신시키고자 한다면 View를 다루는 것이 최고의 방법인 것 입니다.

고로 Presenter는
- View의 명세
- Model의 명세
가진 추상화된 클래스가 되어야 합니다. 아래 코드처럼 표현될 수 있습니다.


public abstract class Presenter {
   
    View mView;
    ItemDetails mModel;
   
    public Presenter( View view, ItemDetails model ) {
        mView = view;
        mModel = model;
    }
   
    abstract public View updateView();
}


3-s6 Presenter 구현체 예시

우리는 이와 같은 Presenter를 adapter에 이미 구현해 놨기 때문에 이를 재활용하면 되는 것이죠. 다음의 getView() 메소드 호출은 실제로는 View를 통해 ListView를 갱신하지 않습니다. 앞서 말씀드린대로 ListView는 외부에서의 접근을 원천적으로 막아놨습니다.

하지만 우리는 ViewHolder 패턴을 통해 View의 명세를 가지고 이를 통해 View의 구성요소에 접근할 수 있기 때문에 여기서는 이를 이용하여 갱신을 하는 것 입니다.

하지만 다시 한번, View라는 추상화 된 객체를 통해 갱신하는 것이 보다 나음을 잊지 마시길 바랍니다. 만약 우리의 메뉴판에 뭔가 특별한 기능이 추가되었다고 생각해보죠.

할인 상품의 이미지에 별모양 스티커 같은 것을 달고 50%할인과 같은 표시를 하려고 업데이트를 한다고 생각해봅시다. 만약 우리가 View를 통해 이를 다룬다고 하면, Model의 업데이트 내역이 갱신된 View를 메뉴판에 갱신시키면 됩니다. 아주 간단하죠. 하지만, View에 데이터를 직접 바인딩 시키고 있다면 메뉴판을 전부 갱신해야 합니다. 왜냐면 할인정보라는 data를 binding할 곳이 layout에 없었기 때문이죠. 이렇게 Model 혹은 View 어느 한 편에 변동사항이 있을때 다른 한 쪽에 영향을 미친다면 이는 커플링이 되었다. 혹은 직교성이 떨어진다라고 말할 수 있습니다.



이와 같은 차이점을 직접 보여드리고 싶지만, 이번 글의 범주는 어디까지나 현 Android GUI 패턴의 일부를 개신시키는 것이기 때문에 미루도록 하겠습니다.

다음 글에서는 요런 사항들을 볼 수 있는 예제를 시도해보도록 하죠.


3-2 Model과 Presenter 간의 관계


Presenter는 Model의 변동사항에 대한 이벤트를 수신하고, 변동된 Model의 요소를 View에 반영시켜 주는 역할을 합니다. Presenter가 View에 대한 명세를 가지고 있다는 것을 다시 한번 말씀드립니다.

Model 은 여러 외부 요인에 의해 변경될 수 있기 때문에, 각 상태 변화를 Presenter에 알려주고 이를 통해 변경된 데이터가 View에 갱신되는 과정을 만들어 보도록 하겠습니다.


Model To View 갱신 


public interface IModelChange {
    public void addItem();
    public void DelItem();
    public void ModItem(int position, ItemDetails obj);
}

3-s6 Model change Interface



public interface IModelChange {
    public void addItem();
    public void DelItem();
    public void ModItem(int position, ItemDetails obj);
}



3-s7 Model Change Listener class


일단 Model 에 대한 리스너들을 준비했습니다. 그리고 이번에는 앞의 예에서 썼던 itemclick 이벤트시 name을 변경하는 간단한 예를 통해 View를 갱신시키도록 하겠습니다.



lv1.setOnItemClickListener(new OnItemClickListener(){
            @Override
            public void onItemClick(AdapterView<?> a, View v, int position, long id) {
                // Model을 수정하는 Code 예시 #1
                ItemListBaseAdapter adapter = (ItemListBaseAdapter)a.getAdapter();
                synchronized( adapter ){
                    Object o = a.getItemAtPosition(position);
                    ItemDetails obj_itemDetails = (ItemDetails)o;
                    obj_itemDetails.setName("MODIFIED");
                    adapter.set( position, obj_itemDetails );
                }
            }     
        }); 



3-s8 model을 갱신시키는 간단한 트리거

public void SetOnModelChangeListener( IModelChange listener ){
    ModelChangeListener = listener;
}


public void SetOnModelChangeListener( IModelChange listener ){
    ModelChangeListener = listener;
}



3-s9 Listner 등록 메소드와 갱신용 메소드


adapter.SetOnModelChangeListener( new OnModelChangeListener() {
    @Override
    public void ModItem(int position, ItemDetails obj) {
        int realPosition = position - lv1.getFirstVisiblePosition();
        View targetView = lv1.getChildAt( realPosition  );
        adapter.getUpdateView(realPosition, targetView, obj); 
    } 


});

3-s10 구현된 Listener의 ModItem


이번에는 좀더 간략화 된 View 갱신 메소드를 만들었습니다.
이론은 똑같이 ViewHolder를 통해 View의 명세를 갱신하는 구조입니다.



public View getUpdateView(int position, View view, ItemDetails obj ) {
    ViewHolder holder = (ViewHolder) view.getTag();
    holder.txt_itemName.setText( obj.getName());
    holder.txt_itemDescription.setText( obj.getItemDescription());
    holder.txt_itemPrice.setText( obj.getPrice());
    holder.itemImage.setImageResource( imgid[obj.getImageNumber() - 1] );

    return view;
} 



3-s11  View update



앞서 말씀드린 내용과 많은 부분이 일치하므로 추가 설명은 하지 않겠습니다.



4. 결론

 MVC 패턴이 등장한지 얼마나 오랜 세월이 지났는지 모르겠지만, 여전히 이 패턴이 자주 사용된다는 것을 Android의 컴포넌트들을 살펴보며 느낄 수 있었습니다. MVP나 MVVM 이 새로운 GUI 구현 패턴으로서 등장했지만, 정작 실 사용에 장벽을 느낀다면 다소의 성능이나 구현의 미려함은 기회비용으로 포기할만 하다는 생각이 듭니다.

 이번 글에서 Android에 녹아있는 MVC 패턴의 장단점과 이를 조금이나마 개선하는 방안을 잠시 살펴봤지만 필요성을 느끼기에는 무리가 있을 수도 있다는 생각이 듭니다. 고작 수십 수백의 데이터를 처리하는데 이 정도의 수고는 오히려 오버해드가 될 수도 있으며, LinearLayout을 통해 ListView의 직접 설계해서 사용할 정도로 성능 향상이 요원한 프로젝트가 적용하기 난해한 점들이 많습니다.

 하지만 무엇을 문제 삼고 어떻게 개선하고자 했는가를 통해 보다 깊은 이해를 얻고 지식을 축적하는 것은 여러모로 장점이 될 수 있다고 생각합니다. 만약 ListView와 같은 Adapter를 사용하지 않는 View를 사용하는 분들에겐 조금 도움이 됐기를 바랍니다.


 향후 Fragment 와 Fragment계열 Adapter에서의 GUI 패턴을 다룰 예정인데, 여기서는 향상된 패턴의 장점을 확연히 보여줄 수 있지 않을까? 하는 기대를 하면서 마무리 짓겠습니다.


- fin