Jetpack Compose, 수동으로 recompose 하는 방법

내가 원하는데 Jetpack Compose 가 바로 recompose 해주지 않을 때가 있다. 이 때 수동으로 즉시 recompose 되게 하는 방법을 알아본다.

상태값을 바꿨는데 왜 단추가 안 나타나는 거지?

내가 원했던 것은, 내가 어떤 단추를 눌렀을 때 다른 곳에 다른 단추가 나타나게 하는 거였다. 논리는 맞다고 생각했는데 단추는 나타나지 않았다. 혹시나 해서 화면을 옆으로 돌려 보았다. 그러자 화면이 다시 그려지면서 내가 원하는 결과가 비로소 나타났다. 즉, 상태값은 제대로 전달됐는데 단지 즉시 recompose 가 되지 않아서 생긴 문제였다.

수동으로 recompose 할 방법을 찾아라.

어떻게든 수동으로 즉시 recompose 되도록 하기 위해서 코드를 이리 저리 변형해 보아도 성공하지는 못했다. 어쩔 수 없이 검색을 했다. 그들이 제시하는 몇 가지 방법을 시도해 보았지만 모두 내 상황에는 맞지 않았다. 그러다가 다음의 글을 찾았다.

If you ever need to force a recompose then create an empty LaunchedEffect with a MutableState value key and toggle the mutable state.
https://stackoverflow.com/questions/66950978/how-to-force-jetpack-compose-to-recompose

나는 처음에 그의 말이 무슨 뜻인지 몰랐고, 그의 코드도 이해하지 못했다. 그러나 다른 방법이 모두 실패한 마당에 기댈 곳은 여기밖에 없다는 느낌이 들었다.

그의 말을 곰곰히 되새겨 보니까 무슨 말인지 알 것 같았다. 여전히 그의 코드가 다 이해되지는 않았지만 이제는 내 방식으로 할 수 있을 것 같았다.

즉시 recompose 하라

상태값 저장

우선 그의 말에 따라, 필요한 상태값을 저장할 곳을 만들었다:

data class MyUiState(
..
..
val recomposeToggleState: MutableState<Boolean> = mutableStateOf(false),
)

recomposeImmediately()

그런 다음, 상태값을 토글 할 멤버 함수를 ViewModel 에 만들었다:

class MyViewModel(...) : ViewModel() {
var myUiState by mutableStateOf(MyUiState())
        private set
..
..
fun recomposeImmediately() {
        myUiState.recomposeToggleState.value = !myUiState.recomposeToggleState.value
    }
}

LaunchedEffect()

상태값을 받아 먹을 LaunchedEffect() 함수를 컴포저블 함수 안의 적당한 곳에 위치시키고, 상태값을 인수로 넣어 준다:

LaunchedEffect(myUiState.recomposeToggleState.value) {}

보다시피 이 함수는 상태값만 받아 먹고 딱히 하는 일은 없다. 바디가 텅 비었다. 컴포저블 함수 안이라도 이걸 넣을 수 없는 곳도 있다, 안드로이드 스튜디오가 뭐라고 한다. 예를 들어, LazyColumn 블록 안에는 못 넣는다.

이제 상태값을 토글 할 함수를 적당한 곳에 넣으면 된다. 내 경우에는 Button() 함수의 onClick 람다가 적당하다.

Button(
                    modifier = Modifier,
                    onClick = {
                        // put some code here
                        viewModel.recomposeImmediately()
                    }) {
                    Text(
                        text = "Recompose", fontSize = 16.sp
                    )
                }

이제 내가 단추를 클릭할 때마다 상태값이 자동으로 바뀐다. 실제로 단추를 눌러 보았다. 효과가 좋았다. 내가 원하는대로 즉시 recompose 되었다.

완성된 컴포저블 함수의 모습

위의 두 코드 블록을 종합하면, 컴포저블 함수는 대략 다음과 같은 모습이다:

@Composable
fun MyExampleComposableFunction(
    viewModel: MyViewModel = viewModel(),
    ..
) {
var myUiState = viewModel.myUiState
..
Button(
                    modifier = Modifier,
                    onClick = {
                        // put some code here
                        viewModel.recomposeImmediately()
                    }) {
                    Text(
                        text = "Recompose", fontSize = 16.sp
                    )
                }
..
LaunchedEffect(myUiState.recomposeToggleState.value) {}
..
}

Leave a Comment