|

Jetpack Compose remember와 rememberSaveable 차이

Compose remember와 rememberSaveable 차이를 설명하는 대표 이미지
상태가 다시 살아나는지 여부는 저장 범위와 수명 주기를 같이 봐야 이해된다

Jetpack Compose remember와 rememberSaveable 차이는 처음엔 단순히 “저장된다/안 된다” 정도로 보이기 쉽습니다. 하지만 실제로는 상태가 얼마나 오래 살아야 하는지를 먼저 정해야 이해가 됩니다.

이번 글에서는 configuration change, process death, Bundle 저장 한계, ViewModel과의 경계까지 포함해서 언제 remember를 쓰고 언제 rememberSaveable을 써야 하는지 정리하겠습니다.

상태 저장의 공식 개념 배경은 Android Developers의 Compose state / state-saving 문서 흐름을 기준으로 이해하면 가장 안전합니다. 이 글에서는 그 개념을 실무 사례 쪽으로 풀어 설명합니다.


remember는 어디까지 살아남을까

remember는 컴포지션 안에서 값을 기억합니다. 즉 같은 화면이 다시 recomposition될 때는 유지되지만, 그 컴포지션 자체가 사라지면 같이 사라집니다.

  • 같은 컴포지션 안 recomposition에는 유지된다
  • 화면이 완전히 사라지면 같이 잊힌다
  • process death 복원은 기대하면 안 된다

그래서 remember는 순간적인 UI 상태, 예를 들어 토글 열림 여부나 임시 입력 상태처럼 짧은 수명의 상태에 더 잘 맞습니다.


rememberSaveable은 무엇이 다를까

rememberSaveable은 이름 그대로 저장 가능한 상태를 Bundle 기반으로 보관해, Activity 재생성이나 일부 복원 상황에서도 다시 꺼낼 수 있게 도와줍니다.

즉 핵심 차이는 “더 오래 산다”는 점입니다. 하지만 여기서 자주 생기는 오해가 있습니다. rememberSaveable이 모든 상태를 마법처럼 복원해주는 것은 아닙니다.

  • Bundle에 담을 수 있는 형태여야 한다
  • 너무 크거나 복잡한 객체는 바로 저장하기 어렵다
  • 필요하면 Saver를 직접 정의해야 한다


코드로 보면 차이가 더 빨리 보인다

remember 예시

@Composable
fun SearchBarRemember() {
    var query by remember { mutableStateOf("") }

    TextField(
        value = query,
        onValueChange = { query = it },
        label = { Text("검색어") }
    )
}

이 코드는 recomposition에는 버티지만, Activity가 다시 만들어지는 상황에서는 입력값이 그대로 남는다고 기대하면 안 됩니다. 즉 화면 안에서만 잠깐 살아도 되는 상태일 때 잘 맞습니다.

rememberSaveable 예시

@Composable
fun SearchBarRememberSaveable() {
    var query by rememberSaveable { mutableStateOf("") }

    TextField(
        value = query,
        onValueChange = { query = it },
        label = { Text("검색어") }
    )
}

이 경우에는 저장 가능한 타입인 문자열을 Bundle 기반으로 다시 꺼낼 수 있어서, 화면 회전 뒤에도 사용자가 입력하던 값이 유지되는 쪽에 더 가깝습니다.

복잡한 객체는 바로 안 되는 경우가 많다

data class FilterState(
    val sort: String,
    val showOnlyInStock: Boolean,
    val selectedTags: List<String>,
)

@Composable
fun FilterScreen() {
    var state by rememberSaveable(
        saver = listSaver(
            save = { listOf(it.sort, it.showOnlyInStock, it.selectedTags) },
            restore = {
                FilterState(
                    sort = it[0] as String,
                    showOnlyInStock = it[1] as Boolean,
                    selectedTags = it[2] as List<String>,
                )
            }
        )
    ) { mutableStateOf(FilterState("latest", false, emptyList())) }
}

실무에서는 여기서 바로 감이 옵니다. 상태가 복잡해질수록 rememberSaveable만으로 버티기보다, 이 상태가 정말 UI 복원용인지 아니면 ViewModel이 들고 가야 할 화면 상태인지 다시 나누는 편이 낫습니다.

configuration change와 process death를 구분해야 한다

Compose 상태 복원에서 가장 자주 헷갈리는 지점이 바로 여기입니다. 화면 회전 같은 configuration change와, 시스템에 의해 프로세스가 죽었다가 다시 살아나는 process death는 체감은 비슷해 보여도 의미가 다릅니다.

remember는 configuration change 이후 Activity가 다시 만들어지면 그대로 남는다고 기대하면 안 됩니다. 반대로 rememberSaveable은 복원 가능 범위를 넓혀주지만, 저장 가능한 상태라는 조건이 붙습니다.


언제 remember를 쓰면 좋을까

  1. 화면 안에서만 잠깐 유지되면 되는 상태
  2. 복원까지는 필요 없는 UI 상태
  3. 입력 도중의 임시 값처럼 가벼운 상태

예를 들어 드롭다운 열림 여부, 임시 선택 상태, 한 번의 화면 생명주기 안에서만 의미 있는 값은 remember가 더 단순합니다.


언제 rememberSaveable을 쓰면 좋을까

  1. 회전 후에도 유지되면 좋은 값
  2. 사용자가 다시 입력하면 불편한 값
  3. 저장 가능한 크기의 단순 상태

탭 선택 상태, 검색창 입력값, 폼 일부 입력값처럼 사용자가 다시 만들면 귀찮은 상태는 rememberSaveable 쪽이 더 자연스럽습니다.


그럼 ViewModel은 어디에 써야 할까

여기서 한 단계 더 가면 결국 ViewModel 경계를 보게 됩니다. rememberSaveable은 저장 가능한 UI 상태를 돕는 도구이고, ViewModel은 화면이 다루는 비즈니스 성격의 상태를 더 오래 관리하는 데 가깝습니다.

즉 사용자 입력값 일부를 잠깐 복원하는 일과, 서버에서 불러온 화면 상태를 유지하는 일은 분리해서 보는 편이 좋습니다. 이 점은 rememberSaveable과 SavedStateHandle 차이 글과도 바로 연결됩니다.


실무에서 자주 하는 실수

  • 모든 상태를 rememberSaveable로 감싸는 것
  • 복잡한 객체를 무리하게 저장하려는 것
  • UI 상태와 화면 비즈니스 상태를 섞는 것

핵심은 더 오래 저장하는 것이 항상 더 좋은 설계가 아니라는 점입니다. 상태는 필요한 만큼만 살아남게 두는 편이 오히려 구조가 더 명확합니다.


마무리

Jetpack Compose remember와 rememberSaveable 차이는 단순 기능 차이보다 상태 수명 주기 차이에 가깝습니다. remember는 컴포지션 안에서, rememberSaveable은 저장 가능한 상태를 더 오래 복원하는 데 초점이 있습니다.

결국 중요한 것은 “이 상태가 어디까지 살아야 하는가”를 먼저 정하는 일입니다. 그 질문이 분명해지면 remember와 rememberSaveable 구분도 훨씬 쉬워집니다.

함께보면 좋은 글