Compose UI 매커니즘
Compose는 데이터를 UI로 변환하기 위해 아래와 같은 세 가지 주요 단계를 통해 UI를 그리는 매커니즘을 사용합니다.

Composable 함수들은 UI를 그리기 위해 각 단계를 매번 수행해야 합니다. 하지만, 동일한 입력일 때 동일한 결과를 계산하는 반복 작업은 불필요합니다.
그래서 Compose에서는 Recomposition에서 이전과 동일한 입력이 보장된다면, 동일한 결과를 계산하는 반복 작업(세 단계의 작업)을 생략합니다.
동일한 입력인지 판단히기
Recomposition은 입력 매개변수가 변경될 떄, 트리거됩니다.
Compose Runtime은 이전 매개변수 값을 기록해 뒀다가, `equals()`를 사용해서 새롭게 변경된 매개변수 값과 비교합니다.
`equals()`는 == 연산자를 사용해서 두 객체의 값이 동일한지 비교하는 메서드입니다.
equals의 한계
하지만, `equals`만으로는 항상 "동일한 결과가 보장된다"고 말할 수는 없습니다.
예를 들어 List와 같은 컬렉션 타입의 경우를 살펴보면,
val list = mutableListOf(1, 2, 3)
이 리스트는 `equals` 기준으로는 내부 요소가 같다면 동일한 값으로 판단됩니다. 하지만 리스트 자체는 mutable 하기 때문에, 같은 인스턴스를 유지한 채 내부 값이 변경될 수 있습니다.
val list = mutableListOf(1, 2, 3)
Composable(list)
list.add(4)
Composable(list)
즉, 겉으로는 동일한 객체처럼 보이지만, 내부 상태는 이미 바뀌어 있을 수 있는 상황이 됩니다. 이러한 이유 때문에 Compose는 다음과 같은 가정을 하지 않습니다.
"매개변수를 비교한 값이 equals()가 true라면, 결과도 동일할 것이다"
Stable 판단
그래서 Compose는 먼저 유형을 안정적(Stable)인지, 아니면 불안정(UnStable)인지부터 구분합니다.
Compose는 타입의 모든 프로퍼티를 하나하나 분석해 결과를 근거로 Stable 판정을 내리지는 않습니다.
대신 다음과 같은 명시적인 규칙을 사용합니다.
- 기본 타입(Int, String 등) 처럼 이미 안정하다고 알려진 타입
- @Immtable, @Stable과 같이 개발자가 안정성을 보장한 타입
그 외의 사용자 정의 타입에 대해서는, 겉보기에는 불변처럼 보이더라도 기본적으로 UnStable로 판단합니다.
data class Contact(val number: Int)
위의 클래스는 모든 프로퍼티가 val이고, Int 역시 불변 타입이지만, Compose는 이 사실을 자동으로 추론하지는 않습니다.
이는 Compose 컴파일러가 실제로 구조를 파악하지 않는다는 것은 아닙니다.
명시적인 안정성 계약
결과론적으로 Compose는 "추론"보다는 "계약"을 선택했습니다.
@Immutable
data class Contact(val number: Int)
이처럼 안정성을 명시적으로 선언했을 때에만, Compose Runtime은 `equals()` 비교 결과를 신뢰하고 불필요한 Recomposition을 안전하게 생략할 수 있습니다.
즉, Compose의 내부 판단은 다음과 같은 순서를 따릅니다.
- 이 타입이 Stable 한가?
- Stable 하다면 -> equals() 결과를 신뢰
- UnStable 하다면 -> equals() 결과와 상관없이 변경 가능성을 가정
정리하며
정리해보면, Compose에서 Recomposition을 생략할 수 있는지는 단순히 equals() 결과만으로 결정되지 않습니다.
Compose Runtime은 먼저 타입의 안정성(Stable 여부) 을 판단하고, 그 결과에 따라 equals() 비교 결과를 신뢰할지 말지를 결정합니다.
- Stable 타입→ equals() 결과를 신뢰하고 Recomposition을 생략할 수 있음
- → 동일한 입력이라면 결과도 동일하다고 가정할 수 있음
- Unstable 타입→ equals()가 true여도 Recomposition을 수행
- → 내부 상태가 바뀌었을 가능성을 배제할 수 없음
이러한 설계는 Compose가 컴파일러의 복잡한 추론에 의존하기보다는,
개발자가 명시적으로 선언한 안정성 계약(contract) 을 신뢰하도록 선택했기 때문입니다.
그래서 Compose에서의 성능 최적화는 “이 값이 지금 변하지 않았는가?”를 맞히는 문제가 아니라,
“이 값은 변하지 않는다고 보장할 수 있는가”를 명확히 표현하는 문제에 가깝습니다.
결국 Compose에서 Stable은 최적화를 위한 힌트가 아니라, 신뢰의 기준이며, @Immutable과 @Stable은 Compose Runtime과 개발자 사이의 약속이라고 볼 수 있습니다.
Reference
Jetpack Compose 단계 | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Jetpack Compose 단계 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 대부분의 다른 UI 도구 키트와 마찬가
developer.android.com
Compose의 안정성 | Jetpack Compose | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Compose의 안정성 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Compose는 유형을 안정적이거나 불안정
developer.android.com
'Android > Compose' 카테고리의 다른 글
| Recomposition과 Compose Navigation를 사용할 때 주의할 점 (1) | 2025.11.20 |
|---|---|
| Compose TextField를 TextFieldState로 사용하는 것에 대해 (0) | 2025.09.09 |
| ViewModel 톺아보기 (1) - ViewModel의 생성과 관리 (0) | 2024.11.18 |
| SavedStateHandle을 통해 Compose Navigation간 데이터 전달하기 (3) | 2024.09.23 |
| Compose의 Side-effects (0) | 2024.08.17 |