--- id: android-compose-performance title: Compose Performance β€” recomposition / stability category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [android, compose, performance, vibe-coding] tech_stack: { language: "Kotlin", applicable_to: ["Android"] } applied_in: [] aliases: [Compose performance, recomposition, stability, immutable, baseline profile, strong skipping mode] --- # Compose Performance > Recomposition 폭발 = jank. **Stable / immutable, derivedStateOf, baseline profile, strong skipping mode**. ## πŸ“– 핡심 κ°œλ… - Recomposition = render again. - Stable = type κ°€ λ³€κ²½ μ‹œ 비ꡐ κ°€λŠ₯ β†’ skip κ°€λŠ₯. - Strong skipping mode (Kotlin 2.0+) = μžλ™ skip. - Baseline profile = startup 빠름. ## πŸ’» μ½”λ“œ νŒ¨ν„΄ ### Recomposition trace ```kotlin @Composable fun MyComponent() { SideEffect { Log.d('Compose', 'recomposed') } // ... } ``` β†’ Frequency μΈ‘μ •. ### Stability annotation ```kotlin @Stable data class User( val id: String, val name: String, ) @Immutable data class Config(...) ``` β†’ Compose κ°€ skip κ°€λŠ₯. ### Unstable의 함정 ```kotlin // ❌ List κ°€ unstable @Composable fun ItemList(items: List) { // λ§€ recomposition. } // βœ… ImmutableList (kotlinx.collections.immutable) @Composable fun ItemList(items: ImmutableList) { // Skip κ°€λŠ₯. } ``` β†’ kotlinx.collections.immutable. ### Lambda stability ```kotlin // ❌ λ§€ recomposition κ°€ μƒˆ lambda @Composable fun Screen(viewModel: ViewModel) { Button(onClick = { viewModel.action() }) { ... } } // βœ… Method reference (stable) @Composable fun Screen(viewModel: ViewModel) { Button(onClick = viewModel::action) { ... } } // βœ… remember @Composable fun Screen() { val onClick = remember { { /* ... */ } } Button(onClick = onClick) { ... } } ``` ### Strong Skipping Mode (Kotlin 2.0+) ```kotlin // build.gradle.kts kotlin { compilerOptions { freeCompilerArgs.add('-P plugin:androidx.compose.compiler.plugins.kotlin:experimentalStrongSkipping=true') } } ``` β†’ μžλ™ skip (lambda 도). Magic 식. ### derivedStateOf ```kotlin @Composable fun Screen(items: List) { val hasMore by remember(items) { derivedStateOf { items.size > 100 } } // hasMore κ°€ λ³€κ²½ μ‹œ 만 update. } ``` β†’ Computed value memoize. ### key (recomposition μ •λ°€) ```kotlin @Composable fun ItemList(items: List) { LazyColumn { items(items, key = { it.id }) { item -> ItemRow(item) } } } ``` β†’ Item 의 identity. Reorder μ‹œ 잘λͺ» recompose μ•ˆ. ### LazyColumn / LazyRow ```kotlin LazyColumn { items(1000) { index -> Card { Text('item $index') } } } ``` β†’ Visible 만 render. RecyclerView 의 modern. ### Layout inspector ``` Android Studio: - Layout Inspector. - Compose tree. - Recomposition count per node. ``` β†’ "이 node κ°€ κ°€μ§œ recompose?". ### Tracing ```kotlin // In code import androidx.compose.runtime.tooling.* Trace.beginSection('MyComponent') // ... Trace.endSection() ``` β†’ Systrace κ°€ visible. ### Baseline profile ```kotlin // macrobenchmark module @RunWith(AndroidJUnit4::class) class BaselineProfileGenerator { @get:Rule val rule = BaselineProfileRule() @Test fun generate() = rule.collect(packageName = 'com.example.app') { startActivityAndWait() // critical user flow } } ``` β†’ AOT compile of hot code. Cold start 30-50% 빠름. ### Stability inspect ```bash ./gradlew assembleRelease -PenableComposeCompilerReports=true # Generated: # build/compose_metrics/-classes.txt # - Stable / unstable λ§€ class. ``` β†’ "μ™œ unstable?" 검증. ### List 의 함정 ```kotlin // ❌ data class State(val items: List) // β†’ List κ°€ unstable. // βœ… data class State(val items: ImmutableList) ``` ### Function 의 함정 ```kotlin // ❌ Function type κ°€ unstable κ°€ default data class State(val onClick: () -> Unit) // βœ… @Stable data class State(val onClick: () -> Unit) ``` β†’ Strong skipping mode κ°€ μžλ™ fix. ### remember vs derivedStateOf ```kotlin val sorted = remember(items) { items.sortedBy { it.name } } // β†’ items κ°€ λ³€κ²½ μ‹œ recalculate. val hasMore by remember { derivedStateOf { items.size > 100 } } // β†’ items κ°€ λ³€κ²½ + hasMore κ°€ λ³€κ²½ μ‹œ 만 update. ``` β†’ derivedStateOf κ°€ fine-grained. ### Immutable collection ```kotlin import kotlinx.collections.immutable.* val list: ImmutableList = persistentListOf(...) val map: ImmutableMap = persistentMapOf(...) ``` β†’ Compose κ°€ stable 인식. ### Stop animation μ‹œ ```kotlin val transition = rememberInfiniteTransition() val rotation by transition.animateFloat( initialValue = 0f, targetValue = 360f, animationSpec = infiniteRepeatable(...) ) // Visible μ•ˆ = μžλ™ stop. ``` β†’ Battery μΉœν™”. ### Modifier order ```kotlin // Order κ°€ important Box(modifier = Modifier .padding(8.dp) .background(Color.Red) .size(100.dp) ) ``` β†’ Modifier κ°€ chain. λ§€ modifier κ°€ cost. ### Profile ```kotlin // Layout Inspector β†’ Recomposition Count. // 큰 number = bottleneck. // Profile mode (release): ./gradlew assembleRelease adb install ... adb shell setprop debug.layout true ``` ### CompositionLocal 의 함정 ```kotlin val LocalUser = compositionLocalOf { error('not provided') } // λ§€ user λ³€κ²½ = λ§€ consumer recompose. ``` β†’ 자주 λ³€κ²½ = unstable. ### Jetpack Compose Compiler Metrics ```bash # build/compose_metrics/ - *-classes.txt (stability) - *-composables.txt (skippable, restartable) ``` β†’ Skippable: input κ°™μŒ = skip OK. Restartable: trigger restart of recomposition. ### Performance tier ``` 1. Layout Inspector (initial check). 2. Compose Compiler Reports (stability). 3. Tracing (Trace.beginSection). 4. Baseline profile (startup). 5. Macrobenchmark (real device). ``` ### Real-world tips ``` - 큰 list = LazyColumn + key. - ImmutableList default. - Strong skipping mode (Kotlin 2.0+). - Method reference > lambda. - @Stable / @Immutable annotation. - Profile λ§€ release. ``` ### vs RecyclerView (μ˜›) ``` RecyclerView: imperative, 빠름. LazyColumn: declarative, λΉ„μŠ· 빠름. β†’ ν˜„μž¬ = LazyColumn. ``` ### Common 함정 ``` - List κ°€ unstable. - Lambda κ°€ 맀번 μƒˆ. - ViewModel state κ°€ 큰 object. - λ§€ recomposition κ°€ expensive work. - LazyColumn κ°€ key μ—†μŒ. - Modifier κ°€ 맀번 μƒˆ. ``` ## πŸ€” μ˜μ‚¬κ²°μ • κΈ°μ€€ | μž‘μ—… | μΆ”μ²œ | |---|---| | Custom data class | @Stable / @Immutable | | List | ImmutableList | | Lambda | Method reference / remember | | Compute | derivedStateOf | | Big list | LazyColumn + key | | Cold start | Baseline profile | | Profile | Layout Inspector + tracing | ## ❌ μ•ˆν‹°νŒ¨ν„΄ - **List κ°€ unstable**: skip μ•ˆ 됨. - **Lambda κ°€ 맀번**: recompose 폭발. - **No key in LazyColumn**: identity 깨짐. - **CompositionLocal 자주 λ³€κ²½**: 폭발. - **Big object state**: λ§€ λ³€κ²½ = 큰 recompose. - **Profile μ•ˆ 함**: blind. ## πŸ€– LLM ν™œμš© 힌트 - Stable / immutable κ°€ 핡심. - Strong skipping mode (Kotlin 2.0+). - Baseline profile κ°€ cold start. - Layout Inspector κ°€ visible. ## πŸ”— κ΄€λ ¨ λ¬Έμ„œ - [[Android_Compose_Recomposition_Pitfalls]] - [[Android_Compose_State_Hoisting]] - [[Android_Baseline_Profile]]