Jetpack Compose에서 상태 관리를 어떻게 해야 할지 헷갈리는 경우가 많음. mutableStateOf, derivedStateOf, produceState, rememberUpdatedState 같은 API들이 각각 언제 쓰는 건지, 그리고 =와 by 차이가 뭔지 정리해봄.
가장 기본적인 상태 생성 방법임. 값을 저장하고, 그 값을 읽는 Composable에서 자동으로 재구성이 일어남.
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("$count")
}
State<T>를 만들어서 Compose Snapshot 시스템이 변경을 추적함.mutableIntStateOf, mutableFloatStateOf 같은 최적화 버전을 쓰면 GC 덜 발생함.다른 상태에서 계산된 값을 만들고 싶을 때 사용함. 비싼 계산을 캐싱해서 불필요한 recomposition을 줄임.
val items by remember { mutableStateOf(listOf(1, 2, 3)) }
val total by remember { derivedStateOf { items.sum() } }
Text("합계: $total")
비동기 소스나 콜백 기반 데이터를 Compose의 상태로 바꾸고 싶을 때 씀. 내부적으로 코루틴을 돌려서 State<T>를 업데이트함.
@Composable
fun Weather(city: String) {
val weather by produceState<String?>(initialValue = null, key1 = city) {
value = repository.fetchWeather(city) // suspend 가능
}
Text(weather ?: "Loading…")
}
LaunchedEffect + mutableStateOf 조합을 한 줄로 합쳐놓은 느낌임.사실 나는 원래 네트워크 호출 같은 비동기 데이터를 처리할 때
var data by remember { mutableStateOf<T?>(null) }
LaunchedEffect(key) { data = repository.getSomething(key) }
이런 식으로 섞어 썼음.
근데 produceState를 보니까 이 패턴을 아예 API 차원에서 지원해주는 거라 훨씬 깔끔해 보였음.
개인적으로 이 부분이 꽤 흥미롭게 다가왔음.
Effect 블록 안에서 최신 값을 안전하게 참조하고 싶을 때 쓰는 API임. LaunchedEffect나 DisposableEffect 같은 블록은 오래 실행되는데, 그 안에서 사용하는 값이 recomposition 때 바뀌면 이전 값을 계속 잡고 있을 수 있음. 이걸 막아줌.
@Composable
fun Timer(onTimeout: () -> Unit) {
val currentOnTimeout by rememberUpdatedState(onTimeout)
LaunchedEffect(Unit) {
delay(3000)
currentOnTimeout() // 항상 최신 onTimeout 실행됨
}
}
mutableStateOf와 달리 recomposition을 트리거하지 않음.= vs byCompose에서 상태를 선언할 때 두 가지 방식이 있음.
val countState = remember { mutableStateOf(0) } // = 방식
var count by remember { mutableStateOf(0) } // by 방식
countState는 MutableState<Int> 객체임. 값을 읽거나 쓸 때 .value를 붙여야 함.
Text("${countState.value}")
countState.value++
count는 by 델리게이트 문법 덕분에 그냥 값처럼 쓸 수 있음.
Text("$count")
count++
동작 차이는 없음. 둘 다 Compose Snapshot 시스템에 의해 감지되고 recomposition 발생함.
차이는 문법 설탕(syntax sugar) 차이일 뿐임.
State 객체 자체를 넘기거나 다뤄야 하면 =, 값만 쓰고 싶으면 by가 편함.
mutableStateOfderivedStateOfproduceStaterememberUpdatedState= vs by는 단순히 문법 차이, 동작은 동일함mutableStateOf + LaunchedEffect 조합을 자주 썼는데, produceState가 그 패턴을 깔끔하게 대체할 수 있다는 점이 인상적이었음이렇게 정리해두면 Compose 상태 관리 관련해서 선택지가 명확해짐.
나처럼 원래 mutableStateOf랑 LaunchedEffect를 붙여 쓰던 사람이라면, produceState가 꽤 반가울 수 있음!!!