MVC, MVP, MVVM 패턴이란?

98oys·2022년 7월 27일
0

language

목록 보기
7/9

MVC 패턴은 모델(Model), 뷰(View), 컨트롤러(Controller)로 이루어진 디자인 패턴입니다.

🍟 Model, 모델

모델은 애플리케이션의 데이터인 데이터베이스, 상수, 변수 등을 뜻합니다.

모델은 아래와 같은 규칙들을 가지고 있습니다.

1. 사용자가 편집하길 원하는 데이터를 가지고 있어야한다.

  • 예를 들어 사각형 모양의 박스 안에 글자가 들어 있다면 그 사각형 모양의 박스 위치 정보, 글자 내용, 글자 위치 등에 관한 정보를 모두 가지고 있어야합니다.

2. 뷰나 컨트롤러에 대해서 어떠한 정보도 알 수 없어야한다.

  • 데이터의 변화가 일어났을 때, 모델이 직접 UI를 수정하는 로직을 갖으면 안된다는 의미입니다

3. 변화가 있을 때, 변화에 대한 처리방법을 구현해야한다.

  • 모델에 변화가 있을 때, 이벤트를 발생시켜 전달해야하며, 이벤트에 반응하는 로직을 구현해야합니다.

Model - 백그라운드에서 동작하는 비지니스 로직 처리

🍔 View, 뷰

뷰는 TextView, EditText, Checkbox 등 사용자 인터페이스 요소를 나타냅니다. 즉, 모델을 기반으로 사용자가 볼 수 있는 화면을 뜻합니다.

뷰는 아래와 같은 규칙들을 가지고 있습니다.

1. 모델이 가지고 있는 정보를 따로 저장해서는 안된다.

  • 값을 표시 하기 위해, 모델이 가지고 있는 정보를 전달받는다. 그 정보를 유지하기 위해 임의의 뷰 내부에 저장해서는 안된다.

2. 모델이나 컨트롤러와 같이 다른 구성요소들을 알면 안된다.

  • 자기 자신을 빼고는 다른 요소를 참조하면 안된다. 뷰는 데이터를 받고 표시해주는 역할만 합니다.

3. 변화가 있을 때, 변화에 대한 처리방법을 구현 해야한다.

  • 모델과 같이 변경이 일어나면, 변경을 알리는 이벤트를 받아 동작하는 로직을 구현해야합니다.

정보를 화면으로 보여주는 역할

🍿 Controller, 컨트롤러

컨트롤러는 하나 이상의 모델과 하나 이상의 뷰를 잇는 다리 역할을 하며 이벤트 등 메인 로직을 담당합니다. 또한, 모델과 뷰의 생명주기도 관리하며, 모델이나 뷰의 변경 통지를 받으면 이를 해석하여 각각의 구성 요소에 해당 내용에 대해 알려줍니다.

컨트롤러는 아래와 같은 규칙들을 가지고 있습니다.

1. 뷰와 모델에 대한 정보를 갖고 있다.

  • 뷰와 모델은 서로에 대한 정보를 갖지 않습니다. 변경을 외부로 알리고, 수신하는 방법만 갖고 있는데 이를 컨트롤러가 중재자 역할을 하기 위해 뷰와 모델에 대한 정보를 갖습니다.

2. 뷰나 모델의 변경을 모니터링 해야한다.

  • 뷰나 모델의 변경 이벤트를 받으면 해석하기 위해 각각의 구성 요소에게 알려야합니다.

사용자의 입력 처리와 흐름 제어 담당, 화면과 Model과 View를 연결시켜 주는 역할

😊이제까지 모델(Model), 뷰(View), 컨트롤러(Controller)의 특성에 대해서 알아보았습니다.


🙄왜 MVC 패턴을 사용할까?

  • 비지니스 로직과 UI로직을 분리하여 유지보수를 독립적으로 수행가능합니다.
  • 모델과 뷰가 다른 컴포넌트들에 종속되지 않아 애플리케이션의 확장성, 유연성, 재사용성에 유리합니다.
  • 중복 코딩의 문제점을 제거합니다.

KeyPoint : 재사용성, 확장성

😨MVC 패턴의 한계점

ViewController에 연결되어 화면을 구성하는 단위 요소이므로 다수의 View를 가질 수 있습니다.
ModelController를 통해 View와 연결되지만, Controller에 의해서 하나의 View에 연결될 수 있는 Model로 여러 개가 될 수 있어 View와 Model이 서로 의존성을 띄게 됩니다.
즉, 애플리케이션이 복잡해질수록 모델과 뷰 관계가 복잡해지는 단점이 있습니다.

KeyPoint : 관계의 복잡성 증가

😄 MVP 패턴

MVP 패턴은 MVC 패턴으로부터 파생되었으며 MVC에서 C에 해당하는 컨트롤러가 프레젠터(Presenter)로 교체된 패턴입니다.

Presenter는 MVC의 Controller와 매우 유사하지만, 다른 점이 있다면 View에 남아 있는 모든 비지니스 로직을 전부 Presenter로 통합하는데 차이점이 있습니다.

MVC 패턴과 MVP 패턴의 차이점

MVC 패턴 View : Controller = N : 1
MVP 패턴 View : Controller = 1 : 1

