[Android] MVVM 패턴 박살내기

MVVM 패턴이란?

코드를 Model, View, ViewModel의 역할에 맞게 분리하서 작성하는 디자인 패턴


왜 MVVM을 사용할까?

1. 사용안하면 뭐 어쩔건데?

MVVM 패턴을 사용하지 않고 액티비티/프래그먼트에 모든 코드를 넣게 되면 어떻게 될까요?

  1. 액티비티/프래그먼트 코드가 매우 복잡해짐
  2. 스파게티 코드가 될 확률이 높아짐
  3. 유지보수 & 테스트가 어려워짐
  4. 다른 사람이 코드를 이해하기 어려워짐 (본인 포함)

이런 단점이 있기 때문에 코드를 분리해서 작성하는 MVVM 패턴을 사용한다.


2. MVVM말고 다른 패턴(MVC, MVP)를 사용하면 안되나요?

MVC의 경우 Model, View, Controller로 코드를 관리
하지만 View와 Model 사이의 의존성이 높다는 단점이 있다.

MVP의 경우 Model, View, Presenter로 코드를 관리
하지만 View와 Presenter 사이의 의존성이 높다는 단점이 있다.


아니 … 의존성은 또 뭐고 그게 높으면 뭐 어떻게 되는데요??? -> 이거 … 보고 오시죠!

-> MVVM > (MVC, MVP)


3. 협업이 쉬워짐

(다른 디자인 패턴도 마찬가지이지만) 팀원들이 전부 MVVM 패턴을 적용하고 이해하고 있으면 개발 커뮤니케이션이 수월해진다!


4. 그리고 …

  • 각각의 부분이 독립적 -> 유닛 테스트 용이
  • 모듈화가 잘 되어 있다면 내장 DB를 통채로 변경하는게 비교적 쉬움 ex) realm -> room
  • ViewModelView가 1:n 연결이 가능 -> 뷰모델 재활용 가능
  • View가 데이터를 실시간으로 관찰하고 자동으로 UI 갱신


자세히 알아보자!

구조

Model

  • 어플리케이션에서 사용하는 데이터와 그 데이터를 처리하는 부분

View

  • 액티비티/프래그먼트, 사용자에게 보여지는 UI
  • 사용자와 상호작용
  • ViewModel의 데이터를 관찰하여 UI 갱신

ViewModel

  • ModelView를 이어주는 역할. Model의 데이터를 가공하여 View에게 data stream을 제공


어떻게 구현?

AAC(Android Architecture Components)를 사용하면 비교적 쉽게 구현 가능

AAC

  • 강력하고 테스트와 유지관리가 쉬운 앱을 디자인하도록 돕는 라이브러리 모음


주로 안드로이드 MVVM 패턴에는 AACLiveDataViewModel을 사용한다.


주의사항

1. AAC의 ViewModel != MVVM의 ViewModel

어 뭐야? 둘이 같은거 아닌가요??? -> 아뇨 … 이름만 같아요 … ㅋㅋㅋ

AAC ViewModel

  • ViewModel 클래스는 수명 주기를 고려하여 UI 관련 데이터를 저장하고 관리하도록 설계되었습니다. ViewModel 클래스를 사용하면 화면 회전과 같이 구성을 변경할 때도 데이터를 유지할 수 있습니다. - 공식 문서

MVVM ViewModel

  • ModelView를 이어주는 역할. Model의 데이터를 가공하여 View에게 data stream을 제공


그러면 어떻게 AAC ViewModelMVVM ViewModel처럼 사용하느냐…! 단순히 AAC ViewModel이 가지고 있는 데이터를 관찰가능하게 해주고 View는 데이터바인딩으로 그것을 구독하고 있으면 된다! 이렇게 하면 데이터를 수명 주기를 고려하여 관리할 수 있으므로 더 좋다!


하지만 단점도 있다.
AAC ViewModel액티비티에 단 하나만 존재할 수 있다. 예를 들어 MainViewModel을 한번 생성하면 그 액티비티에서 MainViewModel을 여러번 생성해도 하나의 객체만 사용된다. MVVM 패턴ViewViewModel이 1:n 관계인데, AAC ViewModel은 이를 지킬 수 없다. 또한 구글은 ViewAAC ViewModel을 1:1 관계로 사용하고 ViewModel안에 여러 ModelLiveData를 사용하도록 권장한다. 이 역시 MVVM 원칙을 위반한다.


2. LiveData의 한계

우선 LiveData가 뭔지 알아보자.
LiveDataLiveCycle을 알고 있는 DataType이라 생각하면 된다. LiveData는 Observer 객체를 이용하여 데이터의 변화를 관찰하며 UI를 업데이트 합니다. 어라? MVVM에서 View의 역할은 ViewModel의 데이터를 관찰하여 UI를 갱신하는건데 … 아! ViewModel의 데이터를 LiveData로 만들면 되겠네!


LiveData vs MutableLiveData

LiveDataread-only이고 MutableLiveDataLiveData를 수정할 수 있다.
ViewModel를 캡슐화 하기위해 외부로 노출되는 값인 read-only LiveDatapublic으로 내부에서는 데이터를 편집할 수 있는 MutableLiveDataprivate로 만들자!

