안녕하세요, '생각하는 개발자'입니다.

지난 포스팅에서는 Text, Button 등을 Column과 Row에 배치하여 화면의 뼈대를 만드는 법을 배웠습니다. 하지만 지금까지 만든 화면은 멈춰있는 그림과 같았죠. 버튼을 눌러도 아무런 반응이 없었습니다.
이번 시간에는 Compose 앱에 생명을 불어넣는 마법, 바로 **상태(State)**에 대해 배울 겁니다. 이 개념을 이해해야만 사용자의 행동에 따라 숫자가 올라가고, 글자가 바뀌는 '진짜 앱'을 만들 수 있습니다.
1. UI가 스스로 변하는 원리: 재구성(Recomposition)
기존 안드로이드(XML) 방식에서는 버튼을 누르면 우리가 직접 "텍스트뷰의 글자를 '짠'으로 바꿔라!"라고 명령해야 했습니다. (textView.text = "짠")
하지만 Compose는 다릅니다. 우리는 그저 **"이 데이터(State)가 바뀌면, 화면은 알아서 바뀌어라"**라고 선언만 해두면 됩니다.
- 데이터(State)가 변경됩니다.
- Compose가 이를 감지합니다.
- 변경된 데이터를 사용하는 UI 부분만 다시 그립니다.
이 '다시 그리는 과정'을 **재구성(Recomposition)**이라고 부릅니다.
2. 상태 기억하기: remember와 mutableStateOf
그럼 Compose에게 "이것이 네가 감시해야 할 데이터야!"라고 어떻게 알려줄까요? 여기서 두 가지 핵심 키워드가 등장합니다.
- mutableStateOf: 데이터를 담는 관찰 가능한 그릇을 만듭니다. 값이 바뀌면 Compose에게 신호를 보냅니다.
- remember: 화면이 다시 그려질 때(재구성), 이 변수가 초기화되지 않고 이전 값을 기억하도록 해줍니다.
백문이 불여일견, 카운터 앱 코드로 확인해 봅시다.
Kotlin
import androidx.compose.runtime.*
import androidx.compose.material3.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@Composable
fun CounterApp() {
// 1. 'count'라는 상태(State)를 만듭니다. 초기값은 0입니다.
// 'remember'가 있어서 화면이 다시 그려져도 0으로 초기화되지 않습니다.
var count by remember { mutableStateOf(0) }
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
// 2. State를 UI에 표시합니다.
Text(text = "현재 숫자: $count")
// 3. 버튼을 누르면 State를 변경합니다.
Button(onClick = { count++ }) {
Text("숫자 증가")
}
}
}
동작 원리:
- 사용자가 버튼을 클릭합니다.
- count 값이 1 증가합니다.
- Compose가 count의 변화를 감지하고 CounterApp을 다시 실행(재구성)합니다.
- Text에 새로운 count 값이 표시됩니다.
3. 상태 끌어올리기 (State Hoisting)
앱이 커지면 UI를 그리는 함수와 상태를 관리하는 함수를 분리해야 할 때가 옵니다. 이때 사용하는 패턴이 **'상태 끌어올리기'**입니다.
쉽게 말해 **"자식은 UI만 그리고, 부모가 상태를 관리한다"**는 원칙입니다.
// 부모: 상태를 관리하고 이벤트를 처리함
@Composable
fun CounterScreen() {
var count by remember { mutableStateOf(0) }
// 자식에게 '값'과 '행동(함수)'을 전달
CounterContent(
count = count,
onIncrement = { count++ }
)
}
// 자식: 상태를 직접 가지지 않고, 받아서 그리기만 함 (Stateless)
@Composable
fun CounterContent(count: Int, onIncrement: () -> Unit) {
Column {
Text("숫자: $count")
Button(onClick = onIncrement) {
Text("증가")
}
}
}
이렇게 만들면 CounterContent는 어떤 로직도 없이 순수하게 그림만 그리는 부품이 되어, 다른 곳에서 재사용하기가 훨씬 쉬워집니다.

요약
- State(상태): UI를 변화시키는 데이터입니다.
- Recomposition(재구성): State가 바뀌면 화면을 다시 그리는 과정입니다.
- remember & mutableStateOf: State를 만들고 기억하는 필수 주문입니다.
다음 시간에는 데이터가 많을 때 사용하는 리스트(List), 즉 LazyColumn에 대해 알아보겠습니다.
'Android (Kotlin & Compose) > Part 2. Jetpack Compose UI' 카테고리의 다른 글
| [오늘의 코드 조각] [2-1] Compose 기본 UI와 레이아웃 (1) | 2025.08.27 |
|---|