뷰와 프레젠터는 1:1 관계이기 때문에 MVC 패턴보다 더 강한 결합을 지닌 디자인 패턴입니다.

  • MVC는 하나의 Controller에서 여러 View를 컨트롤할 수 있으나, MVP는 각 View마다 Presenter가 할당됩니다.
  • MVC에서는 Controller에서 user event를 처리하지만, MVP는 View에서 user event를 받습니다.
  • MVC의 Controller에서 user evnet에 따른 데이터 로직을 수행합니다.
  • MVP에서 View는 오로지 user evnet를 Presenter로 전달만 합니다.

🥓 MVVM 패턴

MVVM 패턴은 MVC에 C에 해당하는 컨트롤러가 뷰모델(ViewModel)로 바뀐 패턴입니다.
이 패턴의 목표는 비지니스 로직과 프레젠테이션 로직을 UI로부터 분리하는 것입니다.
두 로직을 UI로부터 분리하게 되면, 테스트, 유지보수, 재사용성이 쉬워집니다.

ViewModel, 뷰모델

하나의 소프트웨어를 최대한 기능적으로 작은 단위로 분할해 테스트가 쉽고 큰 프로젝트를 상대적으로 관리하기 쉬운 도구입니다.
모든 입력은 View로 전달되며, ViewModel은 입력에 해당하는 로직을 처리하여 View에 데이터를 전달합니다.

ViewModelView를 따로 참조하지 않기 때문에 독립적이며 ViewModel : View=1 : N 관계입니다.

MVVM의 장점

  1. MVVM 사이의 의존성이 없으므로 유닛 테스트가 더 쉬워집니다. (모델이 변경되는 시점에 변수가 제대로 설정됐는지 확인하면 됩니다., UI 가 그려지는 방법을 모르기 때문에 UI를 그리는 방법이 달라져도 뷰모델을 수정하지 않아도 됩니다.)
  2. Databinding 라이브러리를 이용함으로써 서로 간의 의존성을 낮추고, 유닛 테스트를 더욱 쉽게 작성할 수 있고 UI코드는 네이티브 코드에 관여하지 않아도 됩니다.
  3. 중복되는 코드를 모듈화 할 수 있습니다.

MVVM의 단점

  1. View가 변수와 표현식 모두 binding 될 수 있으므로 시간이 지남에 따라 관계없는 로직이 늘어나고 이를 보완하기 위해 XML 코드를 추가하게 됩니다. ( 설계의 어려움 )
  2. DataBinding이 필수적으로 요구됩니다.
  3. 애플리케이션이 커질수록 Controller처럼 ViewModel이 빠르게 비대해집니다.
  4. 표준화된 틀이 없습니다.

안드로이드에서 ViewModel을 사용하는 이유

ViewModel의 역할 : UI 관련 데이터를 저장하고 관리해주는 역할을 합니다.

안드로이드에서 화면이 회전할 때 Activity는 onDestroy()가 불려 View가 메모리에서 떨어졌다가 다시 onCreate()가 불리면서 다시 할당됩니다. 그래서 View를 통해 보여줄 데이터를 유지할 수 가 없습니다. 아래의 그림에서 생명주기를 확인해보면,

activity가 rotated가 되어도 viewModel이 onCleared()가 불리지 않아 소멸하지 않는 것을 확인할 수 있습니다. 그러므로 ViewModel를 통해 UI 관련 데이터를 안전하게 가지고 있을 수 있습니다.

ViewModel - View 예제

activity_main

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <import type="com.market.mvvmpatternexample.MainViewModel" />
        <variable
            name="mainViewModel"
            type="MainViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/count"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{Integer.toString(mainViewModel.count)}"
            android:textSize="50sp"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:backgroundTint="#03A9F4"
            android:onClick="@{() -> mainViewModel.onClickButton()}"
            android:text="클릭!"
            android:textSize="13sp"
            android:textStyle="bold"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@id/count" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

MainViewModel

package com.market.mvvmpatternexample

import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MainViewModel : ViewModel() {
    private var _count = MutableLiveData<Int>()
    val count: LiveData<Int> get() = _count

    init {
        _count.value = 0
    }

    fun onClickButton() {
        Log.d("ViewModel", "$count")
        _count.value = count.value?.plus(1)
    }
}

MainActivity

package com.market.mvvmpatternexample

import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.market.mvvmpatternexample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private val mainViewModel: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        binding.lifecycleOwner = this
        binding.mainViewModel = mainViewModel
    }
}

데이터바인딩

UI요소와 데이터를 프로그램적 방식으로 연결하지 않고, 선언적 형식으로 결합할 수 있게 도와주는 라이브러리를 말합니다. 즉, UI와 로직을 분리하기 위한 라이브러리

(컴퓨터 프로그래밍)

제공자와 소비자로부터 데이터 소스를 함께 묶어 동기화하는 일반적인 기법. XML 데이터 바인딩과 UI 데이터 바인딩에서와 같이 서로 다른 언어를 사용하는 두 개의 데이터/정보 소스로 이루어집니다. UI 데이터 바인딩에서는 언어와 다른 로직 함수의 데이터 및 정보 객체가 서로 결합됩니다.

Databinding의 장점

  1. data가 바뀌면 자동으로 View를 변경
  2. findViewId를 호출하지 않아도 xml에 있는 View를 만들어줌
  3. xml 리소스만 보고 View에 어떤 데이터가 들어있는지 유추 가능
  4. 코드 가독성 증가, 코드량 감소

단점

  1. Class 파일 증가
  2. 빌드 속도 저하
profile
Android Developer, Department of Information and Communication Engineering, Inha University

0개의 댓글