Jetpack Compose의 SaveableStateHolder
Jetpack Compose의 SaveableStateHolder
SaveableStateHolder는 고유 식별자를 키로 사용하여 컴포저블 하위 트리의 rememberSaveable 상태를 독립적으로 보존하고 복원하는 Compose 인터페이스입니다. rememberSaveable이 구성 변경(configuration changes) 시 개별 값을 유지하는 역할을 한다면, SaveableStateHolder는 저장 가능한 상태의 전체 범위(scope)를 관리합니다. 덕분에 내비게이션 시스템의 각 화면처럼 독립적인 생명주기를 갖는 컴포저블에 대해 상태를 저장, 제거, 복원할 수 있습니다. 면접에서 이 주제가 나오면 단순히 rememberSaveable과의 차이만 설명하는 것이 아니라, 내비게이션 상태 관리의 전체 그림을 이해하고 있음을 보여주는 것이 중요합니다.
이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.
SaveableStateHolder가 범위와 목적 면에서rememberSaveable과 어떻게 다른지 설명할 수 있습니다.SaveableStateProvider가 고유 키를 사용하여 컴포저블 하위 트리의 저장 가능한 상태를 어떻게 연결하는지 설명할 수 있습니다.rememberSaveableStateHolder()를 활용하여 화면별 상태를 보존하는 내비게이션 시스템을 구축할 수 있습니다.- 상태를
SaveableStateHolder에 귀속시킬지,ViewModel에 귀속시킬지 판단하는 기준을 제시할 수 있습니다. - 컴포저블 하위 트리가 컴포지션에 진입하거나 이탈할 때 저장된 상태의 생명주기를 설명할 수 있습니다.
- 영구적으로 폐기된 화면의 저장 상태를 제거하여 메모리를 관리하는 방법을 이해할 수 있습니다.
SaveableStateHolder의 동작 원리
SaveableStateHolder는 rememberSaveableStateHolder()를 통해 생성됩니다. 이 인터페이스는 SaveableStateProvider(key, content) 컴포저블을 제공하며, 해당 컴포저블은 하위 트리를 감싸면서 그 안에서 호출되는 모든 rememberSaveable을 지정된 키와 연결합니다. 하위 트리가 컴포지션에서 제거되더라도 상태는 홀더에 보존되며, 동일한 키로 하위 트리가 다시 추가되면 상태가 자동으로 복원됩니다.
@Composable
fun SimpleNavigation(currentRoute: String) {
val stateHolder = rememberSaveableStateHolder()
stateHolder.SaveableStateProvider(currentRoute) {
when (currentRoute) {
"home" -> HomeScreen()
"settings" -> SettingsScreen()
}
}
}
사용자가 "home"에서 "settings"로 이동하면 HomeScreen 컴포저블이 컴포지션에서 제거됩니다. 일반적으로 HomeScreen 내부의 모든 rememberSaveable 상태는 이 시점에서 소실됩니다. 하지만 SaveableStateProvider("home")로 감싸져 있기 때문에, 상태 홀더가 해당 상태를 "home" 키 아래에 유지합니다. 사용자가 다시 "home"으로 돌아오면 SaveableStateProvider("home")가 저장된 상태를 모두 복원하므로, HomeScreen은 사용자가 떠나기 전과 완전히 동일한 상태로 표시됩니다. 이 메커니즘이 바로 화면 전환 시 스크롤 위치나 입력 내용이 유지되는 핵심 원리입니다.
고유 키를 활용한 상태 범위 지정
SaveableStateProvider에 전달하는 키는 상태 범위의 식별자 역할을 합니다. 각 고유 키는 독립적인 저장 상태 집합에 매핑되므로, 서로 다른 키를 가진 두 화면은 동일한 컴포저블 함수를 사용하더라도 완전히 별개의 상태를 유지합니다. 키는 equals()와 hashCode()가 올바르게 구현된 모든 타입을 사용할 수 있으며, 실무에서는 문자열이나 data class를 가장 많이 활용합니다.
키는 반드시 안정적이고 홀더 범위 내에서 고유해야 합니다. 리스트 인덱스를 키로 사용하면 항목 순서가 변경될 때 문제가 발생합니다. 예를 들어 위치 0에 저장되었던 상태가 순서 변경 후 위치 0을 차지하는 전혀 다른 항목에 복원되기 때문입니다. 이 부분은 LazyColumn의 key 파라미터 설계 철학과도 일맥상통하므로, 면접에서 함께 언급하면 이해도가 높다는 인상을 줄 수 있습니다.