본문 바로가기
아키텍처

[앱 아키텍처] 상태 호이스팅

by ftbd 2024. 1. 30.

컴포저블(위젯)을 Stateless로 만들기 위해 상태를 컴포저블(위젯)의 호출자로 옮기는 패턴.

  • value: T: 표시할 현재 값
  • onValueChange: (T) -> Unit: T가 제안된 새 값인 경우 값을 변경하도록 요청하는 이벤트

끌어올린 상태에는 중요한 속성이 몇 가지 있습니다.

  • 단일 정보 소스: 상태를 복제하는 대신 옮겼기 때문에 정보 소스가 하나만 있습니다. 버그 방지에 도움이 됩니다.
  • 캡슐화됨: 스테이트풀(Stateful) 컴포저블만 상태를 수정할 수 있습니다. 철저히 내부적 속성입니다.
  • 공유 가능함: 호이스팅한 상태를 여러 컴포저블과 공유할 수 있습니다. 다른 컴포저블에서 name을 읽으려는 경우 호이스팅을 통해 그렇게 할 수 있습니다.
  • 가로채기 가능함: 스테이트리스(Stateless) 컴포저블의 호출자는 상태를 변경하기 전에 이벤트를 무시할지 수정할지 결정할 수 있습니다.
  • 분리됨: 스테이트리스(Stateless) ExpandingCard의 상태는 어디에나 저장할 수 있습니다. 예를 들어 이제는 name을 ViewModel로 옮길 수 있습니다.
@Composable
fun HelloScreen() {
    var name by rememberSaveable { mutableStateOf("") }

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

@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Hello, $name",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.h5
        )
        OutlinedTextField(
            value = name,
            onValueChange = onNameChange,
            label = { Text("Name") }
        )
    }
}

단방향 데이터 흐름을 따르면 UI에 상태를 표시하는 컴포저블과 상태를 저장하고 변경하는 앱 부분을 서로 분리할 수 있습니다.

 

핵심 사항: 상태를 끌어올릴 때 상태의 이동 위치를 쉽게 파악할 수 있는 세 가지 규칙이 있습니다.
  1. 상태는 적어도 그 상태를 사용하는 모든 컴포저블의 가장 낮은 공통 상위 요소로 끌어올려야 합니다(읽기).
  2. 상태는 최소한 변경될 수 있는 가장 높은 수준으로 끌어올려야 합니다(쓰기).
  3. 동일한 이벤트에 대한 응답으로 두 상태가 변경되는 경우 두 상태를 함께 끌어올려야 합니다.

※ 이러한 규칙에서 요구하는 것보다 상태를 더 높은 수준으로 끌어올릴 수 있습니다. 하지만 상태를 끌어내리면 단방향 데이터 흐름을 따르기가 어렵거나 불가능할 수 있습니다.