안드로이드 시스템·Compose 용어 사전
Composestability · skippable · @Immutable · @Stable

안정성

파라미터가 안 바뀌었음을 컴파일러가 확신할 수 있는 성질. 건너뛰기의 전제.

파라미터가 안 바뀌었음을 컴파일러가 확신할 수 있는 성질. 건너뛰기(skip)의 전제다.

무엇을 안정으로 보나

안정(stable)

  • 원시 타입 · String · 함수 타입
  • 모든 public 프로퍼티가 val 이고 그 타입도 안정인 data class
  • MutableState (변경이 스냅샷으로 관측되므로)

불안정(unstable)

  • var 프로퍼티를 가진 클래스
  • List · Set · Map (인터페이스라 구현이 바뀔 수 있다)
  • 다른 모듈의 클래스 (컴파일러가 볼 수 없다 — 단, Strong Skipping 이 완화한다)
// ❌ 불안정 — 파라미터가 같아도 매번 다시 그려진다
data class UiState(val users: List<User>)

// ✅ 불변 컬렉션
data class UiState(val users: ImmutableList<User> = persistentListOf())
// implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:...")

왜 List가 불안정인가

List 는 인터페이스다 val list: List<User> 로 선언해도 실제 구현이 ArrayList 일 수 있고, 그 ArrayList 를 다른 곳이 참조해 add() 할 수 있다

  • 컴파일러는 "안 바뀐다" 를 보증할 수 없다

ImmutableList 는 타입 자체가 불변을 약속한다

약속으로 해결하기

@Immutable          // 모든 프로퍼티가 절대 안 바뀐다고 개발자가 약속한다
data class UiState(val users: List<User>)

@Stable             // 바뀔 수 있지만 바뀌면 Compose 에 알린다고 약속한다
class MyStateHolder { var value by mutableStateOf(0) }

약속을 어기면 화면이 갱신되지 않는다 — 컴파일러가 검사하지 않는다

  • 정말 불변일 때만 붙인다

Strong Skipping

  • Kotlin 2.0 의 Compose 컴파일러부터 기본 활성화

  • 불안정한 파라미터도 인스턴스 동일성(===) 으로 비교해 건너뛴다

  • 람다가 자동으로 remember 된다

    • onClick = { vm.select(id) } 를 매번 새로 만들어도 재구성이 덜 발생한다
  • 안정성 문제가 상당히 완화됐지만, 여전히

    • 값이 같아도 인스턴스가 다르면 다시 그린다
    • 불변 컬렉션은 여전히 유효한 최적화다

진단한다

// build.gradle.kts — 컴파일러 리포트를 켠다
composeCompiler {
    reportsDestination = layout.buildDirectory.dir("compose_reports")
}

*생성된 -classes.txt 에

  • stable class UiState / unstable class UiState
  • restartable skippable fun MyScreen / restartable fun MyScreen 가 찍힌다 — "skippable 이 아니면" 그게 원인이다

면접 함정

  • "data class면 안정" → 프로퍼티 타입이 전부 안정이어야 한다. List 하나면 불안정이다.
  • "Strong Skipping이 켜졌으니 신경 안 써도 된다" → 인스턴스가 매번 새로 만들어지면 여전히 다시 그린다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — Compose 성능·안정성 — recomposition·@Stable·skippable