성장기록지
Compose Structure 정리하기 (compiler, Runtime, UI) 본문
Compose Structure
컴포즈는 다음과 같은 구조로 이루어져있다.
여기서 Compiler와 Runtime은 Compose compiler와 Compose Runtime이다.
Compose Compiler
Compose를 사용해보신 분이라면 Kotlin 함수에 @Composable 어노테이션을 붙인 함수는
Composable 함수로 변환되는 것은 모두가 알고 있는 사실이다.
그렇다면 코틀린에서 어노테이션은 기본적으로 어떻게 처리되어질까? kapt나 ksp를 통해 이루어진다.
하지만 Compose는 일반적인 어노테이션 프로세서로 처리하지 않고, compose 컴파일러를 활용한다.
Compose 컴파일러는 순수 코틀린으로 작성된 코틀린 컴파일러의 플러그인이다.
자바로 Compose를 사용할 수 없는 이유이기도 하다.
코틀린에는 자체적으로 어노테이션 프로세싱을 지원하지 않기 때문에, 자바의 어노테이션 프로세서를 이용해야하고,
그걸 이루는 것이 kapt이다. 하지만 이때문에 자바 Stub이 생성되고, 절차가 늘어나기 때문에 컴파일 시간이 길어지게된다.
(자바 stub은 Kotlin 코드가 Java 코드처럼 보이도록 생성된 임시 코드이다)
반면, Compose 컴파일러가 코틀린 컴파일러의 플러그인이어서 얻을 수 있는 이점은 다음과 같다고 한다..
- Compose 컴파일 과정이 코틀린 컴파일 과정에 임베드되어 내부적인 정보까지 깊게 액세스 할 수 있으며, 이는 전체 프로세스 속도를 높일 수 있다.
- Compose 컴파일러의 프론트엔드 단계에서 경고 및 오류를 보고가 가능하다. 즉, 정적 분석을 수행하여 개발자는 실제로 빌드하지 않고도 코딩하는 동안 실시간으로 피드백 받을 수 있다.
- IR(Intermediate Representation)을 조정함으로써 (새 코드를 추가하는 것뿐만 아니라) 소스 코드를 자유롭게 조작할 수 있다. (코틀린은 멀티플랫폼을 지원한다). 이는 Compose 컴파일러가 Composable 함수를 런타임에서 지원할 수 있도록 변환하는 능력을 제공합니다
어려운 설명이므로 하나씩 해석해보겠다.
정적 분석(Static Analysis) : 소프트웨어 개발 과정에서 코드 실행 없이 소스 코드를 분석하여 오류, 결함, 스타일 문제, 또는 성능 개선 사항을 찾아내는 방법이다.
IR(Intermediate Representation)?
코틀린 컴파일러는 크게 프론트엔드 컴파일러와 백엔드 컴파일러 두개로 나뉜다.
이때 프론트엔드에서 코드 분석 후 출력하는 산출물이 IR이다 ( VM의 바이트코드와 유사한 기능을 한다고 생각하면된다.)
이러한 IR을 각 대상 플랫폼에 맞게 컴파일을 해야하는데, 이러한 역할을 해주는 것이 백엔드 컴파일러이다.
각 플랫폼(JS, JVM, Native)의 백엔드 컴파일러가 존재하고, 그래서 멀티플랫폼 컴파일러 확장을 더욱 쉽게 구현할 수 있다는 장점이 있다.
따라서 IR을 조정한다는 것은, 컴파일러 플러그인이 IR에 접근할 수 있고, 개발자 몰래 매개변수를 삽입하여 교체하고,
새 매개변수를 추가하고, 코드를 “커밋“하기 전에 코드 구조를 재구성할 수 있음을 의미한다.
이는 Kotlin 컴파일러의 백엔드(backend) 단계에서 발생하고,
이러한 작업들은 Compose가 각 Composable 호출에 대해 암시적인 추가 매개변수인 Composer를 “주입“하기 위해 수행된다.
Compose Compiler의 내부 구조
- Compose Compiler는 Frontend와 Backend가 나뉘어있다.
- Frontend Compiler에서 FIR(Frontend Intermediate Representation)이 생성된다.
- FIR을 활용해 정적 분석, 타입검사, 선언 검사등이 이루어진다.
- 이후 백엔드로 이동하며 IR로 변경되어진다.
- IR을 Lowering 하는 과정에서 최종적으로 stable, unstable, skippable같은 특성이 할당된다!
- (실제론 훨씬 많은 일들이 일어나지만, 흐름을 요약하자면 이정도로 볼 수 있다.)
구조도를 보자면 다음과 같다.
인터넷에 다른 구조도가 떠도는데 그건 좀 틀린거같다..
요약하자면, 컴포저블 함수에 대한 정보를 해석해서 Compose Runtime에 전달해준다.
그리고 Stable, Skippable 같은 특성은 백엔드 컴파일러에서 최종적으로 결정이 된다.(IR Lowering)
Compose Runtime
- Compose 모델 및 상태 관리의 초석 역할을 담당한다.
- Gap Buffer라는 데이터 구조에서 파생된 Slot Table을 사용하여 상태 저장 등 메모리를 운용한다
- 리컴포지션이나 remember함수와 같은 동작을 수행한다.
- Composable 함수에 대한 실질적인 생명주기 관리 및 UI에 대한 정보를 담고 있는 메모리 표현이다
gap buffer란?
메모장 등의 텍스트 입력기에서 커서 근처에 자유공간을 미리 할당해두어 빠른 시간에 삽입, 삭제가 가능한 동적 배열. 아래 링크에 자세히 설명되어 있다.
https://ducorner.tistory.com/12
Slot table이란?
UI 트리를 효율적으로 저장하기 위한 내부 데이터 구조
- composable 함수 호출 위치
- 상태
- Key와 Group
Slot table 예시 설명
아래 그림은 Composer의 Composition에 대한 정보들이 저장되는 Slot table이다.
현재 Slot table은 모두 비워져있다. 즉, 전체가 Gap이라고 볼 수 있다.
실행중인 Composable 계층은 이 자료구조에 접근하여 내용을 삽입할 수 있다.
여기서 중간에 데이터 변경으로 인해 Recomposition이 일어나면 어떻게 될까?
우선, 커서를 배열의 맨 위로 재설정한 다음 다시 실행을 수행한다.
그리고 다시 데이터를 확인하고 아무것도 하지 않거나 값을 업데이트를 한다.
그럼 중간에 UI가 하나 추가된다면 어떻게될까요? (if 등에 의해)
이 경우에는 커서를 변경되어야하는 위치로 옮긴 뒤 갭이 해당 위치로 이동한다.
그리고 갭에 삽입한다.
Gap buffer를 사용하는 이유?
- 갭을 이동하는 시간을 제외한 모든 작업(가져오기, 이동, 삽입, 삭제)의
- **상수 시간 연산이 O(1)** 이기 때문이다. (간격을 이동하는데에는 O(n)이 소요)
- Compose 팀은 UI 구조 자체가 많이 변경되지 않을 것이고,데이터의 변경으로 UI가 변경되는 경우가 많을 것이라고 가정했다.
- 그리고 데이터 변경으로 UI가 변경되는 연산은 빠른시간에 연산될 수 있는 전략을 취하기 위해 Gap buffer가 선택되었다.
Compose UI
- 개발자가 Composable 함수를 통해 레이아웃을 생성할 수 있도록 여러 UI 라이브러리를 통해 컴포넌트 제공
- 각 컴포넌트는 LayoutNode를 생성하고, 결과적으로 Compose 레이아웃 트리 구성을 용이하게 함
- Compose Runtime에 의하여 소비되는 것이 원칙. (관리된다는 뜻으로 이해하면 된다.)
Layout Tree 예시
@Composable
fun MyLayout() {
Column(modifier = Modifier.fillMaxSize()) {
Text(text = "Hello, World!", modifier = Modifier.align(Alignment.CenterHorizontally))
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = { /* Do something */ }) {
Text("Click Me")
}
}
}
https://www.youtube.com/watch?v=bDyhdJk3uZM
https://ducorner.tistory.com/11
'안드로이드 > 안드로이드 지식' 카테고리의 다른 글
Kotlin Flow란? (0) | 2025.01.24 |
---|---|
Kotlin Data Stream (Sequence, Hot, Cold Stream) (0) | 2025.01.23 |
Compose Stability 자세히 알아보기! (0) | 2025.01.21 |
Compose Stability란? (0) | 2025.01.20 |
안드로이드 MVVM 아키텍쳐란? (0) | 2025.01.13 |