[Android] MVVM 패턴 박살내기
MVVM 패턴이란?
코드를 Model, View, ViewModel의 역할에 맞게 분리하서 작성하는 디자인 패턴
왜 MVVM을 사용할까?
1. 사용안하면 뭐 어쩔건데?
MVVM 패턴
을 사용하지 않고 액티비티/프래그먼트
에 모든 코드를 넣게 되면 어떻게 될까요?
액티비티/프래그먼트
코드가 매우 복잡해짐- 스파게티 코드가 될 확률이 높아짐
- 유지보수 & 테스트가 어려워짐
- 다른 사람이 코드를 이해하기 어려워짐 (본인 포함)
이런 단점이 있기 때문에 코드를 분리해서 작성하는 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
ViewModel
과View
가 1:n 연결이 가능 -> 뷰모델 재활용 가능View
가 데이터를 실시간으로 관찰하고 자동으로 UI 갱신
자세히 알아보자!
구조
Model
- 어플리케이션에서 사용하는 데이터와 그 데이터를 처리하는 부분
View
- 액티비티/프래그먼트, 사용자에게 보여지는 UI
- 사용자와 상호작용
ViewModel
의 데이터를 관찰하여 UI 갱신
ViewModel
Model
과View
를 이어주는 역할.Model
의 데이터를 가공하여View
에게data stream
을 제공
어떻게 구현?
AAC(Android Architecture Components)를 사용하면 비교적 쉽게 구현 가능
AAC
- 강력하고 테스트와 유지관리가 쉬운 앱을 디자인하도록 돕는 라이브러리 모음
주로 안드로이드 MVVM 패턴
에는 AAC
의 LiveData
와 ViewModel
을 사용한다.
주의사항
1. AAC의 ViewModel != MVVM의 ViewModel
어 뭐야? 둘이 같은거 아닌가요??? -> 아뇨 … 이름만 같아요 … ㅋㅋㅋ
AAC ViewModel
ViewModel
클래스는 수명 주기를 고려하여 UI 관련 데이터를 저장하고 관리하도록 설계되었습니다.ViewModel
클래스를 사용하면 화면 회전과 같이 구성을 변경할 때도 데이터를 유지할 수 있습니다. - 공식 문서
MVVM ViewModel
Model
과View
를 이어주는 역할.Model
의 데이터를 가공하여View
에게data stream
을 제공
그러면 어떻게 AAC ViewModel
을 MVVM ViewModel
처럼 사용하느냐…! 단순히 AAC ViewModel이 가지고 있는 데이터를 관찰가능하게 해주고 View는 데이터바인딩으로 그것을 구독하고 있으면 된다! 이렇게 하면 데이터를 수명 주기를 고려하여 관리할 수 있으므로 더 좋다!
하지만 단점도 있다.
AAC ViewModel
은 액티비티
에 단 하나만 존재할 수 있다. 예를 들어 MainViewModel
을 한번 생성하면 그 액티비티
에서 MainViewModel
을 여러번 생성해도 하나의 객체만 사용된다. MVVM 패턴
은 View
와 ViewModel
이 1:n 관계인데, AAC ViewModel
은 이를 지킬 수 없다. 또한 구글은 View
와 AAC ViewModel
을 1:1 관계로 사용하고 ViewModel
안에 여러 Model
과 LiveData
를 사용하도록 권장한다. 이 역시 MVVM
원칙을 위반한다.
2. LiveData의 한계
우선 LiveData
가 뭔지 알아보자.
LiveData
란 LiveCycle
을 알고 있는 DataType
이라 생각하면 된다. LiveData는 Observer 객체를 이용하여 데이터의 변화를 관찰하며 UI를 업데이트 합니다. 어라? MVVM
에서 View
의 역할은 ViewModel의 데이터를 관찰하여 UI를 갱신하는건데 … 아! ViewModel
의 데이터를 LiveData
로 만들면 되겠네!
LiveData vs MutableLiveData
LiveData
는 read-only
이고 MutableLiveData
는 LiveData
를 수정할 수 있다.
ViewModel
를 캡슐화 하기위해 외부로 노출되는 값인 read-only
LiveData
를 public
으로 내부에서는 데이터를 편집할 수 있는 MutableLiveData
를 private
로 만들자!
캡슐화를 하는 이유
데이터 내용을 변경할 수 있는 기능을 외부로 노출하면 디버그하기 어렵고 데이터의 내용이 어디서 왔는지 추론하기 어려워지기 때문 다른 이유는 링크 참고
그래서 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
를 사용하면 된다. 하지만 … 이런 방법을 사용해도 Flow
는 LiveData
를 완전히 대체할 수 없다.
Flow
는 스스로 안드로이드 생명주기에 대해 알 수 없다.Flow
는 상태가 없어 값이 할당된 것인지, 현재 값은 무엇인지 알기가 어렵다.Flow
는Cold Stream
방식으로, 연속해서 계속 들어오는 데이터를 처리할 수 없으며collect
되었을 때만 생성되고 값을 반환한다. 만약, 하나의flow builder
에 대해 다수의collector
가 있다면collector
하나마다 하나씩 데이터를 호출하기 때문에 업스트림 로직이 비싼 비용을 요구하는 DB 접근이나 서버 통신 등이라면 여러 번 리소스 요청을 하게 될 수 있다.
StateFlow, SharedFlow
위의 단점을 보완하기 위해 StateFlow
, SharedFlow
가 나왔다. (SharedFlow
는 StateFlow
의 일반적인 버전이다. 자세한 설명은 다른 포스트에서 하겠다.)
StateFlow 특징
StateFlow
는 항상 값을 가지고 있고, 오직 한 가지 값을 가진다.StateFlow
는 여러 개의collector
를 지원한다. 이는flow
가 공유된다는 의미이며 앞서 설명했던flow
의 단점과는 다르게 업스트림이collector
마다 중복으로 처리되지 않는다.StateFlow
는collector
수에 관계없이 항상 구독하고 있는 것의 최신 값을 받는다.StateFlow
는flow builder
를 사용하여 빌드된flow
가cold stream
이었던 것과 달리,hot stream
이다. 따라서collector
에서 수집하더라도 생산자 코드가 트리거 되지 않고, 일반flow
는 마지막 값의 개념이 없었던 것과 달리StateFlow
는 마지막 값의 개념이 있으며 생성하자마자 활성화된다.
LiveData
는 StateFlow
로 완전히 대체 가능하다.
Repository 패턴
아! 이제 MVVM을 사용하는 이유, 기본 개념, AAC ViewModel과 MVVM ViewModel의 차이점, LiveData대신 StateFlow를 사용해라! 까지 알았으니 이제 실습만 하면 되겠군요? ㅋㅋ
아뇨 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
Leave a comment