ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Android] Compose State
    Android📱 2023. 5. 16. 22:26

    Jetpack Compose State

    컴포즈를 사용할 때 State는 가장 기본적이고, 중요한 요소이다.

    State란?

    State란 앱이 시간이 흐름에 따라서 변화할 수 있는 모든 값이다.

    굉장히 광범위한 의미로 디비의 데이터부터 ui에 입력 값까지 모든 것이 state라고 생각할 수 있다.

     

    @Composable
    fun TestScreen() {
        var text = "test"
        TextField(
            value = text, 
            onValueChange = { str -> text = str },
            label = { Text(text = "field!") }
        )
    }

    대표적인 예로 TextField가 있다.

    위 같은 컴포져블을 만들어서 입력을 받아보면 값이 바뀌지 않는 걸 볼 수 있다.

     

    이런 괴상한 움직임이 발생하게 된다.

    왜 그런 걸까?

     

    바로 State 때문이다.

    위에 코드는 그냥 변수이기 때문에 값이 변경되어도 Recomposition이 일어나지 않는다.

    그래서 커서는 움직이지만 값은 변화가 없게 되는 것이다.

    하지만 State로 문자열을 보관하면 State가 변경될 때 Recomposition이 일어나고, 값도 함께 변화가 되게 된다.

    위에 예제 같은 상황이라면 여기서 remember라는 함수를 또 알아야 한다.

    remember함수는 Recomposition이 일어나도 값을 컴포져블이 값을 기억할 수 있도록 도와주는 함수이다.

     

    * Recomposition?

    컴포즈에는 viewd의 생명주기처럼 Initial composition, Composition, Recomposition이 존재한다.

    Composition: UI를 그려주는 것이라고 생각하면 된다. 컴포져블들을 정의하는 것이다.

    Initial composition: 말그대로 처음에 초기화를 해주는 것이다. 처음에 컴포져블을 생성해 준다.

    Recomposition:컴포져블을

     

    State에서 중요한 개념은 Recomposition이다.

    만약 위에 코드의 변수를 State로 바꿔준다면 정상작동이 될까?

    그렇지 않다.

    천천히 보게 되면 text라는 변수에 문자열을 저장하고, text를 TextField라는 컴포져블의 value로 넣어준다.

    그리고 onValueChange가 일어나면 변경된 값을 text에 다시 저장해주고 있다.

    문제는 onValueChange가 일어날 때 발생한다.

    onValueChange가 되면 text에 변경된 문자를 저장하고 리컴포지션이 일어나게 된다.

    그러면 text에는 다시 "test"라는 문자열이 저장이 되고 우리가 변경한 텍스트가 사라지게 되는 것이다.

    그래서 처음에 있었던 문자열이 계속 유지되게 되는 것이다.

     

    이런 문제를 해결하려면 위에서 언급한 remember라는 함수를 사용해 주면 된다.

    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun Greeting(name: String, modifier: Modifier = Modifier) {
        var text by remember { mutableStateOf(name) }
        TextField(
            value = text,
            onValueChange = { str -> text = str },
            label = { Text(text = "field!") }
        )
    }

    위 코드처럼 remember 함수에 mutableState를 넘겨주면 

    이렇게 정상작동이 되는 것을 볼 수 있다.

     

    mutableState는 변화가 가능하고, Compose 런타임과 통합된 관찰 가능한 타입의 State를 생성해 준다.

    그리고 remember함수는 리컴포지션이 일어나도 값을 기억할 수 있게 해주는 함수이다.

     

    고로 onValueChange -> mutableState의 값을 변경 ->

    변경된 값을 remember로 기억 -> 리컴포지션 -> 저장한 값을 가져옴 -> TextField에 넣음

     

    이런 흐름으로 변경된 값이 정상적으로 보이게 되는 것이다.

     

    State Hoisting

     

    Compose를 공부하면 State Hoisting이라는 단어를 자주 보게 된다.

    Compose에서는 Stateless 한 컴포저블이 재사용성이 좋고, 유지보수가 좋은 컴포져블이라고 이야기한다.

    (State가 여기저기 나누어져 있게 되면 여러 번의 이유 모를 리컴포지션이 발생해서 성능이 저하되거나, 하나하나 State를 찾아다녀야 하는 불상사가 일어날 수 있다.)

    그래서 Stateless 하게 컴포져블을 구성하기 위해서는 State Hoisting을 이용해 주면 좋다.

    State Hoisting은 상태는 아래에 있는 컴포져블로 내리고, 이벤트는 위로 올린다는 개념이다.

    이렇게 구성을 하게 되면 HelloScreen에서만 State를 관리하면 되니 HelloContent는 stateless 한 컴포져블이 될 것이다.

    그리고 HelloContent에서 발생한 이벤트로 State가 변경되어야 한다면 람다로 이벤트를 감지해서 HelloScreen의 State를 변경해 주면 정상적인 리컴포지션도 발생시킬 수 있다!

     

    State Hoisting을 효율적으로 사용하기 위해서 StateHolder를 만들어 활용해 주면 State 관리를 더 유용하게 해 줄 수 있다.

     

    Compose를 공부하면서 제가 느낀 점과 공식문서를 조합해서 정리해 보았습니다.

    틀린 점이나 궁금하신 점은 댓글 주세요!

     

    참고

    https://github.com/android/nowinandroid

     

    GitHub - android/nowinandroid: A fully functional Android app built entirely with Kotlin and Jetpack Compose

    A fully functional Android app built entirely with Kotlin and Jetpack Compose - GitHub - android/nowinandroid: A fully functional Android app built entirely with Kotlin and Jetpack Compose

    github.com

    https://developer.android.com/jetpack/compose/state#state-hoisting

Designed by Tistory.