캡슐화를 하는 이유
데이터 내용을 변경할 수 있는 기능을 외부로 노출하면 디버그하기 어렵고 데이터의 내용이 어디서 왔는지 추론하기 어려워지기 때문 다른 이유는 링크 참고


그래서 LiveData의 한계가 뭔데?
바로 비동기 데이터 스트림을 지원하지 않는다는 것! 이유는 LiveData는 UI와 밀접하게 연관되어 있기 때문에 오직 Main Thread에서만 관찰되기 때문이다. 따라서 데이터베이스(Local)과 서버(Remote)와의 통신이 이루어지는 Repository에서 LiveData를 사용할 수 없다.

클린 아키텍처의 Domain Layer에서도 LiveData를 사용할 수 없다. Domain Layer는 순수 Java 및 Kotlin 코드로만 구성하는데 LiveData는 안드로이드 플랫폼에 종속적이기 때문

이러한 이유로 LiveData는 사용하지 않는게 좋다! (Repositroy는 해당 포스트에서 클린 아키텍처는 나중 포스트에서 다룰 예정)


Flow

하지만~!! 비동기 데이터 스트림을 지원하는 Kotlin 클래스 Flow가 존재한다. 이를 통해 ViewModel에서는 LiveData를 사용하고 DB, 서버와 통신을 하는 Repository같은 곳에서는 Flow를 사용하면 된다. 하지만 … 이런 방법을 사용해도 FlowLiveData를 완전히 대체할 수 없다.

  • Flow는 스스로 안드로이드 생명주기에 대해 알 수 없다.
  • Flow는 상태가 없어 값이 할당된 것인지, 현재 값은 무엇인지 알기가 어렵다.
  • FlowCold Stream 방식으로, 연속해서 계속 들어오는 데이터를 처리할 수 없으며 collect 되었을 때만 생성되고 값을 반환한다. 만약, 하나의 flow builder 에 대해 다수의 collector가 있다면 collector 하나마다 하나씩 데이터를 호출하기 때문에 업스트림 로직이 비싼 비용을 요구하는 DB 접근이나 서버 통신 등이라면 여러 번 리소스 요청을 하게 될 수 있다.


StateFlow, SharedFlow

위의 단점을 보완하기 위해 StateFlow, SharedFlow가 나왔다. (SharedFlowStateFlow의 일반적인 버전이다. 자세한 설명은 다른 포스트에서 하겠다.)


StateFlow 특징

  • StateFlow는 항상 값을 가지고 있고, 오직 한 가지 값을 가진다.
  • StateFlow는 여러 개의 collector를 지원한다. 이는 flow가 공유된다는 의미이며 앞서 설명했던 flow의 단점과는 다르게 업스트림이 collector 마다 중복으로 처리되지 않는다.
  • StateFlowcollector 수에 관계없이 항상 구독하고 있는 것의 최신 값을 받는다.
  • StateFlowflow builder 를 사용하여 빌드된 flowcold stream 이었던 것과 달리, hot stream이다. 따라서 collector 에서 수집하더라도 생산자 코드가 트리거 되지 않고, 일반 flow는 마지막 값의 개념이 없었던 것과 달리 StateFlow 는 마지막 값의 개념이 있으며 생성하자마자 활성화된다.


LiveDataStateFlow로 완전히 대체 가능하다.


Repository 패턴

아! 이제 MVVM을 사용하는 이유, 기본 개념, AAC ViewModel과 MVVM ViewModel의 차이점, LiveData대신 StateFlow를 사용해라! 까지 알았으니 이제 실습만 하면 되겠군요? ㅋㅋ


아뇨 Repositroy 패턴까지만 … 보고 가시죠!

repositroy


Repositroy
데이터 출처(로컬 DB인지 API응답인지 등)와 관계 없이 동일 인터페이스로 데이터에 접속할 수 있도록 만드는 것을 Repository 패턴이라고 한다. 레포지토리는 데이터 소스에 액세스하는 데 필요한 논리를 캡슐화하는 클래스 또는 구성 요소이다. Repository 의 존재 덕분에 ViewModel 이 데이터를 관리할 필요가 없게 된다.


마무리

이렇게 Repository에 대한 간단한 설명을 마지막으로 포스팅을 끝내려한다. 이제 남은 일은 Github에서 MVVM 예제 소스를 찾아서 공부하는 것 뿐 … (클린 아키텍처도 공부 …)


레퍼런스

https://velog.io/@haero_kim/Android-%EA%B9%94%EC%8C%88%ED%95%98%EA%B2%8C-MVVM-%ED%8C%A8%ED%84%B4%EA%B3%BC-AAC-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0
https://devvkkid.tistory.com/196
https://readystory.tistory.com/207
https://yjyoon-dev.github.io/android/2022/02/12/android-02/
https://velog.io/@k7120792/Model-View-ViewModel-Pattern
https://leveloper.tistory.com/216 https://blog.yena.io/studynote/2019/03/16/Android-MVVM-AAC-1.html

Categories:

Updated:

Leave a comment