본문 바로가기

Android/Jetpack Compose

[Jetpack Compose] State와 State Hoisting 이해하기

반응형

Jetpack Compose를 사용하면서 가장 기본적이면서도 중요한 개념 중 하나가 State이다. 또한 이를 컴포저블 간에 깔끔하게 관리하기 위한 패턴으로 State Hoisting이 널리 사용되고 있다. 이 글에서는 Compose의 State가 무엇이고, 왜 State Hoisting이 필요한지, 그리고 실제로 어떻게 사용하는지 정리해보려고 한다.

1. Compose에서 State란?

Compose는 선언형 UI 프레임워크이다. "상태(State)가 바뀌면 UI를 다시 그린다"는 철학에 따라 작동한다.
State는 UI를 그리는 데 필요한 데이터를 의미한다. 즉, 화면에 표시되는 값이나, 사용자의 입력, 버튼 클릭 여부 등이 모두 상태로 표현될 수 있다.

Compose에서는 @Composable 함수 안에서 remember와 mutableStateOf를 사용하여 상태를 정의할 수 있습니다.

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }

    Button(onClick = { count++ }) {
        Text(text = "Count: $count")
    }
}

위 예시에서 count는 버튼을 누를 때마다 증가하는 State이다.
mutableStateOf(0)를 통해 초기값을 0으로 설정했고, remember를 통해 recomposition 시에도 상태를 유지할 수 있도록 했다.

2. State Hoisting이란?

State Hoisting은 "상태를 컴포저블 내부가 아니라 외부(부모)로 끌어올려서 관리하는 것"을 의미한다.
왜 이렇게 해야 할까?

  • 재사용성 증가: 내부에 상태를 숨기지 않고 외부에서 주입받으면, 컴포저블을 더 다양한 상황에서 재사용할 수 있음
  • 단방향 데이터 흐름(One-way Data Flow): UI 상태 관리가 명확해져서 코드가 예측 가능해짐
  • 테스트 용이성: 상태를 외부에서 컨트롤할 수 있어 테스트가 쉬워짐

3. State Hoisting 적용 예제

State를 내부에서 관리하는 버전

@Composable
fun NameInput() {
    var name by remember { mutableStateOf("") }

    TextField(
        value = name,
        onValueChange = { name = it },
        label = { Text("Enter your name") }
    )
}

State를 외부로 뺀(State Hoisting 적용한) 버전

@Composable
fun NameInput(
    name: String,
    onNameChange: (String) -> Unit
) {
    TextField(
        value = name,
        onValueChange = onNameChange,
        label = { Text("Enter your name") }
    )
}

변화된 점:

  • name이라는 상태를 매개변수로 받는다.
  • onNameChange라는 콜백을 통해 상태 변경을 외부에 요청한다.

그리고 사용하는 쪽(부모 컴포저블)에서는 이렇게 작성한다.

@Composable
fun NameScreen() {
    var name by remember { mutableStateOf("") }

    NameInput(
        name = name,
        onNameChange = { name = it }
    )
}

이제 NameInput은 자체 상태를 가지지 않으며, 오직 외부에서 받은 값과 콜백만으로 동작한다.
이 구조가 Compose에서 권장하는 "단방향 데이터 흐름"이다.


4. 언제 State Hoisting을 사용할까?

다음 상황에서는 반드시 State Hoisting을 고려:

  • 하위 컴포저블이 독립적으로 재사용되어야 할 때
  • 부모 컴포저블이 전체 화면 상태를 제어해야 할 때
  • 복잡한 상태 관리가 필요한 경우 (예: ViewModel과 연결할 때)

반면, 단순한 UI 요소나 일회성 컴포저블에서는 내부 상태만으로도 충분할 수 있다.

5. 요약

개념 설명
State UI를 구성하는 데 필요한 데이터
State Hoisting 상태를 컴포저블 외부(부모)로 끌어올려 관리하는 패턴
장점 재사용성 증가, 테스트 용이성, 코드 명확성
방법 값과 이벤트 콜백을 매개변수로 전달

 

6. 마무리

State와 State Hoisting은 Jetpack Compose를 제대로 이해하고 사용하기 위한 핵심 개념이다.
특히 앱이 점점 커질수록, 이 원칙을 지키는 것이 유지보수성과 확장성을 크게 향상시킨다.

Compose를 사용할 때 "상태는 필요한 곳에만 두자" 원칙을 꼭 기억해야한

반응형