sysout-achieve / TIL_repo

Today I Learned
0 stars 0 forks source link

11/6 Android 와 개발 패턴 (MVC, MVVM, MVP) #48

Open sysout-achieve opened 5 years ago

sysout-achieve commented 5 years ago

Android와 개발패턴

코드를 작성해가는데 있어서 많은 개발자들은 일관성을 유지하고 유지보수성을 높일 수 있는 많은 개발 모델들을 생각해왔다. 특히나 협업하는 과정에서 로직과 View를 다루는 코드가 뒤섞이게 되면 작성자뿐만 아니라 동료들조차 유지보수가 힘들어지는 모습을 쉽게 볼 수 있다.

이러한 코드 작성때문에 팀단위, 프로젝트단위, 회사단위로 여러 정책을 통해 코드와 개발 모델을 일관되게 하려고 노력한다. 이러한 노력에 개발자들 사이에서 많이 통용되는 개발 패턴을 분석하려 한다.

초창이 안드로이드 개발코드

public class MainAcivity extends Activity {

    @Override
    public void onCreate(Bundle saveInstance) {
       super.onCreate(saveInstance);
       setContent(R.layout.main);

       TextView textView = (TextView) findViewById(R.id.btn_confirm);
       textView.setText("Non-Clicked");

       findViewById(R.id.btn_confirm).setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               TextView textView = (TextView) findViewById(R.id.btn_confirm);
               textView.setText(getClickedText());
           }
       });
    }
    String getClickedText() {
       return "Clicked!!!";
    }
}

Activity 내에 이벤트를 핸들링하는 처리나 뷰에 접근하는 코드들이 모두 있다. 이러한 코드들의 모습은 서버기반 동작 시엔 하나의 Activity내에 네트워크 처리를 위한 쓰레드처리까지 하게 되는 등 코드가 커지면 커질수록 가독성도 떨어지고 유지보수가 힘들어지게 된다. 그래서 기존의 웹에서처럼 좀 더 기능별로 분할하여 코드들을 간결하고 유지보수가 쉬워지도록 하기 위한 방법들이 많이 도입되기 시작하였다.

}


View에 상관없는 로직을 MainModel로 분리하여 Activity는 view와 click event를 처리하는 모습으로 변화하였다. MVC모델에서 Controller는 View에 대한 처리를 직접 하는 것이 아니라 View에 대한 정보만을 View에 전달함으로써 화면을 그리는 View와 동작을처리하는 로직을 분리하고자 하는데서 시작되었다.
그러나 Controller의 역할을 수행하는 Activity에서 View에 대한 직접적인 조작을 수행하는 것은 MVC 모델에 어긋나는 모습을 볼 수 있다.
이는 안드로이드에서 activity가 view와 controller 두 가지의 특성을 모두 가지고 있기 때문에 view나 controller를 한쪽으로 빼게 될 경우 view에 대한 바인딩이나 처리에서 중복코드나 일관성을 잃어버리는 코드를 작성할 수 있기 때문이다. 

- MVVM

public class MainAcivity extends Activity { private MainViewModel mainViewModel;

@Override
public void onCreate(Bundle saveInstance) {
    super.onCreate(saveInstance);
    setContent(R.layout.main);
    mainViewModel = new MainViewModel(MainActivity.this);
}

}

public class MainModel { public String getClickedText() { return "Clicked!!!"; } }

public class MainViewModel { private Activity activity; private MainModel mainModel; private TextView textView;

public MainViewModel(Activity activity) {
    this.activity = activity;
    this.mainModel = new MainModel();
    initView(activity);
}

private void initView(Activity activity) {
    textView = (TextView) activity.findViewById(R.id.btn_confirm);
    textView.setText("Non-Clicked");
    activity.findViewById(R.id.btn_confirm)
        .setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                String text = mainModel.getClickedText();
                textView.setText(text);
            }
        });
}

}

