9148c358d0
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를 Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류. - 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로 자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거, 동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거. - 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming, Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business, Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로, 나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는 title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백). 원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지. - 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서. - 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는 지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지. - Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경. - 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
7.3 KiB
7.3 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| android-compose-performance | Compose Performance — recomposition / stability | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
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
@Composable
fun MyComponent() {
SideEffect {
Log.d('Compose', 'recomposed')
}
// ...
}
→ Frequency 측정.
Stability annotation
@Stable
data class User(
val id: String,
val name: String,
)
@Immutable
data class Config(...)
→ Compose 가 skip 가능.
Unstable의 함정
// ❌ List 가 unstable
@Composable
fun ItemList(items: List<Item>) {
// 매 recomposition.
}
// ✅ ImmutableList (kotlinx.collections.immutable)
@Composable
fun ItemList(items: ImmutableList<Item>) {
// Skip 가능.
}
→ kotlinx.collections.immutable.
Lambda stability
// ❌ 매 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+)
// build.gradle.kts
kotlin {
compilerOptions {
freeCompilerArgs.add('-P plugin:androidx.compose.compiler.plugins.kotlin:experimentalStrongSkipping=true')
}
}
→ 자동 skip (lambda 도). Magic 식.
derivedStateOf
@Composable
fun Screen(items: List<Item>) {
val hasMore by remember(items) {
derivedStateOf { items.size > 100 }
}
// hasMore 가 변경 시 만 update.
}
→ Computed value memoize.
key (recomposition 정밀)
@Composable
fun ItemList(items: List<Item>) {
LazyColumn {
items(items, key = { it.id }) { item ->
ItemRow(item)
}
}
}
→ Item 의 identity. Reorder 시 잘못 recompose 안.
LazyColumn / LazyRow
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
// In code
import androidx.compose.runtime.tooling.*
Trace.beginSection('MyComponent')
// ...
Trace.endSection()
→ Systrace 가 visible.
Baseline profile
// 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
./gradlew assembleRelease -PenableComposeCompilerReports=true
# Generated:
# build/compose_metrics/-classes.txt
# - Stable / unstable 매 class.
→ "왜 unstable?" 검증.
List 의 함정
// ❌
data class State(val items: List<String>)
// → List 가 unstable.
// ✅
data class State(val items: ImmutableList<String>)
Function 의 함정
// ❌ Function type 가 unstable 가 default
data class State(val onClick: () -> Unit)
// ✅
@Stable data class State(val onClick: () -> Unit)
→ Strong skipping mode 가 자동 fix.
remember vs derivedStateOf
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
import kotlinx.collections.immutable.*
val list: ImmutableList<Item> = persistentListOf(...)
val map: ImmutableMap<String, Int> = persistentMapOf(...)
→ Compose 가 stable 인식.
Stop animation 시
val transition = rememberInfiniteTransition()
val rotation by transition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(...)
)
// Visible 안 = 자동 stop.
→ Battery 친화.
Modifier order
// Order 가 important
Box(modifier = Modifier
.padding(8.dp)
.background(Color.Red)
.size(100.dp)
)
→ Modifier 가 chain. 매 modifier 가 cost.
Profile
// Layout Inspector → Recomposition Count.
// 큰 number = bottleneck.
// Profile mode (release):
./gradlew assembleRelease
adb install ...
adb shell setprop debug.layout true
CompositionLocal 의 함정
val LocalUser = compositionLocalOf<User> { error('not provided') }
// 매 user 변경 = 매 consumer recompose.
→ 자주 변경 = unstable.
Jetpack Compose Compiler Metrics
# 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.