본문 바로가기
Android/Compose 숨참고 Deep dive

Compose 도넛홀 스키핑 (Donut-hole skipping)

by DONXUX 2023. 3. 28.

Composition

초기 Composition시 처음으로 Composable을 실행할 때 Composition에서 UI를 기술하기 위해 호출하는 Composable를 추적합니다. 그런 다음에 앱 상태가 변경되면 Recomposition을 예약합니다.

Recomposition은 Compose가 상태 변경사항에 따라 변경될 수 있는 Composable을 다시 실행한 다음 변경사항을 반영하도록 Composition을 업데이트하는 것입니다.

Composition 초기 Composition을 통해서만 생성되고 Recomposition을 통해서만 업데이트될 수 있습니다. Composition을 수정하기 위해서는 Recomposition을 이용해야합니다.

Composition 공식문서를 참고해주세요.

https://developer.android.com/jetpack/compose/lifecycle?hl=ko

 

컴포저블 수명 주기  |  Jetpack Compose  |  Android Developers

컴포저블 수명 주기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 페이지에서는 컴포저블의 수명 주기에 관해 알아보며 Compose에서 컴포저블에 재구성

developer.android.com


🍩 Donut hole?

크리스피 도넛 ^^

그런데 도넛 구멍? 도넛 구멍이 무슨 상관일까요? 이는 Recomposition 작동 방식을 비유하는 표현입니다. 구체적으로 말씀드리자면 Recomposition 대상 범위 내에서, 상태와 입력 값 등 아무것도 변경되지 않은 요소는 건너뛰는 특성Donut-hole skipping 이라고 Compose 개발자들이 Donut-hole caching이라는 용어를 가져와 비유했습니다. 다음 코드를 살펴봅시다.

@Composable
fun Body() {
    var count by remember { mutableStateOf(0) }
    Log.i("Recomposition", "Body recomposition")
    Button(onClick = { ++count }) {
        Text(
            text = count.toString(),
        ).also {
            Log.i("Recomposition", "Text recomposition")
        }
    }.also {
        Log.i("Recomposition", "Button recomposition")

    }
}

 

생성할 때 로그는 이렇게 찍힙니다.

Body recomposition
Text recomposition
Button recomposition

 

여기서 버튼을 누르면 count가 1 증가되고 텍스트 또한 동일하게 1이 증가된 모습을 보일 겁니다. 그렇다면 로그는 어떻게 찍혀있을까요?

Text recomposition

 

생성 때 로그를 제외하고 버튼을 눌렀을 때 로그는 텍스트 단 하나만 찍혀있습니다! 즉, Body, Button은 recomposition이 이루어지지 않았습니다. setContent, Button은 recomposition 과정을 skip했다고 보면 되겠군요.

이처럼 입력 값의 변화가 없는 composable 함수는 recomposition을 하지않고 skip합니다. count를 출력하는 Text만 recomposition이 이루어졌습니다. 

도넛안에 도넛, 그 도넛 안에 또 도넛

Composable을 도넛이라고 생각하면 됩니다. 구멍은 하위 Composable이라 생각하면되는데(여기서 Text는 구멍이 없는 게 정상이지만 편의상 모두 도넛으로 표현하겠습니다), 이는 Recomposition이 skip 될 수도 있다는 것을 의미합니다. 결국, 도넛 구멍안에 도넛이 있고 그 도넛 구멍안에 또 도넛이 있는 형태로 볼 수 있습니다.

 

결론은 도넛홀 스키핑으로 인해 Recomposition이 많이 생략될 수 있습니다. UI 업데이트 비용이 많이 줄어들 것입니다. 즉, 도넛홀 스키핑은 Recomposition 최적화 기법입니다.

 

내부적으로는 어떻게 구현이 되어있을까요? 이는 다음 Compose 내부 구조에 관련된 글에서 뵙겠습니다.

 


Reference

https://developer.android.com/jetpack/compose/lifecycle?hl=ko

https://blog.canopas.com/quick-note-on-jetpack-compose-recomposition-d9af4e3f8e5b

https://www.jetpackcompose.app/articles/donut-hole-skipping-in-jetpack-compose

https://dev.to/zachklipp/scoped-recomposition-jetpack-compose-what-happens-when-state-changes-l78