Study/Android

MVC, MVP, MVVM 을 예제와 함께 알아보자 - android

긷뚜 2021. 5. 14. 22:46
728x90

1. 시작하기 전에

MainActivity.java
public class MainActivity extends AppCompatActivity {

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //뷰 작성
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.text_view);

        //이벤트 처리후 뷰 업데이트
        textView.setOnClickListener(view -> {
            textView.setText(clickedTextView());
        });
    }
    private String clickedTextView(){
        return "Hello World!";
    }

기존에 별다른 생각 없이 코드를 작성 했을때는 위와같은 방법으로 대부분 구현했다.
아주 간단한 예제이지만 뷰, 이벤트 리스너, 데이터 처리 등이 모두 한 액티비티안에 존재한다.
이러한 식으로 구현하면 규모가 커질수록 하나의 액티비티가 복잡하고 비대해져 문제가 생길 수 있고 문제에 대한 유지보수에도 어려움이 있을 수 있다.
또 액티비티 특성상 LifeCycle에 따른 영향도 있을 것이며 데이터도 안전하게 다루지 못한다.

따라서 LifeCycle의 영향, 코드의 복잡, 비대에대한 영향, 앱 사용 환경 등 다양한 이유와 문제점을 바탕으로 좀 더 안전하고 깔끔한 개발을 위해 아키텍처 패턴이 생겨났다

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal|center_vertical">

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="push!" />
</LinearLayout>

2. MVC

  • 구조 및 동작
    • MVC 패턴에서는 사용자 입력은 컨트롤러(Activity)를 통해 들어오며 컨트롤러는 모델과 상호작용을 통해 View(xml)를 업데이트 한다.
    • 이때 뷰는 모델을 참조하게 된다.
  • 특징

    • Controller(Activity)

      • 앱을 묶어주는 접착제 역할(Activity / fragment)
      • 사용자에게 입력을 받아 해당하는 모델을 선택하여 처리한다.
      • 모델의 데이터 변화에 따라 뷰를 선택한다.
    • Veiw(XML)

      • 사용자에게 제공되는 UI
      • UI, 앱과의 상호작용에서 컨트롤러와의 통신
      • 사용자가 어떤 액션을 하든 무엇을 해야할지 모름
    • Model

      • 데이터 + 상태 + 비지니스 로직
      • View나 컨트롤러에 묶이지 않아 재사용 가능
  • 장단점

    • 장점

      • Model과 View를 분리
      • Model의 비종속성으로 재사용 가능
      • 구현하기 가장 쉬움
    • 단점

      • 컨트롤러가 View에 결합되며, VIew의 확장이 될 수 있음
      • View와 Model 사이의 업데이트를 위해 직/간접적으로 참조 이로 인해 서로간의 의존성을 완벽하게는 없앨 수 없음
      • 규모가 커질수록 컨트롤러에 많은 코드가 쌓여 비대화 문제 발생
  • 예제
MainActivity.java
public class MainActivity extends AppCompatActivity {

    private Model model;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //View 작성
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.text_view);

        //Controller 작성
        model = new Model();
        textView.setOnClickListener(view -> {
            textView.setText(model.clikedButton());//Model 부분
        });
    }
}
Model
public class Model {
    String clikedButton(){
        return "Hello World! - MVC";
    }
}

예제의 MainActivity.java 를 보면 View와 클릭이벤트 즉 Model의 처리가 함께 있는것을 확인 할 수 있다. 이러한 직/간접적인 참조를 개선하기 위해 MVP와 MVVM 아키텍처 패턴이 생겨났다.

3. MVP

  • 구조 및 동작
    • Model과 View는 MVC의 개념과 동일하다
    • Model과 View를 분리 시키기 위해 사이에 Presenter라는 개념을 추가시킨다
    • 사용자 입력은 이제 View를 통해 들어온다.
    • View는 이러한 이벤트를 Presenter로 전달하고 Presenter는 Model과의 상호작용을 통해 View에게 업데이트 할 내용을 전달한다.
    • 내용을 전달받은 View가 최종적으로 업데이트 된다
    • 이로 인해 Presenter와 View는 1:1 관계를 유지한다
  • 특징

    • Presenter

      • Model과 View의 상호작용 관리
        • 컨트롤러와 본질적으로는 동일 하지만 View에 연결되지 않는 단순 interface이다.
      • VIew에게 표시할 내용만 전달
    • View

      • 사용자에게 제공되는 UI
      • Activity / fragment가 View의 일부로 간주된다.
      • 사용자의 입력을 받고 이벤트를 Presenter로 전달한다
    • Model

      • MVC와 동일
  • 장단점

    • 장점

      • Model과 View의 의존성이 존재하지 않는다
      • Model은 Presenter의 요청만을 수행한다
    • 단점

      • 규모가 커짐에 따라 Presenter도 추가 비지니스 로직이 모여 비대화 된다
      • MVC에 비해 필요한 클래스 수가 증가한다
      • View와 Presenter의 1:1 관계로 인한 의존성 증가
  • 예제
