깨알 개념/Android

[Android] Jetpack Compose - State Hoisting

interfacer_han 2024. 7. 23. 01:55

#1 개요

 

상태를 호이스팅할 대상 위치  |  Jetpack Compose  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 상태를 호이스팅할 대상 위치 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Compose 애플리케이션에서

developer.android.com

State Hoisting(상태 호이스팅) 패턴을 적용해 코드의 잠재적 유지보수성을 높혀본다.
 

#2 코드

#2-1 State Hoisting 패턴이 적용되지 않은 코드

...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Box(
                modifier = Modifier.fillMaxSize()
            ) {
                ButtonExample(Modifier.align(Alignment.Center))
            }
        }
    }
}

// ↓ ↓ ↓ Stateful Composable
@Composable
fun ButtonExample(modifierParam: Modifier = Modifier) {
    val count = remember {
        mutableStateOf(0)
    }

    Button(
        onClick = { count.value++ },
        modifier = modifierParam
    ) {
        Text(
            text = "Count: ${count.value}",
            fontSize = 40.sp
        )
    }
}

위 코드의 ButtonExample()처럼 State를 보유한 Composable을 Stateful Composable이라고 부른다. Stateful Composable은 재사용성이 떨어지고 테스트하기도 어렵다는 문제를 가진다. 왜냐하면, 의존성 주입을 구현하지 않았기 때문이다. 이 문제를 해결하려면 State를 외부 생성자로부터 주입받는 방식으로, 즉 Composable이 생성자 주입(Constructor Injection)을 구현하게끔 변경해야 한다.
 

#2-2 State Hoisting 패턴이 적용된 코드

...

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Box(
                modifier = Modifier.fillMaxSize(),
            ) {
                val count = remember {
                    mutableStateOf(0)
                }

                ButtonExample(
                    count.value,
                    Modifier.align(Alignment.Center)
                ) { newValue ->
                    count.value = newValue + 1
                }
            }
        }
    }
}

// ↓ ↓ ↓ Stateless Composable
@Composable
fun ButtonExample(
    currentCount: Int,
    modifierParam: Modifier = Modifier,
    updateCount: (Int) -> Unit,
) {
    Button(
        onClick = { updateCount(currentCount) }, // updateCount()는 ButtonExample()이 아닌, ButtonExample()를 호출한 상위 Composable에서 처리된다.
        modifier = modifierParam

    ) {
        Text(
            text = "Count: $currentCount",
            fontSize = 40.sp
        )
    }
}

State가 있는 Composable이 Stateful Composable이었던 것처럼, State가 는 Composable은 Stateless Composable라고 부른다. State를 생성자 주입받는 경우도 Stateless Composable이다. 우선 State를 setContent { ... }로 이동시키고 ButtonExample의 매개변수에 State의 제네릭 타입(여기서는 Int)인 currentCount를 추가한다. 그리고 버튼의 클릭 리스너에 넣을 람다 함수도 매개변수로 추가한다. 이 람다 함수는 setContent { ... }에 있는 State의 값을 조작한다.
 
이렇게 만들어진 위 코드의 구조를 State Hoisting 패턴이라고 부른다. hoist의 사전적 의미는 (흔히 밧줄이나 장비를 이용하여) 들어[끌어]올리다인데, State를 하위 Composable (본 게시글에서는 ButtonExample())로부터 상위 Composable (본 게시글에서는 setContent)까지 끌어 당긴다는 늬앙스로 쓰인 것이다.
 

#3 State Hoisting 패턴의 의의

State Hoisting은 단방향 데이터 흐름(Unidirectional Data Flow)을 구축하는 데 주요하게 사용되는 패턴이다. 단방향 데이터 흐름이란, 상태(State)는 위(setContent)에서 아래(ButtonExample())로 보내고, 이벤트(클릭 이벤트 등)은 아래(ButtomExample())에서 위(setContent)로 보내는 흐름을 의미한다. 따라서 State Hoisting 패턴을 적용할 Composable에게는 2가지가 요구된다. Composable이 현재 표시할 값과 그 값을 변경 요청하는 이벤트다. 전자는 #2-2의 currentCount에 해당하고 후자는 #2-2의 updateCount에 해당한다.
 
단방향 데이터 흐름이 구축되면, 데이터베이스나 서버로부터 State를 받아와 Staless Composable에 주입시켜줄 수 있다는 점에서 캡슐화 및 모듈화가 달성된다. 또 State가 상위 Composable에 위치하기에 더 중앙집중적인 위치에서 State를 일괄적으로, 쉽게 관리할 수 있게 된다.
 

#4 요약

State Hoisting은 State를 상위 Composable로 옮기는 코드 패턴이다.

#5 완성된 앱

 

android-practice/jetpack-compose/StateHoisting at master · Kanmanemone/android-practice

Contribute to Kanmanemone/android-practice development by creating an account on GitHub.

github.com

 

#6 이어지는 글

 

[Android] Jetpack Compose - ViewModel에서 State 사용하기

#1 개요Jetpack Compose에서 ViewModel을 사용해본다. Jetpack Compose를 사용하지 않는 전통적인 방식에서의 ViewModel과 크게 다를 게 없다. Jetpack Compose에 ViewModel을 구현함으로써 State Hoisting 패턴을 극대화시

kenel.tistory.com

ViewModel을 이용함으로써, State Hoisting 패턴을 증대시킨다.