[Android] AAC ViewModel 분석해보기

강승구·2023년 7월 18일
0

ViewModel의 목적

안드로이드는 리소스에 대한 제약이 많은 OS이다.
그렇기 때문에 View가 Destroy 되어도 유지되어야 하는 데이터가 있다면 UI Controller (Activity, Fragment)의 Life cycle에 따라 데이터가 파괴되기 전에 OnSavedInstanceState 등의 메소드를 오버라이드 받아 데이터를 Bundle에 저장한뒤 다시 onRestoreInstanceState를 이용해 복구해야한다.

class MainActivity : AppCompatActivity() {
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState) 
        setContentView(R.layout.activity_main)
    }
}

Activity가 Destory되기 전 저장하고 싶은 데이터를 Bundle의 형태로 onCreate에 넘겨주면 데이터를 날리지 않고 계속 이용할 수 있는 것이다.

하지만 이러한 방법에는 다음과 같은 문제점이 있다.

  • 담을 수 있는 데이터의 크기가 한정적이다. (공식 문서에서는 50k 미만의 데이터를 담을 것을 권장하고 있다.)
  • 담을 수 있는 데이터의 형태가 제한된다.
  • onCreate에서 작업을 처리해야 하므로 UI 컨트롤러가 해야 할 일이 늘어나게 되고 화면을 띄우는데 시간이 오래 걸리게 된다.

Activity나 Fragment와 같은 UI 컨트롤러에서 데이터를 관리하면 생명 주기에 따라서 값이 사라지는 문제가 발생하고 이 문제를 해결하기 위해 saveInstanceState로 데이터를 저장하려면 위와 같은 문제가 존재한다.

따라서 이러한 문제를 해결하기 위해 나온것이 바로 ViewModel이다.

MVVM ViewModel vs AAC ViewModel

  • MVVM에서 말하는 ViewModel은 UI를 비즈니스 로직으로부터 분리하여 개발하는 아키텍처 패턴에 사용된다. 여기서 ViewModel은 UI와 비즈니스 로직 간의 중간 매개체로 동작한다.

ViewModel

ViewModel은 데이터가 Activity에서는 Activity가 완전히 종료될 때까지, Fragment에서는 Fragment가 분리될 때까지 메모리에 남아있도록 설계 되어있다.

UI Controller의 finish() 호출등에 의해 생명주기가 종료됨에 따라 내부의 LifecycleEventObserver를 통해 ViewModel도 onCleared() 콜백 메서드를 호출하고 종료된다.


ViewModel 요청 프로세스

  1. ViewModelProvider를 통해 ViewModel 인스턴스를 요청한다.
  2. ViewModelProvider 내부에서는 ViewModelStoreOwner를 참조하여 ViewModelStore를 가져온다.
  3. ViewModelStore에게 이미 생성된(저장된) ViewModel 인스턴스를 요청한다.
  4. 만약 ViewModelStore가 적합한 ViewModel 인스턴스를 가지고 있지 않다면, Factory를 통해 ViewModel인스턴스를 생성한다.
  5. 생성한 ViewModel 인스턴스를 ViewModeStore에 저장하고 만들어진 ViewModel 인스턴스를 클라이언트에게 반환한다.
  6. 똑같은 ViewModel 인스턴스 요청이 들어온다면, 1~3번의 과정을 반복하게 된다.

ViewModel의 구성요소

1. ViewModelProvider

ViewModelProviderViewModel 인스턴스를 생성하기 위해 사용하는 클래스이다.
ViewModelProvider는 생성자 매개변수로 ViewModelStoreOwner를 필요로 한다.

액티비티나 프래그먼트 내에서 ViewModelProvider 객체를 통해 viewmodel 인스턴스를 얻을 수 있다.

class MainActivity : AppCompatActivity(), View.OnClickListener {
      myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
      ...
}

위 코드에서 this는 LifecycleOwner로서, 해당 생명주기에 맞추어 viewmodel이 관리된다.


2. ViewModelStoreOwner

ViewModelStoreOwner는 인터페이스의 형태로 ViewModelStore를 제공하는 역할을 하고 있다. (ViewModelStore는 아래에서 자세히 설명하겠지만, 간단하게는 ViewModel의 인스턴스를 관리하는 클래스라고 할 수 있다.)

