훌륭한 개발자가 되기 위하여
Jetpack Compose의 안정성(Stability) 본문
안정성(Stability)란?
클래스나 타입이 동일한 매개변수 입력값에 대해 일관된 결과를 생성하도록 보장하는 속성이다.
👉 안정된 클래스나 함수는 recomposition이 여러 번 발생해도 의도와 다르게 동작이 변경되지 않도록 보장한다.
❓ Compose에서 안정성이 중요한 이유
composable 컴파일러는 컴포저블 함수의 매개변수를 검사하고 stable 또는 unstable로 분류한다.
이 정보는 Compose Runtime이 매개변수의 입력값 변경에 따라 컴포저블 함수를 다시 랜더링할지 여부를 결정하는데 사용되므로 효율적으로 recomposition을 관리하여 앱 성능 향상에 기여를 한다.
✅ Stable 파라미터일 때 특징
- 참조가 같으면 변경되지 않았다고 판단
즉, stable 객체가 들어오면 equals 체크를 생략하고 참조 비교(pointer compare)로 충분. - 만약 stable 객체가 내부적으로 값 변화 가능하면
→ Compose runtime이 자체 추적(State object)을 이용해 변화 감지.
👉 Stable 객체는 "값이 바뀌었는지 Compose가 신뢰하고 판단할 수 있는 타입"이다.
✔️ Stable로 간주되는 대표적인 경우
- String을 포함한 원시 타입이 읽기 전용인 val로 정의된 경우
- Int, Float, Boolean, String 등
- 값을 캡처하지 않는 람다 표현식
- ex) (Int) -> String 과 같은 함수 타입
- 외부 상태를 캡처하지 않는 람다는 동작이 예측 가능하므로 Stable
val lambdaFunc: (Int) -> String
- 클래스, val 이거나 immutable하고 stable 한 public 속성을 가진 데이터 클래스
data class User(
val id: Int,
val name: String
)
- 안정성 어노테이션이 명시적으로 붙어있는 클래스
- @Stable
- @Immutable
✔️ Unstable 간주되는 대표적인 경우
- 인터페이스 및 추상 클래스
- List, Map, Any와 같은 인터페이스는 컴파일 시점에 구현체의 종류를 보장할 수 없기 때문
- 가변적인 프로퍼티를 가진 클래스
- 하나 이상의 가변적이거나 본질적으로 불안정하다고 간주되는 타입의 public 프로퍼티를 단 하나라도 포함하는 클래스
data class UnstableUser(
val id: Int,
var name: String
)
🥸 Composable 함수 추론하기
성능을 최적화하기 위해 컴파일러는 컴포저블 함수를 Restartable, Skippable, Moveable, Replaceable과 같은 유형으로 분류한다.
Restartable
- recomposition 프로세스의 기본 단위 ➡️ 매개변수 입력값 or 상태가 변경되면 compose 런타임은 UI를 업데이트 하기 위해 recomposition을 위해 함수를 재호출함
- 대부분의 컴포저블 함수는 기본적으로 Restartable로 간주
Skippable
- 스마트 recomposition에 의해 활성화된 특정 조건 하에서 recomposition 스킵할 수 있음 ➡️성능 향상에 중요
- 컴포저블 함수는 restartable이면서 동시에 skippable일 수 있음 ➡️ 필요할 때 recomposition 수행할 수 있지만 특정 조건이 허용되면 건너뛸 수 있음
Smart Recomposition이란?
컴파일러에서 제공하는 안정성 정보를 기반으로 불필요한 업데이트를 선택적으로 건너뛰어 UI 성능과 응답성을 최적화하는 매커니즘이다.
동작 방식
- 새로운 매개변수 입력값이 컴포저블 함수에 전달될 때마다 Compose는 클래스의 equals() 매서드를 이용하여 이전 값과 비교한다.
- Stable 매개변수
- 입력값 동일 ➡️ equals()가 true 반환 ➡️ recomposition Skip
- 입력값 바뀜 ➡️ equals()가 false 반환 ➡️ recomposition 트리거함
- Unstable 매개변수
- Compose는 입력값 변경 여부에 관계없이 항상 recomposition 트리거함
안정성 어노테이션
@Immutable
클래스 내의 모든 속성이 컴파일러에 의해 변경 불가능한 것으로 처리되도록 보장한다.
@Immutable
data class User(
val id: Int,
val items: List<String>
)
👉 List는 기본적으로 Unstable이지만 @Immutable 때문에 명시적으로 stable 유형으로 만들 수 있음
💥유의사항
- 모든 필드가 val
- 필드 타입도 모두 immutable
@Stable
속성이 immutable 하거나 recomposition에 영향을 미치지 않는 제어된 가변성을 가진 클래스에 사용
👉 읽기 전용보다는 강력하지만 @Immutable 어노테이션 보다는 덜 엄격함
👉 클래스 자체는 stable로 간주되지만 해당 속성은 여전히 변경될 수 있음
@Stable
class PlayerState {
var position by mutableFloatStateOf(0f)
var isPlaying by mutableStateOf(false)
}
@Immutable ⚔️ @Stable
| 구분 | @Immutable | @Stable |
| 불변성 | 완전 불변 | 제어된 가변성 |
| 프로퍼티 | 모두 Immutable | Mutable 허용 |
| 사용 사례 | DTO, Model | UI State |
| Recomposition | 완전히 Skip 가능 | 상태 변경시 트리거 |
참고자료
https://developer.android.com/develop/ui/compose/performance/stability?hl=ko
'안드로이드' 카테고리의 다른 글
| [Jetpack Compose] State Hoisting이란? (0) | 2025.08.09 |
|---|---|
| [Jetpack Compose] State와 Remember란? (0) | 2025.08.06 |
| Jetpack Compose의 동작 구조 (0) | 2025.07.26 |
| 네트워크 상태 읽기 (0) | 2025.02.20 |
| Jetpack Compose Stability (1) | 2025.01.20 |