Recomposition이란?
Jetpack Compose에서 특정 이벤트로 인해 UI의 데이터가 변경되면, 변경된 데이터를 반영하여 해당 Composable
함수가 다시 실행되어 UI가 새롭게 그려지는 과정을 재구성(recomposition)이라고 한다.
간단한 예제를 통해 UI가 어떻게 재구성되는지 알아보자.
@Composable
fun ClickCounter() {
var clicks by remember { mutableStateOf(0) }
Button(onClick = { clicks++ }) {
Text("I've been clicked $clicks times")
}
}
1. 클릭된 횟수를 clicks 변수로 선언
2. Button을 클릭하면 clicks가 1 증가되어 상태가 변경
3. clicks 값이 바뀔 때마다 ClickCounter() 함수가 자동으로 다시 실행되어 UI가 갱신된다.
Recomposition의 특징
더 많은 특징이 존재하지만, 우선 3가지만 소개하려 한다.
1. Composable 함수의 변수가 자주 바뀌면, 진행 중인 재구성이 취소되고 최신 값으로 다시 재구성이 시작된다.
다음 예제를 보자.
@Composable
fun Composable1(sharedObj: SharedObj, value: String) {
sharedObj.value = value
Text("Value is $value")
}
value 변수가 자주 바뀌면 현재 진행 중인 재구성은 취소되고 최신 값으로 Composable1 함수가 호출된다.
하지만 재구성이 취소되어도 sharedObj는 이미 바뀐 상태이므로 일관성이 없는 상태일 수 있다.

⇒ 실제 UI는 v3만 보여주지만,
sharedObj는 v1 → v2 → v3 순서로 모두 변경되어 일관성이 깨진다.
따라서, Composable 함수 안에는 값에 따라 UI만 갱신될 수 있게 Text만 남겨두는 것을 추천한다.
❓ 그럼 sharedObj.value = value는 어디에 두나요 ??
❗️ 이 때 사용하는 것이 Effect이다.
간단히 설명하자면,
데이터를 변경하는 작업을 포함한 UI 외부 작업의 처리를 도와주는 Jetpack Compose의 기능이다!
2. Composable 함수는 동시에 실행이 가능하다.
Compose는 여러 Composable 함수를 병렬로 실행하거나, 우선순위가 낮은 화면 밖의 Composable을 백그라운드에서 처리할 수 있다.
@Composable
fun ListWithBug(myList: List<String>) {
var items = 0
Row(horizontalArrangement = Arrangement.SpaceBetween) {
Column {
for (item in myList) {
Card {
Text("Item: $item")
items++
}
}
}
Text("Count: $items")
}
}
하지만, Composable 함수 안에 변수를 선언하고 이를 여러 스레드에서 호출하게 되면
→ 각 스레드에서 저장하고 있는 서로 다른 items가 불러와질 수 있다.
따라서, 상태는 외부에서 관리하고 Composable에는 매개변수로 전달해야 스레드 일관성을 보장할 수 있다.
3. Composable 함수는 순서와 관계없이 실행된다.
@Composable
fun ButtonRow() {
MyFancyNavigation {
StartScreen()
MiddleScreen()
EndScreen()
}
}
보이는 ButtonRow 함수는 StartScreen() → MiddleScreen() → EndScreen() 의 순서로 실행된다고 생각할 수 있다.
실제로는 순서와 관계없이 실행되는 독립적인 함수들이다!
이처럼 최초 Composition 이후 데이터가 변경 시 UI가 다시 그려지는 Recomposition은
선언형 패러다임의 대표 특징이라 할 수 있다.
:)
출처
https://developer.android.com/develop/ui/compose/mental-model?hl=ko#recomposition