ViewModelStoreOwner가 구현된 코드를 살펴보면 ViewModelStore 객체를 반환하는 함수를 정의하고 있는 것을 확인할 수 있다.

ViewModelProvider는 ViewModelStoreOwner를 생성자 매개변수로 받아 ViewModelStore를 얻고, 이를 사용해 ViewModel 인스턴스를 저장하고 관리하는 것이다.

그럼 ViewModelProvider는 생성자의 매개변수로 ViewModelStoreOwner를 전달받는다고 했는데 위에서 ViewModel 인스턴스를 생성하는 예제에서는 this를 넘겨주고 있었다. 그렇다면 ViewModelStoreOwner와 Activity, Fragment는 무슨 관계일까?

이에 대한 해답은 Activity의 부모 클래스인 ComponentActivity(AppCompatActivity)에서 찾을 수 있다.

ComponentActivity의 내부 코드를 살펴보면 ViewModelStoreOwner를 구현하고 있다는 것을 알 수 있다.
Fragment 역시 부모 클래스에서 ViewModelStoreOwner를 구현하고 있다.

즉, Activity와 Fragment가 ViewModelStoreOwner를 구현하기 때문에 ViewModel 객체를 생성할 때 어떤 Owner를 전달하느냐에 따라 ViewModel의 Scope이 정해진다.


3. ViewModelStore

공식문서에 따르면 ViewModelStore의 역할은 다음과 같이 정리되어있다.

ViewModelStore는 ViewModel 인스턴스를 저장하기 위한 클래스이다. AAC 관점에서의 ViewModel의 근본적인 역할은 Configuration Change가 발생해도 데이터를 유지하고 파괴되지 않도록 보장하는 것이다. 즉, ViewModel 인스턴스를 ViewModelStore에 저장해두고 Configuration Change가 발생해 UI Controller가 재생성되는 시점에, ViewModelStore에 저장된 ViewModel 인스턴스를 다시 가져와 사용할 수 있도록 한다.

ViewModelStore 클래스가 구현된 코드를 살펴보면 HashMap<String, ViewModel>의 형태로 ViewModel을 관리하고 있다. 그리고 get함수에 key값을 전달해 mMap의 value (ViewModel 인스턴스)를 반환하고 있다.

그렇다면 ViewModel의 인스턴스는 왜 Map의 구조를 가지고 있을까?

ViewModelProviderOwner(Activity나 Fragment)를 기반으로 ViewModel을 생성하고 관리한다. 여기서 중요한 점은, 서로 다른 Owner(예: 다른 Activity나 Fragment)에서 동일한 ViewModel 클래스를 사용하더라도, 다른 ViewModel 인스턴스를 생성하게 된다.

val viewModel1 = ViewModelProvider(activity1).get(MyViewModel::class.java)
val viewModel2 = ViewModelProvider(activity2).get(MyViewModel::class.java)

위 코드를 보면, activity1과 activity2는 동일한 MyViewModel 클래스를 사용하지만, 서로 다른 Owner에 의해 생성된 ViewModel은 별개의 메모리 공간을 사용하기 때문에 다른 객체가 된다.
즉, ViewModel의 인스턴스는 Owner별로 독립적이며, 다른 메모리 공간을 사용하여 서로 다른 객체로 존재하는 다른 인스턴스라는 것이다.

정리하자면, Owner별로 고유한 값(Key)를 사용해 ViewModel 인스턴스를 관리하기 위해서 ViewModelStore에서는 ViewModel을 Map의 구조로 관리하고 있는 것이다.


4. ViewModelFactory

마지막으로 ViewModelFactory이다.
ViewModel은 기본적으로 파라미터가 없는 생성자를 사용해 인스턴스화된다. 하지만 ViewModel 객체를 생성할 때 특정한 매개변수가 필요한 경우가 있다. 이때

ViewModelFactory는 ViewModel을 만들기 위한 Factory(공장) 역할을 합니다. 기본 ViewModel 생성자에서는 인자를 받을 수 없으므로, ViewModelFactory를 통해 원하는 인자를 전달하고 해당 인자를 사용하여 ViewModel을 생성할 수 있습니다.

ViewModelFactory를 만들려면 ViewModelProvider.Factory 인터페이스를 구현해야 한다.

profile
강승구

0개의 댓글

관련 채용 정보