[Jetpack] MVVM 및 ViewModel

이동건·2023년 4월 30일
1

jetpack

목록 보기
1/3


MVVM 아키텍쳐에 대해 공부를 하면서 MVC, MVP와는 어떤 차이점이 있는지 정리해보고자 한다. 또한 MVVM에서의 ViewModel과 AAC(Android Architecture Components) ViewModel 과의 차이점에 대해서도 알아보고자 한다.

MVC

MVCModel-View-Controller의 약자로, 사용자에게 보여지는 UI와 데이터 및 로직 처리를 따로 분리한다.

  • Model : 데이터와 데이터를 처리하는 로직을 포함하는 데이터 레이어
  • View : 사용자에게 보여지는 화면
  • Controller : 사용자의 입력을 받아서 처리하는 부분

안드로이드 프로젝트에 적용한다 하면, View는 XML 코드로 구성되고, Controller는 Activity 또는 Fragment 클래스가 담당합니다. 하지만 Controller 역할을 하는 클래스는 View를 보여주는 역할도 수행하고 있어, View와 Controller가 매우 강하게 결합되어 있습니다. 또한, View는 Model에 대한 의존성도 갖고 있습니다. 이러한 이유로 안드로이드에서는 일반적으로 Model-View-Controller (MVC) 패턴을 잘 사용하지 않습니다.


MVP

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

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 사이에는 다음과 같은 특징이 존재한다.

  • ViewModelView의 관계는 일반적으로 1 : N이다. ViewModel 하나를 여러 View가 사용 가능하다. 하지만 필요한 경우가 아니면 View별로 따로 만드는 것이 좋다
  • ViewModelView가 쉽게 사용할 수 있도록 Model의 데이터를 가공하여 View에게 제공한다.

ViewModelViewModel 사이에서 데이터를 관리하고 바인딩해주는 요소이다.

MVC, MVP, MVVM 한눈에 보기

https://vtsen.hashnode.dev/mvc-vs-mvp-vs-mvvm-design-patterns

MVVM의 목표

MVVM 아키텍쳐의 목표는 비즈니스 로직(또는 도메인 로직)프레젠테이션 로직사용자 UI로부터 분리하는 것이다.

Business Logic : https://en.wikipedia.org/wiki/Business_logic
Presentation Logic : https://en.wikipedia.org/wiki/Presentation_logic

  • 비즈니스 로직(또는 도메인 로직) : 사용자의 요청에 맞춰 어떻게 데이터를 생성, 저장 및 변경 할지(CRUD)에 대한 비즈니스 규칙을 코드로 구현
  • 프레젠테이션 로직 : 비즈니스 로직이 사용자에게 어떻게 보여줄 것인지에 대한 로직

즉, 사용자의 요청이 발생하면 Model에서 데이터를 처리하고(비즈니스 로직) 이를 ViewModel이 가공하여 View에게 제공한다(프레젠테이션 로직). ViewModel은 사용자에게 보여질 상태 값을 관리한다.

이 말은 View에서는 프레젠테이션 로직을 작성하지 않아야 함을 의미한다. 만약에 뷰에서 다음과 같이 코드를 작성하면, UI에서 프레젠테이션 로직을 구현한 것이다.

binding.btnSample.setOnClickListener{
	viewModel.sampleState.value = true
}

val sampleState = MutableStateFlow<Boolean>(false)

View에서는 ViewModel을 참조하여 Immutable한 상태를 구독하고, 변화를 관찰하는 형태로 구현하자!


AAC ViewModel

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 vs AAC ViewModel

https://leveloper.tistory.com/216

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://kotlinworld.com/42

https://vtsen.hashnode.dev/mvc-vs-mvp-vs-mvvm-design-patterns

profile
성장하는 활동적인 개발자

0개의 댓글