LazyColumn 아이템 키와 상태 보존
LazyColumn 아이템 키와 상태 보존
LazyColumn의 아이템이 순서가 바뀌거나 추가, 삭제될 때 Jetpack Compose는 각 아이템의 remember 상태를 올바른 데이터에 연결해 두어야 합니다. 아이템 키(item key)가 바로 이 연결 고리 역할을 합니다. 키를 명시하지 않으면 Compose는 아이템의 **위치(position)**를 기본 키로 사용하는데, 위치가 바뀌는 순간 상태가 엉뚱한 아이템에 연결되는 문제가 발생합니다. 또한 rememberSaveable을 사용하여 Activity 재생성 이후에도 상태를 유지하려면, 키가 안드로이드의 Bundle 메커니즘과 호환되는 타입이어야 합니다.
이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.
- 위치 기반 키 지정 방식이 리스트 재정렬 시 왜 상태 불일치를 유발하는지 설명할 수 있습니다.
- 안정적이고 고유한 키가 Compose에서 아이템의 위치 변경을 어떻게 추적하는지 이해할 수 있습니다.
rememberSaveable과 함께 사용하는 키에Bundle호환성이 요구되는 이유를 파악할 수 있습니다.Bundle이 키 값으로 지원하는 타입 목록을 나열할 수 있습니다.rememberSaveable상태를 포함하는LazyColumn에 아이템 키를 올바르게 적용할 수 있습니다.
위치 기반 키 지정의 문제점
기본적으로 Compose는 LazyColumn 내 각 아이템의 상태를 **리스트에서의 위치(position)**에 매핑합니다. 가령, 5개의 아이템이 있고 각각 remember { mutableStateOf(0) } 카운터를 보유한다고 가정해 봅시다. 이 경우 위치 0에 있는 카운터는 해당 슬롯에 어떤 데이터 아이템이 들어오든 상관없이 항상 위치 0에 고정됩니다.
리스트를 재정렬하면 위치 0에 있던 데이터 아이템이 위치 3으로 이동하지만, 위치 0의 카운터 값은 그대로 남아 있습니다. 결과적으로 사용자에게는 카운터 값이 엉뚱한 아이템에 붙어 보이게 됩니다. 아이템 삽입이나 삭제 시에도 동일한 문제가 발생하는데, 상태가 데이터를 따라가지 못하고 인접한 위치로 밀려나기 때문입니다.
이 문제는 사용자 상호작용이 있는 아이템에서 특히 두드러집니다. 사용자 입력이 담긴 텍스트 필드, 체크 상태를 가진 체크박스, 현재 펼쳐진 확장 섹션 등이 재정렬 후 엉뚱한 아이템에 표시됩니다. 컴포저블 함수 자체는 정상적으로 실행되기 때문에 디버깅이 까다로운데, 근본 원인은 Compose가 상태를 아이템에 매핑하는 방식에 있습니다. 면접에서 이 부분을 정확히 설명할 수 있다면 Compose 내부 동작에 대한 깊은 이해를 보여줄 수 있습니다.
안정적이고 고유한 키 지정
items DSL의 key 매개변수를 통해 키를 지정하면 이 문제를 해결할 수 있습니다. 키는 반드시 **안정적(stable)**이어야 하며(특정 데이터 아이템에 대해 변하지 않아야 함), **고유(unique)**해야 합니다(동일한 키를 가진 아이템이 두 개 이상 존재하면 안 됨).
data class MyItem(val id: String, val content: String)
@Composable
fun MyStatefulList(myItems: List<MyItem>) {
LazyColumn {
items(
items = myItems,
key = { item -> item.id } // 데이터 고유 ID를 키로 지정
) { dataItem ->
ItemWithState(item = dataItem)
}
}
}