훌륭한 개발자가 되기 위하여
Jetpack Compose의 동작 구조 본문
Jetpack Compose는 크게 Compose Compiler, Compose Runtime, Compose UI 세 가지 주요 계층으로 이루어짐
Compose Compiler
Kotlin으로 작성된 선언적 UI 코드를 Jetpack Compose가 실행할 수 있는 최적화된 코드로 변환하는 역할
- 컴파일 타임에 @Composable 함수 처리, 필요한 UI 업데이트 및 recomposition 로직 생성
- 효율적인 코드 생성 보장, 상태 관리, 코드 최적화, 람다 리프팅 같은 기능 지원
※ 람다 리프팅 : Composable 내부의 람다를 외부 함수로 추출하여, 재사용 가능하고 식별 가능한 형태로 만드는 컴파일러 최적화 기법
👉 함수 오브젝트 재생성을 피하고 불필요한 recomposition 방지, 성능 최적화, 람다 identity 보장 가능
Compose Runtime
Recomposition 및 상태 관리를 지원하는데 필요한 핵심 기능 제공
- 변경 가능한 상태 (mutable state)를 처리하고, 스냅샷을 관리, 상태가 변경될 때마다 UI 업데이트 트리거
- 상태 변경에 따라 올바른 UI 컴포넌트가 동적으로 업데이트되도록 보장
- Side effect 관리, remember를 사용한 상태 보존, 상태 변경 시 recomposition 트리거, compositionLocal을 사용한 컨텍스트별 데이터 저장, UI 계층 구조를 효율적으로 생성하기 위한 compose 레이아웃 노드 구축 등 포함됨.
- Slot Table : 컴포지션 상태를 메모이징 하는 방식으로 작동
※ CompositionLocal : 컴포지션 트리 전체에 값을 주입하고, 트리 아래에 있는 모든 자식 컴포저블에게 그 값을 읽을 수 있게 하는 매커니즘
※ 슬롯 테이블 : 컴포지션 단계 중 UI 컴포넌트의 상태 저장 및 관리하기 위해 Jetpack Compose에서 사용되는 자료 구조
👉 연관된 상태를 효율적으로 추적하여 상태 변경에 영향을 받는 요소만 업데이트함으로써 최적화된 recomposition을 가능하게 함.
Compose UI
- 애플리케이션 구축을 위한 고수준 컴포넌트 및 UI 위젯 제공
- 텍스트, 버튼, 레이아웃 컨테이너와 같은 기본 요소와 커스텀 UI 컴포넌트 구축을 위한 상위 호환의 API를 포함.
- Compose Runtime에 의해 처리되는 Compose 레이아웃 트리 구축을 단순화하도록 설계된 광범위한 컴포넌트 제공
Compose Phase
Jetpack Compose는 UI를 화면에 그릴 때 Composition -> Layout -> Drawing 세 가지 단계로 나누어진 렌더링 파이프라인을 따른다. 각 단계는 독립적으로 동작하며, Compose의 선언적 UI 모델이 효율적으로 작동하도록 서로 긴밀하게 연결되어 있다.
각 단계는 순차적으로 진행되지만, 상태 업데이트 발생 시 Composition과 Layout만 다시 실행되고, Drawing은 필요한 부분만 다시 그려진다.

