
이전 시간에 우리는 Jetpack Compose 에 관해 간단하게 알아봤다.
하지만 Compose 환경에서 데이터를 변경하는 방법에 대해서는 알지 못했다.
마지막 글에 정리한 상태 관리 시스템 이라는 것을 활용해야 하는데, 이건 어떻게 구현할까?
Jetpack Compose 환경에서의 Lifecycle 은 View Lifecycle 과는 다르다.
View Lifecycle 은 Activity, Fragment 에서
Compose Lifecycle 의 경우
여기서 우리가 유의깊게 봐야하는 것은 Recomposition 으로 이 Recomposition 이 일어나지 않으면 데이터의 변화가 생기더라도 UI 가 다시 그려지지 않기에 UI 업데이트가 이루어지지 않는다.
다음 코드를 복사하여 실행해보자
class MainActivity() : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
enableEdgeToEdge()
MaterialTheme {
var text = "밥"
Scaffold {
Column(modifier = Modifier
.fillMaxSize()
.padding(it)
) {
Text(text = text)
Button(
onClick = {
text = if(text == "국") "밥" else "국"
Timber.d("${text}으로 변경 완료")
}
) { Text(text = "${text}")}
}
}
}
}
}
}
위의 코드는 Text Composable [텍스트를 표기하는 Composable] 의 text 값으로 "밥" 이라는 문자를 넣어줬다.
그리고 Button Composable [버튼 UI를 담당하는 Composable] 를 클릭 시 text variable 을 "국" 으로 변경하도록 설정했다.
이제 버튼을 눌러 변경해보면?
변경을 해보았으나 UI 는 그대로이다.
이유는 이전에 말했던 것처럼 Recomposition 이 발생해야하는데, 조건이 맞춰지지 않아 진행이 되지 않은 것이다.
그렇다면 이 Recomposition 을 유발하려면 어떻게 해야할까?
바로 상태 관리 시스템 을 이용하면 된다.
Compose Library 에서는 Compose 환경에서 쓸 수 있는 여러 State interface, class 를 제공해주는데 이를 구현하고 값을 변경해주면 된다.
이전의 text 를 var text by mutableStateOf("밥") 으로 변경해주자

어라?
IDE 에서 현재 State 객체와 remember 라는 것을 함께 사용하라고 주의를 준다.
우선 이를 무시하고 시도해보자.

값이 잘 변경되는 것을 확인할 수 있다.
하지만 이는 올바른 방법이 아니다.
다시 한 번 코드를 변경해보자.
이번에는 text 를 다음과 같이 Scaffold 내부에 넣어볼 것이다.
class MainActivity() : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
enableEdgeToEdge()
MaterialTheme {
Scaffold {
var text by mutableStateOf("밥")
Column(modifier = Modifier
.fillMaxSize()
.padding(it)
) {
Text(text = text)
Button(
onClick = {
text = if(text == "국") "밥" else "국"
Timber.d("${text}으로 변경 완료")
}
) { Text(text = "${text}")}
}
}
}
}
}
}
이제 실행하고 버튼을 눌러보자.

방금 시도한 방법은 아까와는 달리 Recompositon 이 발생한 것임에도 데이터가 변경되지 않았다.
이것을 설명하려면 remember 와 Recomposition 에 대한 설명이 필요한데, 이제 state 를 사용할 때 remember 를 사용하는 이유에 대해 알아보자.
Compose System 에서 사용되는 함수로 Recomposition 시에도 값을 잃지 않기 위해,
내부 Lambda [Calculate]의 값을 currentComposer 에 저장하는 역할을 한다.
설명에서는 Lambda 에 기록된 값을 저장한다고 하는데, 이렇게 사용하는 이유는 다음과 같다.
Recomposition 은 상태를 읽는 대상 Scope Composable 을 재실행 시킨다.
만약 var a = 1 이라는 값을 a = 2 로 변경했어도, 리컴포지션되면 최초의 var a = 1 초기화가 다시 진행되기 때문에 값이 원래대로 돌아간다는 의미이다.
아까 처음 코드는 UI 변화가 이루어지고, 두 번째 코드에서는 UI 변화가 이루어지지 않는 것도 이와 같다.
리컴포지션 범위가 Scaffold 내부였기에 그 밖에서 선언한 text 는 영향을 받지 않는다. 즉, 값 초기화가 되지 않았다.
리컴포지션 범위가 Scaffold 내부였기에 내부에서 선언한 text 는 리컴포지션에 의해 초기화된다.
만약 첫 번째 코드처럼 밖에서 선언한, remember 로 지정하지 않는 state 가 존재할때, 동일한 스코프 내에서 리컴포지션이 발생한다고 생각해보자. 의도치 않은 버그가 발생할 것이다.
만약, 이를 방지하기 위해서는 무엇을 해야할까?
데이터가 유실되지 않게 어딘가에서 캐싱을 해주면 된다.
remember 함수는 이러한 기능을 제공하며, 내부 값이 Recompostion 에 의해 유실되지 않게 해준다.
@Composable
inline fun <T> remember(crossinline calculation: @DisallowComposableCalls () -> T): T =
currentComposer.cache(false, calculation)
내부를 살펴보면 currentComposer.cache 라는 함수를 이용하는 것으로 나오는데, 자세히 알아보려면 어려우니 나중에 설명하자.
var text by remember { mutableStateOf("밥") }
아까의 코드를 위의 코드로 변경한 뒤, 실행해보자.

"밥" 이었던 문자가 "국" 으로 변하게 된 것을 알 수 있다.