05. 컴포즈 마법사 행사 후기

간단 후기

Image 행사 굿즈

1부는 Compose의 Recomposition과 관련된 내용을 전반적으로 소개해주셨다

전반부는 ComposeRestartable하지 못할 때와 그럴 경우 Restartable한 경우와의 차이를 앱 시연을 통해 보여주었다.

후반부는 Stable, Immutable 어노테이션을 이용한 Recomposition 생략을 보여주었다.

전반부의 내용은 최근에 Compose Internals라는 책을 읽고 있는데, 여기서 나오는 내용이 조금 담겨 있었던 것 같다.
특히 Restartable하지 않은 Composable함수에서 Recomposition이 생기게 되었을 때, 이 Composable을 갱신시키기 위해 Restartable한 부모 Composable함수 까지 거슬러 올라가게 되기에 부모까지 불필요한 Recomposition이 생기게 되는 것을 경계해야 한다는 내용이었다.

특히 이것에 더해서 Composable함수가 Restartable할 경우 State를 읽는 시점이 지연되게 되면, State를 읽기 시작하는 범위의 ComposableRecomposition이 되어서 State 자체가 상단에 있더라도 읽기 시작하는 곳에서만 Recomposition이 일어나는 Donut Hole Skipping최적화도 있기 때문에, Restartable 가능한 Composable로 만드는 것이 중요함을 다시 알 수 있었던 것 같다.

아래의 코드에서 count를 선언한건 Donut Composable 함수 위치이지만 이를 정작 읽는 것은 Button 내부의 ScopeText에서 읽게 된다.
그렇기에 Donut 부모는 Recomposition 되지 않고 Button 내부만 다시 실행되게 된다.

// file: "DonutHoleSkipping.kt"
@Composable
fun Donut() {
    // Donut Recomposition Scope
    var count by remember { mutableStateOf(0) }
    println("부모 recomposition")
  
    // Column은 Inline이라 Recomposition Scope가 아니다
    Column(
      modifier = Modifier.background(Color.Gray),
    ) {
        println("Column recomposition")
        Button(onClick = { count++ }) { 
          // Button 내부 Recomposition Scope
          println("Button Recomposition Scope")
          Text("클릭 수 : $count") 
        }
    }
}

후반부는 부모에서 Recomposition이 일어나게 되면 자식 Composable도 기본적으로 Recomposition이 일어나게 되는데 이 때 매개변수가 동일하다고 판단되면 Recomposition이 SKip될 수 있다.

하지만 매개변수의 TypeUnstable하다고 Compiler가 판단한다면, 매개변수가 동일하다고 판단을 내릴 수가 없기 때문에 항상 Recomposition이 일어난다.

대표적인 예가 List 같은 것을 매개변수로 받을 때인데, 결론적으로 이를 해결하기 위해서는 ImmutableList, PersistentList를 매개변수로 넘겨주어 CompilerStable하다고 판단할 수 있게 하는 것이고, 매개변수가 data class일 경우 data class의 프로퍼티가 Unstable하게 되면 역시 항상 Recomposition이 일어난다.

다만 Stable하게 사용하는 것이 보장될 경우 @Stable과 같은 어노테이션을 명시적으로 붙혀서 CompilerStable하게 판단하게 할 수 있다.
다만 이 경우 Stable하지 않은 경우에도 @Stable을 붙힐 경우 잘못된 동작을 초래할 수 있기에 주의가 필요하다.
아래의 코드는 성빈님이 설명을 진행하면서 보여주신 일부 코드이다.

// 동일한 매개변수(본 예시에서는 매개변수가 없고, 없다는 동일한 입력이 들어오는 상황) 다른 값을 반환하는 Unstable한 함수이지만, @Stable을 붙혔고, 이러한 잘못된 사용은 잘못된 동작을 야기하게 된다.
@Stable
fun stableCall(): Long = currentTimeMillis()

2부는 Flow에 대해서 다른 분의 발표가 있었다.
다만 아쉽게도 좋아보였던 목차와 다르게, 너무 많이 긴장하셔서 발표 내용을 온전하게 이해하지 못했던 것이 아쉽다.

저렇게 나가서 발표할 역량이 되길 바라며 이번 후기를 마친다.


© 2025. Na2te All rights reserved.