
리컴포지션의 기본 원리는 Composable 함수의 요소가 변경되면 Jetpack Compose가 해당 요소에 의존하는 모든 함수를 다시 계산하는 것입니다. 이를 통해 변경된 요소만 재계산할 수 있어 성능이 향상됩니다.
예를 들어, 특정 Composable 함수가 ImageView 요소를 사용하여 이미지를 표시한다고 가정해봅시다. 이때 이미지 소스가 변경되면 해당 함수는 다시 계산됩니다. 하지만 이 함수는 ImageView 요소의 변경 사항에만 반응하며, 전체 UI를 다시 계산하지 않습니다.
Jetpack compose로 선언형UI를 처음 접하는 사람은 큰 감흥이 없을 수 있겠지만, Flutter에선 이와같은 Recomposition이 없다보니 불필요한 UI를 다시 그리게 되는 눈물나는 상황을 볼 수 있다.
(물론 Flutter도 Consumer를 통해 필요한 UI만 그리게 할 순 있지만, 일일히 이를 선언해주는 걸 보면 코드가 지저분하게 보이긴 하다.)
효율성에 대해선 위에도 잠깐 언급했으니 생략하고, 복잡성의 경우 중간에 XML없이 Kotlin 코드만으로 설계하며, 일부 컴포넌트는 코드 자체도 확 줄어든 것도 존재한다. (예: LazyColumn, LazyRow 등) Composable 함수는 각각 작은 단위로 나눠서 선언할 수 있는데, 이와같이 나눠놓은 Composable 함수는 다른 화면에서 동일하게 필요한 경우 쉽게 재사용 할 수 있다는 장점도 잇다.
불필요한 Recomposition이 발생하여 성능을 저하시킨다는데에 의문을 가지는 사람도 있을 수 있다.
분명 Recomposition은 변경된 부분만 다시 그리지 않나? 어떻게 성능 저하가 발생하지?
아래 코드를 예시로 들어보겠다.
@Composable
fun MyScreen() {
var count by remember { mutableStateOf(0) }
Column {
Button(onClick = { count++ }) {
Text("Increase Count")
}
MyComposable(title = "Hello", count = count)
}
}
@Composable
fun MyComposable(var title: String, count: Int) {
Log.d("Recomposition", "MyComposable recomposed!")
Column {
Text("Title: $title")
Text("Count: $count")
}
}
위의 코드에서 버튼을 누르면, 변해야 하는건 count뿐이어야 한다.
하지만 실제로는 Text("Title: $title")도 그리고 있다. 위와같은 케이스는 var로 선언했을 때 새로운 값이 할당된 것으로 인식되어 Recomposition을 하게되는데, 이와같이 불필요한 Recomposition이 발생할 수 있고, 이는 곧 앱의 성능저하로 이어지니 주의하도록 하자.