Model
public class Model {
    String clikedButton(){
        return "Hello World! - MVP";
    }
}
Presenter(단순 인터페이스)
public interface Presenter {
    void confirm();

    interface View{
        void setText(String text);
    }
}
PresenterImpl(Presenter의 구현체)
public class PresenterImpl implements Presenter{
    private Presenter.View view;
    private Model model;

    PresenterImpl(View view){
        this.view = view;
        this.model = new Model();
    }

    // Model에 데이터 요청 후 View에 업데이트 내용 전달
    @Override
    public void confirm(){
        if(view != null){
            view.setText(model.clikedButton());
        }
    }

}
MainActivity.java
public class MainActivity extends AppCompatActivity implements Presenter.View {

    private Presenter presenter;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        presenter = new PresenterImpl(MainActivity.this);

        textView = findViewById(R.id.text_view);
        textView.setOnClickListener(view -> { presenter.confirm();}); //Presenter로 이벤트 전달
    }

    // Presenter로 부터 전달받은 내용으로 View 업데이트
    @Override
    public void setText(String text){
        textView.setText(text);
    }
}

MVP는 Presenter가 Model과 View 사이에서 관리를 해주기 때문에 Model과 View 사이의 의존성이 없다.
그러나 규모가 커지면 Presenter와 View 의 1:1 관계로 인해 의존성이 강해지는 문제점이 있다.
이러한 문제를 해결하기위해 MVVM 아키텍처 패턴이 생겨났다

4. MVVM

  • 구조 및 동작
    • Model과 View는 MVC의 개념과 동일하다
    • MVP와 마찬가지로 View와 Model을 분리시키기 위해 ViewModel이라는 개념이 들어온다
    • View는 사용자 입력에 따른 자신이 이용할 ViewModel을 선택해 바인딩하여 업데이트를 받는다
    • ViewModel과 Model이 상호작용을 하여 Model이 변경되면 ViewModel을 이용하는 View가 자동으로 업데이터 된다.
    • 이로인해 View와 Model 사이의 의존성이 없고, MVP 처럼 View 와 ViewModel이 1:1 관계가 아닌 독립적이기 때문에 이 둘 사이의 의존성이 없다
  • 장단점

    • 장점

      • View에 대한 의존성이 전혀 없으므로 유닛 테스트에 용이
      • 중복되는 코드를 모듈화 할 수 있음
    • 단점

      • ViewModel의 설계가 어렵다
      • View에대한 처리가 복잡할수록 ViewModel이 거대해진다
      • 상대적으로 View는 아무 역할도 하지 않음
      • ViewModel이 또다른 형태의 액티비티 클래스 구현으로 변질될 우려가 있다
  • 특징

    • ViewModel

      • View를 나타내주기 위한 Model + View의 로직 담당
      • View와 독립적
      • UI 관련 데이터 보관 및 관리
      • Model이 변경되면 해당 ViewModel을 사용하는 View가 자동으로 업데이트
    • View

      • 사용자에게 제공되는 UI
      • 사용자의 입력을 받고 이벤트를 자신이 사용할 ViewModel로 전달
    • Model

      • MVC와 동일하다
  • 예제

Model
public class Model {
    String clikedButton(){
        return "Hello World! - MVVM";
    }
}
ViewModel
public class ViewModel {
    private Activity activity;
    private Model model;
    private TextView textView;

    ViewModel(Activity activity){
        this.activity = activity;
        this.model = new Model();
        initView();
    }

    private void initView(){

        //View의 표현과 Model과의 상호작용
        textView = activity.findViewById(R.id.text_view);
        textView.setOnClickListener(view -> {
            textView.setText(model.clikedButton());
        });
    }
}
MainActivity.java
public class MainActivity extends AppCompatActivity {

    private ViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //ViewModel에 위임
        viewModel = new ViewModel(MainActivity.this);
    }
}

ViewModel의 initView() 부분을 보면 기존 액티비티에서 하던 일을 ViewModel이 하는것을 볼 수 있다.
이것이 MVVM 패턴이 액티비티의 LifeCycle의 영향을 받지 않고 ViewModel 인스턴스가 유지되면서 데이터를 안전히 다룰 수 있는 이유다.
그러나 앞서말했듯이 View가 할 일을 ViewModel이 대신 하기때문에 ViewModel에 로직들이 모이고 또다른 View 클래스를 생성한 꼴이 될 수 있으므로 유의하며 구현해야 한다.


reference

https://stonybean.blogspot.com/2019/03/blog-post.html
https://kkangsnote.tistory.com/9
https://brunch.co.kr/@oemilk/113
728x90