ViewModel로 view에 대한 처리가 분리되었음을 볼 수 있다. 하지만 안드로이드에서 MVVM이 가지는 문제점은 View에 대한 처리가 복잡해질수록 ViewModel이 거대해지게 되고 상대적으로 Activity는 아무런 역할도 하지 않는 형태의 클래스로 변모하게 된다.
Controller의 성격을 지닌 activity가 실질적으로 아무런 역할을 하지 못하고 ViewModel에 치중된 모습을 보여주면서 다른 형태의 activity 클래스를 구현한 꼴이 되어버린다.
MainViewModel에 있는 로직을 다시 activity로 롤백한다하면 다시 MVC가 가지고 있는 문제점을가지게 되는 아이러니한 모습을 볼 수 있다.

이러한 View - Model - Controller의 모습을 명확히 구분하고자 MVP 모델이 나오게 되었다.

- MVP

public interface MainPresenter { void setView(MainPresenter.View view); void onConfirm(); public interface View { void setConfirmText(String text); } }

public class MainAcivity extends Activity implements MainPresenter.View { private MainPresenter mainPresenter; private Button confirmButton; @Override public void onCreate(Bundle saveInstance) { super.onCreate(saveInstance); setContent(R.layout.main); mainPresenter = new MainPresenterImpl(MainActivity.this); mainPresenter.setView(this); confirmButton = (Button) findViewById(R.id.btn_confirm); confirmButton.setOnClickListener(new View.OnClick() { @Override public void onClick(View view) { mainPresenter.onConfirm(); } }); } @Override public void setButtonText(String text) { confirmButton.setText(text); } }

public class MainModel { public String getClickedText() { return "Clicked!!!"; } }

public class MainPresenterImpl implements MainPresenter { private Activity activity; private MainPresenter.View view; public MainPresenterImpl(Activity activity) { this.activity = activity; this.mainModel = new MainModel(); } @Override public void setView(MainPresenter.View view) { this.view = view; } @Override public void onConfirm() { if (view != null) { view.setConfirmText(mainModel.getClickedText()); } } }


MVP 모델의 구분은 다음과 같다.

1. View 는 실제 view 에 대한 직접적인 접근을 담당한다.
2. view 에서 발생하는 이벤트는 직접 핸들링하나 Presenter 에 위임하도록 한다.
3. Presenter 는 실질적인 기능을 제어하는 곳으로써 ViewController 로써 이해하면 쉽다.
4. Model 은 비지니스 로직을 실질적으로 수행한다.

<br>
<hr>
<br>

![image](https://user-images.githubusercontent.com/35280158/48052287-d6f86500-e1ea-11e8-98c8-7b5904ccd82a.png)

MVC
외부의 모든 이벤트를 Controller(Activity) 에서 처리하되 View 에 관여는 하지 않는 것이 원칙이다. 하지만 Activity 의 특성상 View 관여하지 않을 수 없다. 결국 Android 에서는 MVC 의 구조를 계속적으로 유지하기에는 무리가 있다.

MVVM
ViewModel 이 뷰와의 상호작용 및 제어에 집중한다. ViewModel 에서 직접 뷰의 이벤트를 처리하기 때문에 Controller 의 역할도 함께 수행하게 되어 점점 코드가 집중 되는 구조가 될 수 있다. 또한 초기화와 외부 이벤트(뷰에 의한 것이 아닌 이벤트)를 제외 하고는 Activity 의 역할이 모호해질 수 있다.

MVP
View 에 대한 직접적인 접근이 요구되는 Android 의 Activity 는 직접적인 view 접근은 Activity 가 하도록 하고 이에 대한 제어는 Presenter 가 하도록 하고 있다.

어느 것이 낫다라고 말하기에는 어려운 단계이다. 하지만 많은 개발자들이 안드로이드에서는 MVC 자체의 모습보다는 MVVM 이나 MVP 가 가장 적합하다는 말을 많이 하고 있다. 이는 Activity 가 View 를 포함한 클래스이기 때문에 나타나는 현상이라고 볼 수 있다.

<br>
<hr>

> [출처] (https://tosslab.github.io/android/2015/03/01/01.Android-mvc-mvvm-mvp.html)
sysout-achieve commented 5 years ago

패턴형식으로 개발이 자연스러울 때까지 복습!