Composition
@Composable 함수를 실행하고 UI 트리를 구축하여 컴포저블 함수에 대한 설명을 생성하는 역할
초기 UI 구조를 구축, UI 트리 업데이트, Slot Table에 컴포저블 간의 관계를 기록
✔️ 주요 역할
- @Composable 함수 실행
- UI 트리 생성 및 변경 사항 업데이트
- Slot Table에 컴포저블 구조와 상태 연결 정보 기록
- 상태 변경 발생시 어떤 UI가 영향을 받았는지 추적하여 필요한 경우 Recomposition 수행
"UI 선언을 실제 내부 구성으로 변환하는 단계" 👉 UI의 청사진 제공
Layout
Compostion에서 구성된 UI 트리를 기반으로 각 Composable의 크기 측정과 위치 배치를 결정하는 과정
✔️ 주요 역할
- UI 컴포넌트 크기 측정
- 부모 👉 자식 방향으로 제약 조건 전달
- 자식은 해당 제약 아래에서 자신의 크기를 산출한 뒤 부모에게 보고
- 자식의 실제 너비·높이 계산
- 계산된 값을 기반으로 부모 내 배치
Drawing
Layout까지 완료된 UI를 실제 화면에 렌더링하는 과정
Skia 그래픽 엔진을 사용하여 렌더링 제공
✔️ 주요 역할
- UI 컴포넌트 시각 렌더링
- 화면에 UI 컴포넌트 그리기
- 커스텀 드로잉(Canvas API)을 통한 직접 구현 가능
Jetpack Compose에서 선언적 UI의 주요 특징
1. State-Driven UI(상태 기반 관리)
Jetpack Compose는 상태 관리 시스템이 라이브러리 자체에 내장되어 있다.
Compose Runtime은 각 UI 컴포넌트의 상태를 추적하고 상태가 변경될 때 UI를 자동으로 업데이트한다.(Recomposition)
👉 상태가 변경될 때마다 프레임워크는 recomposition을 트리거하여 영향을 받는 UI 컴포넌트만 업데이트하고 최신 데이터를 반영하여 뷰를 수동적으로 관리할 필요가 없다.
2. 컴포넌트를 함수 또는 클래스로 정의
Compose에서는 UI를 @Composable 어노테이션을 함수에 붙임으로써 재사용 가능한 UI 컴포넌트 정의한다.
- UI 요소를 함수로 표현하므로 재사용·조합성 향상
- 다른 함수와 결합하여 모듈식이며 확장 가능한 구조를 만들 수 있음
- 함수형 접근 방식으로 가독성과 유지보수 향상
3. 직접적인 데이터 바인딩
Text(text = uiState.message)
Compose에서는 UI 함수의 매개변수에 상태 값을 직접 전달한다.
데이터가 Compose 함수로 직접 전달되므로 코드 흐름이 단순해지고 구조가 명확해진다.
4. 컴포넌트 멱등성(Component Idempotence)
✏️멱등성 : 동일한 입력값을 가진 함수는 몇 번 호출되더라도 동일한 결과를 반환해야 한다.
👉 Composable 함수는 스크롤, 애니메이션, 상태 변경 등의 상황에서 예측 불가능한 횟수로 재호출 되기 때문에 부수효과(side-effect)가 있으면 UI가 불안정해지기 때문에 멱등성이 핵심 원칙이다.
✔️ Compose가 멱등성을 보장하는 방법
- 내부적으로 Slot Table을 이용해 UI 구조 기록
- Parameter 변경 여부를 추적하여 변경이 없으면 composable을 실행하지 않는(Skip) 최적화 수행
선언적 UI vs 명령형(XML) UI
XML 기반 명령형 UI
개발자가 UI 업데이트 동작을 직접 명령해야 함.
👉 무엇을 어떻게 변경할지를 개발자가 직접 지시해야 하는 방식
💥단점
- UI 변경 시 모든 영향을 수동으로 관리해야 함
- 여러 View가 얽혀 있는 경우 UI / state 불일치 문제 빈번
- Fragment / Activity LifeCycle과 강하게 결합됨
- 복잡한 화면일수록 보일러플레이트 증가
Jetpack Compose
현재 상태를 기반으로 자동으로 그려짐
상태가 변경되면 필요한 부분만 다시 그려줌(Recomposition)
👉 UI가 어떻게 그려져야 하는지만 선언하면 됨
👍이점
- Composable 은 함수이므로 단위 테스트 가능
- UI 로직과 비즈니스 로직 분리가 용이
- Fragment / Activity LifeCycle과 의존도 감소
- 전체적인 코드량 감소, 구조 간결
참조 문서
https://developer.android.com/develop/ui/compose/phases
https://developer.android.com/develop/ui/compose/mental-model#recomposition
https://developer.android.com/develop/ui/compose/why-adopt?hl=ko
'안드로이드' 카테고리의 다른 글
| [Jetpack Compose] State와 Remember란? (0) | 2025.08.06 |
|---|---|
| Jetpack Compose의 안정성(Stability) (0) | 2025.07.27 |
| 네트워크 상태 읽기 (0) | 2025.02.20 |
| Jetpack Compose Stability (1) | 2025.01.20 |
| 공유 상태를 사용하는 코루틴의 문제와 해결책, 코루틴의 동작 방식 (0) | 2025.01.15 |