Android

ViewModel 맛보기

까망사과 2023. 2. 27. 03:00

ViewModel 클래스는 UI 관련 데이터 및 비즈니스 로직을 포함하는 아키텍처 컴포넌트다.

 

👍 ViewModel의 이점

앱을 사용하다가 화면 회전 같은 구성 변경이 발생하면 UI 컨트롤러(액티비티 및 프래그먼트)가 소멸한 뒤 바로 재생성된다.

이 과정에서 UI에 표시되어 있던 데이터가 손실될 수 있으므로 이를 보존하는 작업이 필요하다.

액티비티의 onSaveInstanceState, onRestoreInstanceState 콜백 메서드에서 Bundle을 사용하여 데이터를 백업 및 복원할 수 있지만, 이 방법은 추가적인 코드가 필요하며 적은 용량의 데이터만 보관할 수 있다는 단점이 있다.

ViewModel은 구성 변경이 발생해도 소멸하지 않으며 추가적인 코드 없이 백업 및 복원이 용이하므로 UI 데이터를 보존하기 적합하다.

 

🌱 ViewModel의 범위

ViewModel의 범위(scope)는 ViewModelStoreOwner 인터페이스의 서브클래스의 수명주기를 따른다. ComponentActivity, Fragment, NavBackStackEntry는 모두 ViewModelStoreOwner의 서브클래스다. ViewModelStoreOwner가 아래와 같은 경우로 완전히 사라질 때까지 ViewModel은 메모리에 남는다.

  • 액티비티(ComponentActivity)가 소멸(destroy)한다.
  • 프래그먼트(Fragment)가 분리(detach)된다.
  • 내비게이션 항목(NavBackStackEntry)이 백 스택에서 삭제된다.

ViewModel이 소멸할 때 onCleared 콜백이 호출된다.

 

📝 ViewModel에서 해야 할 것들

  • UI 데이터 포함하기
    UI를 표시하는 데 필요한 데이터를 포함한다.
    보통 LiveData 및 데이터 바인딩을 함께 사용한다.
  • 비즈니스 로직 포함하기
    UI 관련 데이터를 가공하거나 사용자 이벤트를 처리하는 비즈니스 로직을 ViewModel에 배치하면 UI 컨트롤러가 UI를 표시하는 데만 집중하게 만들 수 있다.
  • UI 참조하지 않기
    액티비티 및 프래그먼트, View 등 UI에 관련된 요소는 구성 변경의 영향을 받기 때문에 ViewModel에서 참조하면 안 된다.

 

🏭 ViewModel 객체 가져오기

ViewModelProvider에서 가져오기

ViewModel 객체는 ViewModelProvider에서 제공하며 이가 생성자 인자로 받는 항목은 다음과 같다.

  • ViewModelStore
    내부의 MutableMap을 사용하여 ViewModel 객체를 저장한다.
  • ViewModelStoreOwner
    ViewModelStore를 포함하며 ViewModel의 범위를 결정한다.
  • Factory
    ViewModel 객체를 새로 생성한다.

ViewModelProvider 객체의 get 메서드를 호출하면 다음 과정을 거쳐 ViewModel 객체가 리턴된다.

  1. ViewModelStore에서 ViewModel 객체를 검색한다.
  2. 기존 객체가 있다면 이를 바로 리턴한다.
  3. 기존 객체가 없다면 Factory에서 ViewModel 객체를 새로 생성하여 리턴한다.
val provider = ViewModelProvider(this) // this: ViewModelStoreOwner
val viewModel = provider.get(ExampleViewModel::class.java)

 

Kotlin 확장 함수로 가져오기

Kotlin을 사용한다면 viewModels 확장 함수를 사용하여 범위가 지정된 ViewModel 객체를 간단하게 가져올 수 있다.

ComponentActivity, Fragment에서 확장 함수를 호출할 수 있으며 모두 내부적으로 ViewModelProvider를 사용한다.

ViewModel 객체의 범위에 따라 사용하는 방법이 조금씩 다르다.

가장 가까운 범위 지정하기

ViewModelStoreOwner의 수명주기를 ViewModel의 범위로 지정한다.

// androidx.activity-ktx
import androidx.activity.viewModels

class ExampleActivity : AppCompatActivity() {
    // 현재 액티비티의 수명주기가 ViewModel의 범위가 된다.
    val viewModel by viewModels()
}

// androidx.fragment-ktx
import androidx.fragment.app.viewModels

class ExampleFragment : Fragment() {
    // 현재 프래그먼트의 수명주기가 ViewModel의 범위가 된다.
    val viewModel by viewModels()
}

 

액티비티로 범위 지정하기

프래그먼트의 경우 ViewModelStoreOwner를 반환하는 함수형 파라미터를 전달하면 ViewModel의 범위를 따로 지정할 수 있다.

프래그먼트가 포함된 액티비티의 수명주기를 범위로 지정하고 싶다면 activityViewModels 확장 함수를 대신 사용할 수 있다.

// androidx.fragment-ktx
import androidx.fragment.app.viewModels
import androidx.fragment.app.activityViewModels

class ExampleFragment : Fragment() {
    // 상위 프래그먼트의 수명주기가 ViewModel의 범위가 된다.
    val viewModel by viewModels(
        ownerProducer = { requireParentFragment() }
    )

    // 현재 프래그먼트가 포함된 액티비티의 수명주기가 ViewModel의 범위가 된다.
    val activityViewModel by activityViewModels()
}

'Android' 카테고리의 다른 글

LiveData 맛보기  (0) 2023.02.28
Retrofit2로 JSON 데이터 요청하기  (0) 2023.02.21
WorkManager (4) : 작업 체이닝  (0) 2022.12.10
WorkManager (3) : 작업 관리하기  (0) 2022.12.05
WorkManager (2) : 작업 상태  (0) 2022.12.02