Views and ViewModels
Distributing responsibilities
-
이상적으로 ViewModel 은 Android 에 대한 어떤 것도 알아서는 안 된다.
이는 testability 를 늘려주고, 더 안전하며, module 화하기 편해진다.
이를 가장 쉽게 확인할 수 있는 방법은 android.arch.* 외에는 android.* import 가 없어야 한다.
-
조건문, loop, 동작 결정 등은 Activity, Fragment 가 아닌 ViewModel 이나 다른 layer 에서 수행해야한다.
View 는 어떻게 data 를 display 할지만 신경쓰고, user event 를 ViewModel 이나 Presenter 로 전달하는 역할만 해야 한다.
이것이 Passive View patten 이다.
View references in ViewModels
-
ViewModel 들은 activity 나 fragment 와 다른 scope 을 보인다.
ViewModel 이 살아있고 동작하는 동안, activity 는 어떤 lifecycle state 든 들어갈 수 있다.
ViewModel 이 인지하지 못하는 동안 Activity 나 Fragment 가 destroy 되고 다시 create 될 수 있다.
-
View 에 대한 참조를 ViewModel 로 전달하는 것은 아주 위험하다.
ViewModel 이 network 에 데이터를 요청하고, 그 결과가 나중에 온다고 해보자.
그럼 더 이상 valid 하지 않은 View 에 대한 참조가 이루어져서 memory leak 또는 crash 를 야기할 수 있다.
-
추천되는 ViewModel 과 View 간의 컴 방법은 observer pattern 이다.
LiveData 나 observable lib 을 사용하는 것이다.
Observer Pattern
-
View 가 ViewModel 을 observe 하는 것이다.
ViewModel 은 android 에 대해 모르기 때문에, android 가 view 를 얼마나 자주 죽이는지 이런것은 알지 못한다.
이는 config change 에 대해 ViewModel 은 영향을 받지 않으며, 따라서 rotation change 등이 발생했을 때 requery 같은 것을 안 해도 된다는 장점을 가질 수 있다.
앞서 설명한 memory leak 을 방지할 수 있고, 존재하지 않는 view 에 대한 NPE 도 피하기 좋다.
Fat ViewModels
-
ViewModel 이 너무 많은 code 나 책임을 가지고 있다면 다음을 고려해보라.
몇몇 로직을 presenter 로 이관해보자.
Clean Architecture 의 Domain layer 를 추가해보자. 이를 통해 testability 와 architecture 유지보수력이 좋아진다. main thread 에 대한 점유도 빨리 놓아버리는 장점도 생긴다.
Using a data repository
-
대부분이 다음과 같은 data source 를 가지고 있다.
1. Remote; network 나 cloud
2. Local : db 나 file
3. In-memory cache
-
presentation layer 에서 알지 못하는 data layer 를 갖는 것은 좋은 방법이다.
cache 를 쓰는 것, db 와 sync 맞추는 것, network query 하는 것은 쉽지 않은 문제이다.
이들은 별도의 repository class 로 관리하고 entry point 를 하나로 관리하는 것은 복잡함을 관리하는 데 좋다.
Dealing with data state
-
ViewModel 을 통해 노출된 List item 을 가진 LiveData를 observe 한다고 해보자.
이 때 View 는 어떻게 data loading 중, network error, 실제 empty 인 list 등의 상태를 구분할 수 있을까?
이는 LiveData<DataState> 라는 것을 따로 관리함으로써 할 수 있다.
DataState 변화에 따라 observe callback 이 불릴거고, 그 값을 기준으로 분기하는 로직이 있으면 되겠다.
Saving activity state
-
LMK 등의 상황에서는 ViewModel 도 제거되는데 UI state 를 저장하고 복구하기 위해서 onSaveIntanceState 와 ViewModel 모두를 사용할 수 있다.
관련된 내용은 다음 링크를 참고하자
Events
-
activity 가 rotate 되면서 recreate 되고, 새롭게 observe 를 시작하면, LiveData 는 old value 를 바로 전달한다.
이것이 문제를 야기할 수 있는데, 이를 다른 lib 이나 architecture component extension 으로 풀기보다는, design 문제로 여겨야 한다.
event 는 state 의 일부로 취급해야 한다.
이에 대한 자세한 정보는 다음을 참고하자
Leaking ViewModels
-
reactive 패러다임은 안드로이드에서 잘 작동한다.
LiveData 는 이의 key component 이며, activity 와 fragment 가 이를 observe 한다.
이상적으로 ViewModel 은 observe 하는 view 가 없다면 함께 사라지는 것이 맞다.
-
ViewModel.onCleared() 가 불리면 repository 에게 ViewModel 에 대한 callback 을 버리라고 하면 된다.
Repository 에서는 WeakReference 나 EventBus 를 사용하는 방법이 좋다.
LiveData 를 사용해서 Repository 와 통신하는 것이 좋다. View 에도 LiveData 를 넘겨주는 것이 좋다.
LiveData in repositories
-
ViewModel 의 leak 과 callback hell 을 피하기 위해서, repository 가 observe 될수도 있다.
ViewModel 에서 repository 를 LifecycleOwner 없이 어떻게 구독할 수 있을까?
Transformation 을 사용하면 쉽다.
Transformations.switchMap 은 다른 LiveData 객체들의 변화에 반응하는 새로운 LiveData 를 쉽게 만들 수 있게 해준다.
그리고 이것은 Observer 의 Lifecycle 정보를 chain 을 따라 전파하는 데도 좋다.
Extending LiveData
-
LiveData 를 쓰는 가장 일반적인 방법은 ViewModel 안에서 MutableLiveData 를 사용하는 것이다.
그리고 그를 LiveData 의 형태로 노출하여 observer 에서는 immutable 로 인식하게 하는 것이다.
When not to extend LiveData
-
onActive() 를 사용하여 data 를 load 하는 service 를 시작시킬 수 있다.
그러나 적합한 이유 없이는 LiveData 가 observe 되기까지 기다릴 필요가 없다.
일반적인 패턴은 ViewModel 에 start() 함수를 추가해서 가능한 빨리 부르는 것이다.
또는 load 를 시작시키는 property 를 set 시킬 수도 있다.
Summary
-
Good Design 추구하기
activity, fragment 에 logic 을 최소화하자.
UI 에 data 를 밀어넣기보다는, UI 가 변화를 observe 하도록 하라.
책임을 분산하고 필요하다면 domain layer 를 추가하라.
single-point entry 를 가진 data repository 를 추가하자.
data 의 상태에 대한 정보를 wrapper 나 LiveData 를 사용하여 노출하자.
event 를 상태의 일부로 디자인하자.
leak 과 얼마나 operation 이 오래걸리는지 등의 edge case 를 고려하여 architecture 를 수정하자.
ViewModel 안에서 Lifecycle object 가 필요하다고 생각된다면, Transformation 이 해결책이 될 수 있다.
-
Bad Design 피하기
ViewModel 이나 Presenter 들이 Android framework 에 대해 알게 하지 말라
ViewModel 이 View 를 ref 하도록 하지 말라.
ViewModel 에 상태나 관련 데이터 저장에 대해 중요한 로직을 넣지 말아라. ViewModel 을 통한 call 이 마지막 Call 이 될 수 있다는 것을 기억하라. (돼왕: 정확히 이해가 안 되어 원문을 추가한다. Don't put logic in the ViewModel that is critical to saving clean state or related to data. Any call you make from a ViewModel can be the last one.)
LiveData 를 상속할 일은 왠만해서는 없다. activity 나 fragment 가 ViewModel 에 data loading 할 시간이라고 알리게 하라.
-
참고 : https://medium.com/androiddevelopers/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54
끝
'프로그래밍 놀이터 > 안드로이드, Java' 카테고리의 다른 글
[android] finishAffinity() 와 finishAndRemoveTask() 에 대하여 with 실험 (0) | 2021.01.27 |
---|---|
[android] context 마스터 하기! (0) | 2021.01.26 |
[android] ViewModel 에 대해 알아보자 (0) | 2021.01.21 |
[android] LiveData 에 대해 알아볼까 (0) | 2021.01.20 |
#5 모바일 앱 보안 강화 - 안드로이드 모바일 앱 모의해킹 (0) | 2020.11.23 |
댓글