이전 글에서 Jetpack Compose 에서 아주 중요한 State 와 remember 에 대해서 알아보았다.
이제 우리는 Jetpack Compose 를 이용하면서 Composition 을 발생시키기 위해서는 어떻게 해야하는지와 데이터를 유지하기 위한 방법을 알아냈다.
class MainActivity() : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
enableEdgeToEdge()
GyuDefaultTheme {
Scaffold {
var text by remember { mutableStateOf("밥") }
Column(modifier = Modifier
.fillMaxSize()
.padding(it)
) {
Text(text = text)
Button(
onClick = {
text = if(text == "국") "밥" else "국"
Timber.d("${text}으로 변경 완료")
}
) { Text(text = text)}
}
}
}
}
}
}
이전 글과 같은 코드를 구현해보았다.
이 코드는 버튼을 누를때마다 "밥" 은 "국" 으로, "국"은 "밥" 으로 변경되고 유지된다.

그런데 갑자기 이를 이용하던 사용자가 화면을 돌리게 되었다 !
현재는 "밥" -> "국" 으로 변경한 상황.
이 경우 데이터는 어떻게 될까?

화면 회전으로 인해 액티비티가 파괴되고, 재실행되면서 remember 로 저장했던 캐싱 데이터가 유실되었고, 이로 인해 초기화가 이루어졌다.
이러한 구성 변경 (Configuration Change) 시에도 데이터를 유지하기 위한 방법이 바로 rememberSaveable 의 사용이다.
아까의 text 를 초기화하며 선언했던 rememebr 를 rememberSaveable 로 변경해주자.
var text by rememberSaveable { mutableStateOf("밥") }
변경이 되었다면, 버튼을 눌러 "국" 으로 변경해주고 화면을 회전시켜보자.

이번에는 아까와 달리 값이 유지되었다는 것을 알 수 있다 !
우선 rememberSaveable 에 대해 알아보자.
currentComposer에 데이터를 저장하는 remember 와 달리 전역변수로 초기화된LocalSaveableStateRegistry에 Map<key, value> 를 저장하는 함수
이전 게시글에서 배웠던 remember 의 경우 currentComposer 라는 객체의 내부에 데이터를 저장한다.
이 currentComposer 는 구성 변경이 일어나는 경우 Activity 가 파괴되면서 Composition Tree 가 정리, 이후 다시 만들어지므로 값이 초기화된다.
하지만 rememberSaveable 의 경우 이와는 다르다.
두 개의 차이를 쉽게 설명하자면 다음과 같다.
remember는 구성 변경시 Composition Tree 와 함께 재생성되지만,rememberSaveable은 여러 단계를 거쳐 Activity Bundle 에 저장되므로 값이 유지된다.
LocalSaveableStateRegistry 라는 것을 가져오고 이곳에 값을 저장한다.val LocalSaveableStateRegistry = staticCompositionLocalOf<SaveableStateRegistry?> { null }
@Composable
fun <T : Any> rememberSaveable(
vararg inputs: Any?,
saver: Saver<T, out Any> = autoSaver(),
key: String? = null,
init: () -> T
): T {
val compositeKey = currentCompositeKeyHash
// key is the one provided by the user or the one generated by the compose runtime
val finalKey = if (!key.isNullOrEmpty()) {
key
} else {
compositeKey.toString(MaxSupportedRadix)
}
@Suppress("UNCHECKED_CAST")
(saver as Saver<T, Any>)
val registry = LocalSaveableStateRegistry.current
val holder = remember {
// value is restored using the registry or created via [init] lambda
val restored = registry?.consumeRestored(finalKey)?.let {
saver.restore(it)
}
val finalValue = restored ?: init()
SaveableHolder(saver, registry, finalKey, finalValue, inputs)
}
val value = holder.getValueIfInputsDidntChange(inputs) ?: init()
SideEffect {
holder.update(saver, registry, finalKey, value, inputs)
}
return value
}
registry.consumeRestore 에 의해 값이 복원 된다.단, Bundle 로 저장되기 때문에 primitive type 만 지원하며 그 외의 것을 저장하기 위해서는 Custom Saver 를 구현해야한다.
rememberSaveable 에 대해서 알고 있었는데, 글을 다시 쓰면서 내부적으로도 깊게 알아보다보니 생각보다 글 작성이 오래걸렸다.
물론 이 모든 것을 이번 글에 써내리기에는 기초가 아닌 심화 난이도를 가지기에, 추후 다시 작성하려고 한다.
일부 참고 https://jowunnal.github.io/android/%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC3/