안드로이드 아키텍처 용어 사전
상태 관리rememberSaveable

SavedStateHandle

ViewModel이 프로세스 종료까지 상태를 넘기게 해 주는 Bundle 기반 저장소.

ViewModel이 프로세스 종료까지 상태를 넘기게 해 주는 Bundle 기반 저장소.

두 종류의 죽음

  • ① 구성 변경 (회전 · 다크모드 전환 · 폰트 크기 변경)

    • Activity 는 죽고 ViewModel 은 산다
  • ② 프로세스 종료 (백그라운드에서 시스템이 메모리를 회수)

    • Activity 도 ViewModel 도 죽는다
    • 사용자는 앱이 죽은 줄 모른다 — 돌아오면 그 화면이 복원돼야 한다
                         구성 변경   프로세스 종료
remember                    ✗            ✗
rememberSaveable            ✓            ✓
ViewModel                   ✓            ✗
ViewModel + SavedStateHandle ✓           ✓

쓰는 법

@HiltViewModel
class SearchViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle,
    private val repository: SearchRepository
) : ViewModel() {

    val query: StateFlow<String> = savedStateHandle.getStateFlow(KEY_QUERY, "")

    val results: StateFlow<List<Item>> = query
        .debounce(300)
        .flatMapLatest { repository.search(it) }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())

    fun onQueryChange(q: String) { savedStateHandle[KEY_QUERY] = q }

    private companion object { const val KEY_QUERY = "query" }
}

네비게이션 인자도 여기로 들어온다

// route = "detail/{itemId}" 로 이동했다면
val itemId: Long = checkNotNull(savedStateHandle["itemId"])

Fragment의 arguments를 ViewModel에서 직접 읽을 필요가 없어진다 — 안드로이드 의존이 하나 줄어든다.

크기 제한이 실재한다

Bundle 은 Binder 트랜잭션으로 전달된다

  • 전체 트랜잭션 버퍼가 약 1MB

  • 넘으면 TransactionTooLargeException 으로 앱이 죽는다

  • 넣어도 되는 것 — 검색어 · 선택된 탭 인덱스 · 스크롤 위치 · 화면 인자 ID 넣으면 안 되는 것 목록 데이터 · 비트맵 · 큰 JSON

큰 데이터는 ID 만 저장하고 복원 시 DB·네트워크에서 다시 가져온다

어떻게 테스트하나

@Test fun `저장된 검색어로 복원된다`() = runTest {
    val handle = SavedStateHandle(mapOf("query" to "안드로이드"))
    val vm = SearchViewModel(handle, FakeRepository())
    assertEquals("안드로이드", vm.query.value)
}

개발 중에 어떻게 재현하나

개발자 옵션 → "활동 유지 안 함" 을 켠다

  • 또는 — adb shell am kill <package> (백그라운드로 보낸 뒤)

에뮬레이터에서 회전만 시켜 보고 "복원 잘 된다" 고 결론 내리는 것이 가장 흔한 검증 누락이다 — 회전은 ①만 검증한다

면접 함정

  • "ViewModel이 있으니 상태 저장은 필요 없다" → 프로세스 종료를 못 넘는다.
  • "rememberSaveable이면 다 된다"Bundle 직렬화가 가능한 타입만이고 크기 제한도 같다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 상태 관리 — 호이스팅·SavedStateHandle·이벤트