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
객체가 리턴된다.
ViewModelStore
에서ViewModel
객체를 검색한다.- 기존 객체가 있다면 이를 바로 리턴한다.
- 기존 객체가 없다면
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 |