MVVM 아키텍쳐에 대해 공부를 하면서 MVC, MVP와는 어떤 차이점이 있는지 정리해보고자 한다. 또한 MVVM에서의 ViewModel과 AAC(Android Architecture Components) ViewModel 과의 차이점에 대해서도 알아보고자 한다.
MVC
는 Model-View-Controller의 약자로, 사용자에게 보여지는 UI와 데이터 및 로직 처리를 따로 분리한다.
안드로이드 프로젝트에 적용한다 하면, View는 XML 코드로 구성되고, Controller는 Activity 또는 Fragment 클래스가 담당합니다. 하지만 Controller 역할을 하는 클래스는 View를 보여주는 역할도 수행하고 있어, View와 Controller가 매우 강하게 결합되어 있습니다. 또한, View는 Model에 대한 의존성도 갖고 있습니다. 이러한 이유로 안드로이드에서는 일반적으로 Model-View-Controller (MVC) 패턴을 잘 사용하지 않습니다.
MVP
는 Model-View-Presenter의 약자로, MVC의 Controller 대신 Presenter
가 등장한다.
Presenter
는 View에서 사용자로부터 액션을 받으면, 모델로 데이터 처리 요청을 보내고 결과를 수신하면 UI를 업데이트한다. Presenter와 View의 관계는 1:1 이다.
안드로이드에서 Presenter를 구현한다하면, View는 xml 코드로 구성되고 Presenter는 Activity/Fragment가 된다. MVC와 다르게 View가 Model에 대해 직접적인 의존성을 가지진 않지만, 아직도 View와 Presenter 사이가 강하게 결합되어 있다.
MVVM은 Model-View-ViewModel의 약자로, MVP에서의 Presenter 대신 ViewModel
이라는 요소가 등장한다. 기존 MVP에서는 View와 Presenter가 강하게 결합되어 있지만, MVVM에서 View와 ViewModel은 느슨하게 결합되어 있다.
ViewModel
에서는 View를 조작하지 않는다. View에서 연결된 상태값을 업데이트 및 관리를 하거나 연결된 메서드를 구현한다. 즉, ViewModel은 View에 대한 참조를 가지지 않으며, View에게 변경 사항을 알려주는 역할을 한다. View는 ViewModel의 상태 변화를 관찰(옵저빙)한다. 정리하면 ViewModel은 View와 Model 사이에서 데이터를 관리하고 바인딩 해주는 요소이다.
ViewModel과 View 사이에는 다음과 같은 특징이 존재한다.
ViewModel
과 View
의 관계는 일반적으로 1 : N
이다. ViewModel 하나를 여러 View가 사용 가능하다. 하지만 필요한 경우가 아니면 View별로 따로 만드는 것이 좋다ViewModel
은 View
가 쉽게 사용할 수 있도록 Model
의 데이터를 가공하여 View
에게 제공한다.⇒ ViewModel
은 View
와 Model
사이에서 데이터를 관리하고 바인딩해주는 요소이다.
https://vtsen.hashnode.dev/mvc-vs-mvp-vs-mvvm-design-patterns
MVVM 아키텍쳐의 목표는 비즈니스 로직(또는 도메인 로직)
과 프레젠테이션 로직
을 사용자 UI로부터 분리하는 것이다.
Business Logic : https://en.wikipedia.org/wiki/Business_logic
Presentation Logic : https://en.wikipedia.org/wiki/Presentation_logic
즉, 사용자의 요청이 발생하면 Model에서 데이터를 처리하고(비즈니스 로직) 이를 ViewModel이 가공하여 View에게 제공한다(프레젠테이션 로직). ViewModel은 사용자에게 보여질 상태 값을 관리한다.
이 말은 View에서는 프레젠테이션 로직을 작성하지 않아야 함을 의미한다. 만약에 뷰에서 다음과 같이 코드를 작성하면, UI에서 프레젠테이션 로직을 구현한 것이다.
binding.btnSample.setOnClickListener{
viewModel.sampleState.value = true
}
val sampleState = MutableStateFlow<Boolean>(false)
View에서는 ViewModel을 참조하여 Immutable한 상태를 구독하고, 변화를 관찰하는 형태로 구현하자!
https://developer.android.com/topic/libraries/architecture/viewmodel?hl=ko#kotlin_1
AAC ViewModel
은 안드로이드 생명 주기를 고려하여 UI 관련 데이터를 저장하고 관리하도록 설계된 클래스이다. UI 컨트롤러가 제거되거나 재생성되는 경우(구성이 변경되는 경우) 일시적 데이터가 삭제되어 데이터 관리가 어려웠던 점을 해결해준다.
구글 공식문서에는 Activity/Fragment를 UI 컨트롤러라고 부르고 있다.
UI 컨트롤러가 생성 및 파괴가 되고 난 후에 ViewModel이 생성되고 파괴가 되기 때문에, 구성이 변경되어 View를 새로 그리는 상황에서도 데이터가 유지가 된다.
주의: ViewModel은 뷰, Lifecycle 또는 활동 컨텍스트 참조를 포함하는 클래스를 참조해서는 안 된다.
ViewModel 객체는 뷰 및 생명주기 객체에 관해 모르기 때문에, 뷰 클래스를 참조해서는 안된다. 만약 정말 ViewModel안에서 생명주기 객체가 필요하면, Application Context를 사용해야 한다.
MVVM ViewModel
은 View에서 보여질 상태값을 관리하고, 비즈니스 로직을 담당하여 데이터를 처리하고 바인딩 해주는 요소이지만, AAC ViewModel
은 안드로이드 생명주기를 고려하여 UI 상태 값을 관리하는 클래스이다. 둘 다 UI 상태값을 관리한다는 면에서는 동일하지만, AAC ViewModel은 생명주기를 고려하여 설계되었으며 데이터 바인딩을 직접 처리하지 않는다.
즉 AAC ViewModel을 사용한다 해서 MVVM 패턴을 구현한 것은 아니다. 이는 공식문서를 아무리 읽어봐도 MVVM 아키텍쳐에 대한 언급이 없는 점에서도 볼 수 있다. ViewModel 내에서 LiveData, StateFlow 및 Observable 객체를 사용하고 데이터 바인딩 해주면 MVVM 패턴의 ViewModel을 구현할 수 있다.
다음과 같이 데이터 바인딩 라이브러리를 사용하면 쉽게 구현할 수 있다.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.mvvm.MyNumberViewModel" />
</data>
<TextView
android:id="@+id/tv_number"
android:text='@{viewModel.currentValue}'
android:textSize="16sp"
android:layout_marginStart="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</layout>
혹은 Jetpack Compose를 사용한다면, 컴포저블 내에서 ViewModel 내의 상태값을 관찰하도록 하면 된다.
@Composable
fun ProfileScreen(
viewModel: ProfileViewModel = hiltViewModel(),
){
val profileState: ProfileState by viewModel.viewState.collectAsStateWithLifecycle()
Text(text = profileState.name)
}
https://leveloper.tistory.com/216
https://vtsen.hashnode.dev/mvc-vs-mvp-vs-mvvm-design-patterns