Compose 는 "필요한 부분만 다시 그린다" 가 디폴트지만, stable / immutable / skippable 조건이 깨지면 매번 다시 그림. Layout Inspector + Compose Compiler Metrics 로 측정 후 최적화.
📖 핵심 개념
Stable: 필드가 변하지 않거나 변경 시 알림. compose 가 ==/equals 안전 비교 가능.
Immutable: 자체적으로 mutate 불가 (val + immutable types).
Skippable: stable 인 매개변수만 받는 composable. 같은 args → skip.
💻 코드 패턴
Immutable data class
@ImmutabledataclassUser(valid:String,valname:String)// 모든 필드 val + immutable type
@ComposablefunUserCard(user:User){...}// skippable
List 는 ImmutableList
importkotlinx.collections.immutable.ImmutableListimportkotlinx.collections.immutable.persistentListOf@ComposablefunItems(items:ImmutableList<Item>){// 일반 List 는 unstable
LazyColumn{items(items){Row(it)}}}
derivedStateOf — 파생 값 캐시
valshowButtonbyremember(items){derivedStateOf{items.isNotEmpty()&&items.all{it.isValid}}}// items 변하지 않으면 계산 skip
key() 로 강제 재생성
@ComposablefunAnimated(target:T){key(target.id){AnimatedContent(target){...}// id 바뀌면 새 instance
}}
Lambda — remember 없이 인라인 OK (Compose 가 안정화)
Button(onClick={vm.onClick()}){...}// OK — Compose 1.x 부터 안정
🤔 의사결정 기준
상황
도구
큰 list
LazyColumn / LazyVerticalGrid + key
자식이 매번 재구성됨 (불필요)
데이터 타입 immutable / stable 점검
비싼 계산
remember(deps) { compute() }
파생 state
derivedStateOf
부모 state 변경에 무관한 자식
Modifier.composed 또는 별도 composable
❌ 안티패턴
일반 List 를 prop 으로: Compose 가 unstable 로 봄. Recomposition 빈발. ImmutableList.
Map<K,V> 일반 사용: 마찬가지. ImmutableMap.
lambda 안 의 state 가 매번 다른 참조: skippable 깨짐. 보통은 OK 지만 문제되면 remember.
remember 없이 비싼 객체 매번 생성: GC 부담.
Modifier 가 매번 새 인스턴스: skippable 영향. 자주 그러면 미리 만들거나 Modifier.composed.
ViewModel 의 mutableStateOf 직접 노출: 외부 mutate 가능. State 또는 StateFlow 로 노출.
profiler 없이 추측 최적화: 측정 후 optimize.
🤖 LLM 활용 힌트
"데이터 클래스는 @Immutable 또는 @Stable 마크, List 는 ImmutableList" 강조.