본문 바로가기
Android/Compose

Compose의 상태

by 겸 2023. 11. 2.

상태

모든 Android앱은 사용자에게 상태를 표시한다.

  ex) 스낵바, 댓글, 애니메이션 등.. 모두 상태이다.

Jetpack Compose는 Android앱에서 상태를 저장하고 사용하는 위치와 방법을 나타낸다.

 

Compose는 선언적이다.

따라서 Compose를 업데이트하는 유일한 방법은 새로운 인수로 동일한 컴포저블을 호출하는 것이다.

새로운 인수가 UI 상태를 표현한다.

상태가 업데이트될 때마다 리컴포지션이 실행되어 UI가 업데이트 되는 것이다.

  1. Composition
    1. Jetpack Compose가 @Composable을 실행할 때 빌드한 UI
  2. 초기 Composition
    1. 처음 @Composable을 실행하여 생성된 Composition
  3. Recomposition
    1. 데이터가 변경될 때 Composition을 업데이트하기 위해 @Composable을 다시 실행하는 것

컴포저블의 상태

remember

remember API를 사용하여 메모리에 객체를 저장할 수 있다.

remember에 의해 계산된 값은 초기 컴포지션 중에 컴포지션에 저장되고, 저장된 값은 리컴포지션 중에 반환된다.

 

remember은 변경 가능한 객체뿐만 아니라 변경할 수 없는 객체를 저장하는 데 사용할 수 있다.

remember를 호출한 컴포저블이 컴포지션에서 삭제되면 그 객체를 잊는다.

MutableState

interface MutableState<T> : State<T> {
    override var value: T
}

mutableSateOf를 사용하면 관찰 가능한 MutableState<T>를 생성한다.

이는 런타임 시 상태를 관찰할 수 있다.

value가 변경되면 컴포저블 함수의 리컴포지션이 예약된다.

 

MutableState객체를 선언하는 3가지 방법

  1. val mutableState = remember { mutableStateOf(default) }
    1. mutableState.value로 값을 불러와야한다.
  2. var value by remember { mutableStateOf(default) }
    1. by 위임 구문에는 import추가가 필요하다.
    2. import androidx.compose.runtime.getValue 
      import androidx.compose.runtime.setValue
    3. value를 바로 사용할 수 있다.
  3. val (value, setValue) = remember { mutableStateOf(default) }
    1. setValue는 변경된 값을 저장하고, 같은 값으로 변경되면 recomposition이 일어나지 않도록 한다.
    2. TextField(value = value, onValueChange = setValue)

상태 변경

버튼을 클릭해서 변수의 상태를 변경하는 코드를 구현해보자.

@Composable
private fun Greeting(name: String) {
    var expanded = false 

        /* ... */

        ElevatedButton(
        onClick = { expanded = !expanded }
    ) {
        Text(if (expanded) "Show less" else "Show more")
    }

        /* ... */
}

위의 코드는 동작하지 않는다.

expanded 변수에 다른 값을 설정해도 컴포즈는 이를 상태가 변경되었다고 감지하지 않기 때문이다.

해당 변수는 컴포즈가 추적하지 않으므로 리컴포지션 시 트리거하지 않는다.

@Composable
fun HelloContent() {
   Column(modifier = Modifier.padding(16.dp)) {
       Text(
           text = "Hello!",
           modifier = Modifier.padding(bottom = 8.dp),
           style = MaterialTheme.typography.h5
       )
       OutlinedTextField(
           value = "",
           onValueChange = { },
           label = { Text("Name") }
       )
   }
}

위 코드를 실행하면 TextField에 텍스트가 입력되지 않는다.

value매개 변수가 변경될 때 리컴포지션이 일어나는데,

새로운 상태가 업데이트되지 않으므로 리컴포지션이 일어나지 않기 때문이다.

 

컴포저블 내부에 상태를 추가하기 위해서는 mutableStateOf 함수를 사용하면 된다.

컴포즈가 State를 읽는 함수를 재구성한다.

StateMutableState는 어떤 값을 보유하고 그 값이 변경될 때마다 UI 업데이트(리컴포지션)를 트리거하는 인터페이스이다.

 

이때, 값을 변경하려면 어떻게 해야할까?

리포지션을 할 때 상태를 유지하려면 remember을 사용해서 변경 가능한 상태를 기억해야한다.

@Composable
fun Greeting() {
    val expanded = remember { mutableStateOf(false) }
    ...
}

remember을 사용하지 않는다면 값이 변경될 때마다 리컴포지션이 일어나므로 expanded값이 다시 false로 설정된다.

remember을 사용하면 MutableState의 리컴포지션을 방지하므로 상태가 초깃값으로 재설정 되지 않는다.

  • 주의할 것

ArrayList 또는 변경 가능한 데이터 클래스 같은 관찰 불가능한 객체는 Compose에서 관찰할 수 없으며 객체가 변경될 때 리컴포지션을 트리거하지 않는다.


상태 유지 및 복원

remember함수는 컴포저블이 컴포지션에 유지되는 동안만 작동한다.

기기를 회전하면 Activity가 재시작되면서 프로세스가 중단되므로 모든 상태를 잃게된다.

이를 방지하기 위해 rememberSavable을 사용하면 화면 회전으로 프로세스가 중단되어도 상태를 저장한다.

rememberSaveable API는 Bundle에 데이터를 저장할 수 있는 remember코드의 래퍼이므로 상태유지가 가능하다!

상태를 저장하는 방법

1. Bundle에 추가되는 모든 데이터 유형은 자동으로 저장된다.

2. Bundle에 추가할 수 없는 항목을 저장하는 경우

a. Parcelize

dataclass를 저장할 때는 @Parcelize 어노테이션을 추가하여 객체를 parcelable로 만든다.

@Parcelize
data class City(val name: String, val country: String) : Parcelable

@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable {
        mutableStateOf(City("Madrid", "Spain"))
    }
}

b. MapSaver

c. listSaver

3. 상태 홀더 클래스 사용

관찰해야할 상태의 양이 늘어난다면 상태 홀더 클래스를 만들 수 있다.

호이스팅한 상태 객체이다.

상태를 저장하기 위한 클래스를 만들고 객체 상태를 변경한다.

@Composable
fun rememberMyAppState(
    windowSizeClass: WindowSizeClass
): MyAppState {
    return remember(windowSizeClass) {
        MyAppState(windowSizeClass)
    }
}

@Stable
class MyAppState(
    private val windowSizeClass: WindowSizeClass
) { ... }

반응형

'Android > Compose' 카테고리의 다른 글

Compose의 레이아웃 - 기본사항  (0) 2023.12.01
Compose의 상태 - Flow, LiveData  (0) 2023.11.29
Compose의 상태 - 호이스팅이란?  (0) 2023.11.02
Compose 이해  (0) 2023.10.26