- Composable
- Composable은 Jetpack Compose에서 선언형 UI를 구성하기 위한 기본 단위 함수
- @Composable 어노테이션이 붙은 함수로, UI의 상태를 기반으로 UI를 동적으로 렌더링한다.
- Composition
- Composition은 Composable 함수들이 화면에 그려지기 위해 수행되는 전체 과정을 의미한다.
- Recomposition은 Composition 후 상태 변경에 의해 필요한 부분만 다시 그려지는 과정이다.
- Composition은 처음 1회 수행되고, 이후 변경된 데이터에 따라 Recomposition이 발생한다.
- Recomposition 단계
- Compose는 이전 Composition의 결과를 재활용하여 UI 일부만 업데이트한다.
- Composable 함수는 매개변수 변화를 감지하여 필요한 부분만 Recomposition을 트리거한다.
- 매개변수와 상태 관리
- 매개변수가 없는 함수는 상태 변화를 감지하지 못해 Recomposition이 트리거되지 않는다.
@Composable
fun LoginScreen(showError: Boolean) {
if (showError) {
LoginError() // showError가 true일 때만 호출됨
}
LoginInput() // 항상 호출됨
}
@Composable
fun LoginInput() { /* ... */}
- 반복문에서 상태 관리
- Compose는 콜 사이트와 실행 순서를 기준으로 상태를 관리한다.
- 반복문에서 동일한 콜 사이트가 여러 번 사용될 경우, 각 호출을 고유하게 식별해야 한다.
- Key 제공
- key를 사용하여 각 Composable의 고유성을 지정하면 상태 유지 및 불필요한 Recomposition을 방지할 수 있다.
@Composable
fun MoviesScreen(movies: List<Movie) {
Column {
for (movie in movies) {
key(movie.id) {
MovieOverview(movie)
}
}
}
}
@Composable
fun MoviesScreen(movies: List<Movie) {
LazyColumn {
items(movies, key = { movie -> movie.id }) { movie ->
MovieOverview(movie)
}
}
}
Jetpack Compose의 렌더링 단계는 아래와 같다.
- Data ➡️ Composition ➡️ Layout ➡️ Drawing ➡️ UI
- Data → 데이터 상태를 기반으로 Compose 트리가 생성된다.
- Composition : 무엇을 화면에 보여줄지 결정한다.
- Layout : 각 요소의 크기와 위치를 계산한다.
- Drawing : UI를 화면에 렌더링한다.
- Compose와 View 시스템의 차이
- XML 기반 View 시스템은 중첩된 뷰가 많을수록 비효율적이다.
- Compose는 UI 트리를 단 한 번만 탐색하여 Composition, Layout, Drawing을 수행하므로 효율적이다.
- Smart Recomposition: 성능 향상을 위해 변경된 부분만 다시 그리기
- Composable 함수의 콜사이트나 실행순서가 같다면 Recomposition이 필요한지 아닌지 Compose가 어떻게 알 수 있을까?
- 매개변수에 따라 Compose가 Recompotion이 필요한지 판단할 수 있음
Compose는 매개변수의 안정성 유형을 바탕으로 Recomposition을 최적화한다.
- Immutable
- 변경 불가 데이터(String, Int, Float 등)
- 안정적이며 Recomposition을 트리거하지 않는다.
- Stable
- 변경 가능한 데이터
- Compose에서 변경을 추적하여 필요한 경우만 Recomposition을 트리거한다.
- Unstable
- 변경 가능한 데이터
- Compose에서 변경을 추적할 수 없어 항상 Recomposition이 발생한다.
- 💡
- 가능한 모든 매개변수를 Stable 또는 Immutable로 유지하여 성능 최적화를 달성해야 한다.
- @Stable 어노테이션을 활용해 직접 클래스를 안정적인 데이터로 선언할 수 있다.