안드로이드의 ViewModel과 LiveData를 사용하는 인터넷의 거의 아니 모든 예제가 Activity나 Fragment에서 ViewModel에 있는 LiveData 로부터 변경사항을 전달 받기위해 위해 다음과 같은 코드를 사용한다.


// Fragment

viewModel.liveData.observe(viewLifeCycleOwner, Observe { uiState ->

     // UI update

})


//ViewModel

private val _liveData = MutableLiveData<UiState>()

val liveData: LiveData<UiState>

get() = _liveData



ViewModel에서 LiveData를 노출시킬 때 캡슐화를 위해 MutableLiveData인 private member를 선언하고 외부에서 값을 세팅하는 것을 방지하기 위해 LiveData인 public member를 외부에 노출시킨다.  캡슐화는 된 것처럼 보이나 굳이 View에 LiveData를 사용한다는 구현의 디테일을 노출시킬 필요까지는 없다. 예를 들어 추후에 LiveData의 구현을 Coroutines의 StateFlow를 사용하여 바꾸었을 경우, 원하지 않는 View쪽의 코드변경이 요구된다. 만약 프로젝트가 큰 규모이고 앱 전체에 대해 마이그레이션을 해야한다면, 상당히 부담스러운 변경이 될 것이다. 따라서 ViewModel은 View에게 구현디테일을 감출 필요가 있다.


간단한 리팩토링을 통해 코드를 변경해 보자.


//ViewModel

private val liveData = MutableLiveData<UiState>()


fun observe(owner: LifeCycleOwner, onChange: (UiState) -> Unit) {

    liveData.observe(owner, Observer(onChange))

}


// Fragment

viewModel.observe(viewLifeCycleOwner) { newChange ->

    

}


ViewModel에서는 더이상 public member를 노출하지 않고 있으면 _liveData 와 같이 별로 맘에 들지않는 생긴 변수명을 쓰지 않게 되었다. 이 변수명을 코틀린 명명규칙에 어긋나지는 않지는, 가독성에 방해만될 뿐더러 별로 모양새는 좋지않다.

View쪽의 코드는 오히려 약간 더 짧아졌다, ViewModel에서 어떻게 observe를 구현하지는 전혀 신경쓸 필요가 없게 되었다. 또한 추후에 ViewModel에서 구현 상에 변화가 생기더라도 같은 observe 패턴을 사용하는 한 코드를 변경하지 않아도 되게 되었다.  


상황에 따라서는 더 나아가 LiveData의 observe와 postValue를 전담하는 전용클래스를 만들어 사용할 수 도 있을 것이고, BaseViewModel을 두어 LiveData를 observe하는 코드를 위치시키고, 모든 ViewModel이 BaseViewModel을 상속받는다면, 코드 중복을 줄일 수도 있을 것이다.


interface  LiveDataHolder<T> {

    fun observe(owner: LifeCycleOwner, onChange: (T) -> Unit)

    fun emitState(T)

}


abstract class BaseViewModel<T> : ViewModel()  {

   abstract val liveDataHolder:  LiveDataHolder<T>


  fun observe(owner: LifeCycleOwner, onChange: (T) -> Unit) {

      liveDataHolder.observe(owner,  onChange)

}


이런 패턴은 ViewModel의 크기가 커져셔 여러 개의 서브 클래스로 쪼갤 때 도움이 된다.