compose로 UI를 개발하다보면 상태 하나만 관리하면 될 줄 알았던 UI가 점점 복잡해지는 순간이 찾아옵니다.
이렇게 '원본 상태'외에도 그 상태로부터 파생되는 값들이 점점 늘어나면서 상태 관리가 어려워지고 코드도 복잡해지기 시작합니다.
특히 compose는 recomposition이 자주 발생하기 때문에 이런 파생 값들을 잘못 관리하면 불필요한 연산 + 성능 저하로 이어지기 쉽습니다. 그래서 이번 글에서는 Jetpack Compose에서 파생 상태를 효율적으로 다루는 derivedStateOf를 기본 개념부터 mutableStateOf와 비교, 실제 예제까지 정리해보겠습니다.
derivedStateOf는 Jetpack Compose에서 제공하는 파생 상태 계산 도구입니다. 다른 상태로부터 계산되는 값을 효율적으로 관리할 수 있도록 도와줍니다.
주요 특징
@Composable
fun SearchScreen() {
var searchQuery by remember { mutableStateOf("") }
val items = remember { listOf("Apple", "Banana", "Cherry", "Date", "Elderberry") }
val filteredItems by remember {
derivedStateOf {
items.filter { it.contains(searchQuery, ignoreCase = true) }
}
}
Column {
TextField(
value = searchQuery,
onValueChange = { searchQuery = it },
label = { Text("검색") }
)
LazyColumn {
items(filteredItems) { item ->
Text(item, modifier = Modifier.padding(8.dp))
}
}
}
}
동작 원리
1. searchQuery가 변경될 때만 filteredItems가 재계산됨
2. searchQuery가 같은 값으로 여러 번 recomposition되어도 한 번만 계산
3. 필터링 결과가 실제로 달라질 때만 LazyColumn이 업데이트됨
파생 상태를 관리할 때 mutableStateOf를 사용할지, derivedStateOf를 고민하게 되는데, 두 방식의 차이를 비교해보겠습니다.
mutableStateOf를 사용한 방식 (❌ 비효율적)
@Composable
fun SearchScreenWithMutableState() {
var searchQuery by remember { mutableStateOf("") }
val items = remember { listOf("Apple", "Banana", "Cherry", "Date", "Elderberry") }
// mutableStateOf로 필터링 결과를 별도 상태로 관리
var filteredItems by remember { mutableStateOf(items) }
Column {
TextField(
value = searchQuery,
onValueChange = { newQuery ->
searchQuery = newQuery
// 수동으로 필터링 결과 업데이트 - 실수하기 쉬움!
filteredItems = items.filter {
it.contains(newQuery, ignoreCase = true)
}
},
label = { Text("검색") }
)
LazyColumn {
items(filteredItems) { item ->
Text(item, modifier = Modifier.padding(8.dp))
}
}
}
}
문제점
derivedStateOf를 사용한 방식 (✅ 효율적)
@Composable
fun SearchScreenWithDerivedState() {
var searchQuery by remember { mutableStateOf("") }
val items = remember { listOf("Apple", "Banana", "Cherry", "Date", "Elderberry") }
// derivedStateOf로 자동 계산
val filteredItems by remember {
derivedStateOf {
items.filter { it.contains(searchQuery, ignoreCase = true) }
}
}
Column {
TextField(
value = searchQuery,
onValueChange = { searchQuery = it },
label = { Text("검색") }
)
LazyColumn {
items(filteredItems) { item ->
Text(item, modifier = Modifier.padding(8.dp))
}
}
}
}
장점
derivedStateOf는 Jetpack Compose에서 파생 상태를 효율적으로 관리하는 핵심 도구입니다. 이를 적절히 활용하면 불필요한 재계산을 방지하고, 코드를 더 선언적이고 유지보수하기 쉽게 만들 수 있습니다. 상태 관리가 복잡해질 때마다 "이 값은 다른 상태로부터 파생되는 것인가?"를 한 번 생각해고, 그렇다면, derivedStateOf가 여러분의 코드를 한층 더 깔끔하게 만들 수 있을 것입니다.
Jetpack Compose 공식 문서 - State
Compose Performance 가이드
derivedStateOf API 문서