해당 글은 https://newsletter.jorgecastillo.dev/p/the-composable-node-tree 번역 글입니다.
Jetpack Compose 노트 트리가 빌드되고, 업데이트 되고, 어떤식으로 변경사항이 적용되고 다양한 노드 타입들에 알아보겠습니다.
컴포저블 노드 트리가 어떤식으로 표현되고 생성되고 유지 되는지에 대한 글은 인터넷에 많지 않습니다. 여기서 알아보도록 하겠습니다.
The in-memory node tree (메모리 내의 노드 트리)
Compose 는 Slot table 이라고 불리는 인 메모리 자료구조에 UI 와 관련있는 정보들을 저장해서 모든 Composable 호출을 추적합니다. 매번 재구성(Recomposition) 이 발생하면 업데이트 하는 인메모리 트리 노드를 가지고 있습니다. 해당 포스트의 후반부에서 이부분에 집중할 예정입니다.
트리는 보통 노드 트리 또는 인메모리 트리라고 자주 부릅니다. 그리고 컴포즈 런타임에서는 Composition 클래스로 표현됩니다.
Composition 이 진행중 Composable 함수를 최초로 실행하면 트리 노드를 만듭니다. 매번 재실행될 때마다(재구성, Recomposition) 트리 노드에 새로운 값을 제공하고 업데이트 합니다.
Why to maintain an in-memory tree ? (왜 인-메모리 트리 형식으로 유지할까요?)
컴포즈 런타임은 플랫폼에 의존성이 있지 않습니다. 트리 노드가 존재하는지 , 어떤식으로 최적화 할지, 어떤식으로 순회하는지만 알고 있습니다. 따라서 트리 노드에서 발생하는 액션들은 Compose UI 같은 클라이언트 라이브러리에 위임합니다. (어떤 플랫폼이든 컴포즈 트리 노드를 표현만 할 수 있다면 만들 수 있겠네요.)
이외에도 컴포즈 UI 는 안드로이드, 데스크탑 등 멀티 플랫폼 입니다. 그리고 트리 노드를 만들고 업데이트 할 수 있습니다. 따라서 플랫폼에 영향이 없는 인 메모리에서 표현 가능한 구조를 가지고 있어야 합니다. 그래야 여러 플랫폼에서 표현 가능한 특정 트리로 번역되고 표현될 수 있습니다.
Creating a Composition (컴포지션 만들기)
Composition 은 ComposeView.SetContent 를 사용해서 매번 생성할 수 있습니다. 맨처음 만들어질 때 Composition.setContent(content) 를 호출하고 Composable 호출들이 content 람다 함수 내에서 실행되어 Composition 의 일부가 됩니다.
Composition 은 Compose runtime 의 한 부분으로 역시나 특정 플랫폼에 구애받지 않습니다. 따라서 트리 노드를 화면에 그려내거나, 키보드를 연결하고, 접근성 이벤트 등 여러 이벤트를 안드로이드 플랫폼과 연결해야 합니다. 위의 코드처럼 WrappedComposition 을 사용해서 Android 와 연결합니다.
아래는 ComposeView.setContent 가 호출되었을 때, WrappedComposition 이 어떤식으로 만들어지는 요약한 다이어그램 입니다.
주관적인 생각 : setContent 가 호출되었을 때 Composition 이 만들어지고 언제 dispose 될지 전략도 가지고 있고, 함수가 시작됨에 따라 Snapshot 상태를 관찰하고 이런 일련의 과정들이 주의 깊게 봐야할 것 같아요. 정말 좋은 자료 같습니다. - Lucas
Composition 이 언제 제거되는지 를 결정하는 것이 ViewCompositionStrategy 인데 만약 궁금하다면 여기에서 살펴볼 수 있습니다
게다가 Composition 이 만들어질 때, 몇가지 일들이 발생합니다.
2개의 suspend 함수가 LaunchedEffect 이내에서 실행 되며(코드가 나와있지 않아서 일반적으로 키보드등을 제어하려면 LaunchedEffect 를 사용하기 때문에 생략한게 아닌가 싶네요), 키보드 제어, 접근성 이벤트 등 안드로이드에 상응하는 기본 요소들(primitives)에 연결되게 됩니다.
Android 기본 요소들과 관계 있는 Composition locals 들은 Composable 트리 아래 노출되어 있기 때문에 같은 Composable 스코프라면 어디서든 접근해서 사용할 수 있습니다. 위에 이미지중 Context, LifecycleOwner, Density 등을 의미합니다.
마지막으로 snapshot 쓰기 상태에 대해 관찰하기 시작하고 (재구성을 해야하니까) content 가 세팅 됩니다.
The list of changes (변경 사항 목록)
Composition 은 Composable 함수가 실행, 재실행 될 때 생성되고 업데이트 된다고 배웠습니다. 사실 조금 다른 부분이 있습니다. 하나 또는 여러 Composable 함수가 실행되면, 트리에 실제로 생성되는 것은 변경 사항 목록 으로 노드를 삽입, 제거, 대체하는 작업은 람다(아래 이미지에 { Add Column } 과 같은 부분을 의미합니다)에서 실행 되고, 또한 람다는 slot table (remember 호출 등) 에 저장 혹은 변경 합니다.
이런 변경은 람다 안에 래핑 되어 있기 때문에 값의 평가가 지연 됩니다. composition 또는 recomposition 이 완료되면 트리에 변경 사항을 적용할 시간 입니다. 이런 식으로 평가를 늦춰서 효율적으로 화면에 표시되도록 합니다.
Applying the change (변경사항 적용하기)
컴포즈 런타임은 Recomposer 을 사용해서 Recomposition 이후 혹은 첫 composition 프로세스가 발생하도록 합니다. 매번 이러한 프로세스들이 완료될 때 마다 Recomposer 은 Composition 에 트리에 지연된 변경사항들을 적용하도록 지시합니다.
변경 사항 적용을 책입을 지는 Entity 는 Applier 라고 부릅니다. Applier 는 Compose.setContent 가 호출될 때 구현되어 전달 됩니다
이를 위해, Composition 은 기본적으로 변경사항을 하나하나 반복하며 실행 합니다. 변경된 람다들이 실행될 때, Applier 에 레퍼런스를 가지고 있기 때문에 Applier 를 사용해서 변경사항을 효율적으로 적용하게 되고 그결과 트리 노드는 삽입되고, 제거되고, 대체, 이동 됩니다.
트리를 최종적으로 업데이트 하기 위해 언급된 모든 작업들은 실제로 Applier 를 통해 노드 자체에 위임 됩니다. (그래야 Compose UI 가 각 플랫폼에 맡게 그려낼 수 있기 때문이겠네요) 컴포즈 런타임은 플랫폼에 구애 받지 않고, 각각의 클라이언트 라이브러리(Compose UI) 가 각각에 맞는 타입을 생성하고 그려낼 수 있도록 합니다.
이외에도 람다의 변경은 slot table 쓰기, 읽기에 대한 레퍼런스를 가지고 있습니다. 스스로 composition 의 현재 상태를 업데이트 하거나 체크해야할 때가 있기 때문입니다. 앞으로는 이에 대해 더 많은 글을 작성할 예정입니다.
Node types in Android(안드로이드에서 노드 형태들)
가령 UI 트리에 레이아웃만 존재한다고 가정하면, LayoutNode 트리 하나가 생성됩니다. LayoutNode 는 UI 인 메모리의 한 부분을 담당하고 있습니다. LayoutNode 는 자식 노드나 자식 노드의 순서, 자식 노드들을 어떻게 측정하고 배치할지 알고 있습니다.
트리가 업데이트 될 때마다(가령 새로운 자식이 생겼을 때) 루트 레벨에 존재하는 ComposeView 가 무효화 됩니다(invalidated) ComposeView 는 안드로이드의 ViewGroup 이기 때문에 dispatchDraw 가 다시 호출되고 영향이 있는 노드들만 재측정, 레이아웃 재배치, 다시 그리게 됩니다.
그러나 노트 타입 (LayoutNode) 뿐만 아니라 다른 타입들도 존재합니다. Compose UI 는 LayoutNode 와 VNode 두가지 타입을 제공합니다. 트리 노드에서 두가지 모두 발견할 수 있습니다.
VNode 는 인-메모리 벡터 노드로, 벡터 노드들은 트리의 노드로도 표현할 수 있습니다.
보통 UI 는 다양한 노드들을 포함하고 있고, 하나 또는 여러개의 자식 Composition(subcompositions 포함) 이 생성되고 트리에 서로 연결됩니다. composition, subcomposition 각각은 노드 타입을 고정합니다. 이를 통해 서브 트리에 상응하는 다양한 노드 타입들을 도입할 수 있습니다. 다음은 몇가지 레이아웃과 Image composable 이 포함된 벡터로 구성된 UI 트리 예시 입니다.
컴포지션과 하위 컴포지션들을 트리로써 연결하면 변경사항과 여러 컴포지션에서 접근 가능한 CompositionLocal 을 마치 단일 컴포지션에서 제공하는 것처럼 전파할 수도 있습니다.
그럼 20000-
'Android > 번역' 카테고리의 다른 글
[번역] Stateful vs Stateless Composables (0) | 2023.12.30 |
---|---|
[번역] Modifiers: order of precedence (0) | 2023.12.30 |
[번역] The color of Composable functions (1) | 2023.12.30 |
[번역] Effective android - Using Jetpack Compose with MVVM (0) | 2023.12.29 |
[번역] Crash Course on the Android UI Layer Part 2 (2) | 2023.12.29 |