[Jetpack Compose] RememberSaveable 이란?

오규성·2025년 11월 6일

Jetpack Compose 기초

목록 보기
3/13

이전 글에서 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 로 저장했던 캐싱 데이터가 유실되었고, 이로 인해 초기화가 이루어졌다.

RememberSaveable 사용 시

이러한 구성 변경 (Configuration Change) 시에도 데이터를 유지하기 위한 방법이 바로 rememberSaveable 의 사용이다.

아까의 text 를 초기화하며 선언했던 rememebr 를 rememberSaveable 로 변경해주자.

var text by rememberSaveable { mutableStateOf("밥") }

변경이 되었다면, 버튼을 눌러 "국" 으로 변경해주고 화면을 회전시켜보자.

이번에는 아까와 달리 값이 유지되었다는 것을 알 수 있다 !

rememberSaveable 로 저장된 데이터가 유지되는 이유

우선 rememberSaveable 에 대해 알아보자.

currentComposer 에 데이터를 저장하는 remember 와 달리 전역변수로 초기화된 LocalSaveableStateRegistry 에 Map<key, value> 를 저장하는 함수

이전 게시글에서 배웠던 remember 의 경우 currentComposer 라는 객체의 내부에 데이터를 저장한다.
currentComposer 는 구성 변경이 일어나는 경우 Activity 가 파괴되면서 Composition Tree 가 정리, 이후 다시 만들어지므로 값이 초기화된다.

하지만 rememberSaveable 의 경우 이와는 다르다.
두 개의 차이를 쉽게 설명하자면 다음과 같다.

remember 는 구성 변경시 Composition Tree 와 함께 재생성되지만, rememberSaveable 은 여러 단계를 거쳐 Activity Bundle 에 저장되므로 값이 유지된다.

rememberSaveable 의 내부 동작 (간단히)

  1. 내부적으로 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
}
  1. SaveableStateRegistry 는 Activity (또는 SavedStateRegistryOwner) 의 생명주기를 따르며, onSaveInstanceState() 시점에 Bundle 로 직렬화되어 저장된다.
  2. 구성 변경 시에도 Bundle 은 유지되므로 내부 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/

profile
안드로이드 개발자 Gyu 의 개발 블로그 !

0개의 댓글