ViewModel·LiveData·StateFlow — 화면 상태를 살린다
ViewModel은 안드로이드에서 화면 상태와 그 로직을 담는 자리다 — 화면(Activity/Fragment)보다 오래 살아 회전에도 상태를 지킨다. 그 상태를 화면에 흘리는 관찰 가능한 홀더가 LiveData(고전)와 StateFlow(현대)다. 이 장은 안드로이드 위에서 ViewModel·관찰·생명주기 안전 수집을 묶는다. (출처: Android — ViewModel / StateFlow & LiveData.)
ViewModel — 화면보다 오래 사는 상태 홀더
ViewModel은 구성 변경(회전)에도 유지되고, 화면이 완전히 끝날 때(onCleared)에만 소멸한다. 그래서 네트워크로 받은 목록·입력 중인 폼 같은 화면 상태를 여기 두면 회전에 날아가지 않는다.
class MemberViewModel(private val repo: MemberRepository) : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
init { refresh() }
fun refresh() = viewModelScope.launch { // ViewModel 소멸 시 자동 취소
_uiState.value = runCatching { repo.load() }
.fold({ UiState.Success(it) }, { UiState.Error(it.message ?: "") })
}
override fun onCleared() { /* 자원 해제 */ }
}
viewModelScope는 ViewModel 전용 코루틴 스코프라 소멸 시 진행 중이던 코루틴을 자동 취소한다([andkt 07]) — 누수 없는 비동기의 핵심.
ViewModel을 얻는 법
// Compose
val vm: MemberViewModel = viewModel() // 또는 hiltViewModel()
// Fragment/Activity KTX
private val vm: MemberViewModel by viewModels()
생성자에 의존성이 있으면 팩터리나 Hilt(@HiltViewModel)([andarch 05])로 주입한다. *같은 ViewModelStoreOwner(화면)*면 같은 인스턴스를 돌려받아 Fragment끼리 공유도 된다(activityViewModels).
LiveData vs StateFlow
둘 다 관찰 가능한 상태 홀더지만:
| LiveData | StateFlow | |
|---|---|---|
| 출신 | 안드로이드 전용 | 코틀린(Flow) |
| 초기값 | 선택 | 필수 |
| 생명주기 인지 | 내장(observe) | 별도(repeatOnLifecycle/collectAsStateWithLifecycle) |
| 연산자 | 적음 | Flow 연산자 풍부 |
| 권장 | 레거시 | 신규 권장 |
구글은 신규 코드에 StateFlow(+ 생명주기 안전 수집)를 권장한다. 순수 코틀린이라 테스트·멀티플랫폼에 유리하고 Flow 생태계와 맞물린다.
생명주기 안전 수집 — 핵심 함정
StateFlow를 그냥 collect하면 백그라운드에서도 수집해 낭비·크래시가 난다. 화면이 보일 때만(STARTED) 수집하고 안 보이면 멈춰야 한다.
// Compose — 가장 간단 (권장)
val state by viewModel.uiState.collectAsStateWithLifecycle()
// View 시스템 — repeatOnLifecycle
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect { render(it) } // STARTED~STOPPED 구간만 수집
}
}
collectAsStateWithLifecycle()(Compose)와 repeatOnLifecycle(STARTED)(View)가 그 안전장치다 — 예전 LiveData.observe가 공짜로 주던 생명주기 인지를 StateFlow에선 이걸로 챙긴다.
정리 — 상태는 ViewModel에, 수집은 생명주기 안전하게
핵심 — ViewModel이 화면 상태를 보유(회전 생존·viewModelScope로 누수 없는 비동기), 상태는 StateFlow로 노출(신규 권장), 수집은 collectAsStateWithLifecycle/repeatOnLifecycle(STARTED)로 생명주기 안전하게. *상태를 올바른 자리(ViewModel)*에 두고 *올바른 방식(생명주기 안전)*으로 흘리는 것이 안드로이드 UI의 토대다.
한 줄 요약 — ViewModel=회전 생존 상태 홀더(
viewModelScope자동 취소·by viewModels()/hiltViewModel()). 상태 노출은 StateFlow(초기값 필수·신규 권장) vs LiveData(레거시·내장 생명주기). 수집은 *collectAsStateWithLifecycle(Compose)·repeatOnLifecycle(STARTED)(View)*로 안전하게.
(출처: Android — ViewModel / StateFlow / Lifecycle-aware collection / collectAsStateWithLifecycle.)