State(상태)에 대해 한 줄 요악하면
시간이 지남에 따라 변경될 수 있는 어떤 값
이라 할 수 있습니다.
시간에 따라 변경될 수 있다? 일반 변수나 LiveData도 사용자가 작성해 놓고 시간만 지나면 변경될 수 있는 값인데 무슨 소리를 하는 거지?라는 의문이 들 겁니다. (저는 그렇게 생각이 들더라고요)
그래서 저는 State를
UI를 그리는 데 사용하는 관찰 가능 한 값
이라 정의하겠습니다.
State and Jetpack Compose
State를 배우기 앞서 Compose를 간단하게 알아보겠습니다.
기본적으로 Compose는 '선언형'입니다. 따라서 UI를 업데이트하는 방법은 새로운 인수로 같은 Composable을 호출하는 것입니다. 그렇게 만들어진 인수는 UI State라고 불립니다.
언제든 State가 업데이트되면 recomposition이 실행되지만 명시적으로 새 State를 알려줘야 합니다.
TextField 같은 것 들은 명령형 XML 기반의 뷰에서 작동했던 것처럼 자동으로 업데이트되지 않습니다.
주요 용어
initial composition : 컴포지션 최초 생성
Composable (컴포저블) : @Composable가 붙은 함수
Composition (컴포지션) : Jetpack Compose가 Composable을 실행 시 생성할 UI에 대한 명세
Recomposition (리컴포지션) : Composition의 State가 바뀌었을 때 Composable을 재 시작
명시적으로 알려주지 않으면 어떻게 되는지 예제를 통해 확인할 수 있습니다.
@Composable
fun Greeting() {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Hello!",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.bodyMedium
)
OutlinedTextField(
value = "",
onValueChange = {} // <- 아무것도 업데이트 하지 않음,
label = { Text("Name")}
)
}
}
위 코드를 실행해 본다면 아무런 변화가 없을 겁니다. TextField에 값을 입력해도 Text의 값이 바뀔 리도 없고요. 왜냐면 TextField에서 아무것도 업데이트하고 있지 않기 때문입니다.
State in composable
Composable 함수는 객체를 저장하는데 remember API를 사용할 수 있습니다. remember API를 쓰게 되면 첫 composition 상승 이후 값이 저장되게 됩니다. 따라서 안정된 결괏값을 받을 수 있습니다.
또한 recomposition 중 remember은 mutable, immutable과 상관없이 둘 다 저장할 수 있습니다.
저장된 객체는 remember이 Composition에서 사라지면 제거됩니다.
remember API는 Composition 안에서만 사용할 수 있습니다.
실제 코드로 비유하면 @Composable을 붙인 함수 안에서만 사용할 수 있습니다.
MutableStateOf
컴포즈 런타임 동안 관찰가능한 MutableState <T> 타입을 생성하는 함수입니다.
interface MutableState<T> : State<T> {
override var value: T
operator fun component1(): T
operator fun component2(): (T) -> Unit
}
value에는 뭐든 들어갈 수 있고, Composable함수 안에선 언제든 읽을 수 있습니다.
MutableState 객체를 Composable에서 호출하는 방법은 총 3가지입니다.
val default:String = ""
//1번
val mutableState: MutableState<String> = remember { mutableStateOf(default) }
//2번
var value: String by remember { mutableStateOf(default) }
//3번
val (value: String, setValue: (String) -> Unit ) = remember { mutableStateOf(default)}
선언 방법이 다를 분 결과 자체는 비슷합니다.
1번은 remember 된 MutableState 자체를 리턴 받기 때문에 타입이 MutableState <String>입니다.
2번은 by를 통해 MutaleState <String>를 위임받았으므로 내용물만 가지게 됩니다. 따라서 String 타입입니다.
3번은 MutableState의 value와 component2()를 리턴 받습니다. value는 mutableStateOf에 저장된 값을 리턴 받고 setValue는 remember 에 들어가 있는 익명 함수(내용물)를 리턴 받습니다.
이러한 선언은 동일한 것이며 서로 다른 용도의 상태를 사용하기 위한 구문 슈가(syntax sugar) 로 제공됩니다. 셋 중에 읽기 쉬워보이는거 쓰면 됩니다.
구문 슈가 (syntax sugar) : 문법과 의미를 바꾸지 않으면서도 새로운 기능을 기존에 있는 기능으로 표현함으로써 언어에 추가하는 것, 간결하고 명확하게 표현 가능한 문법을 말합니다.
더 짧게 줄이자면 겁나 간결해서 개꿀빨 수 있는 문법입니다
remember로 랩핑 된 값은 다른 컴포저블에 쓸 수없고 컴포저블의 상태를 바꾸는 로직에 쓸 수 있습니다. 이를 이용하여 처음 작성했던 예제를 제대로 작동할 수 있도록 바꿀 수 있습니다.
@Composable
fun Greeting() {
var name by remember { mutableStateOf("") }
Column(modifier = Modifier.padding(16.dp)) {
if(name.isNotEmpty())
Text(
text = "Hello! $name",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.bodyMedium
)
OutlinedTextField(
value = name,
onValueChange = { name = it},
label = { Text("Name")}
)
}
}
remember는 recomposition동안 State가 유지되도록 도와줍니다. 만약 구성이 바뀐다면(ex. 가로모드) Stae는 유지되지 않습니다. 이러한 경우 rememberSavable를 사용하면 됩니다. rememberSavable는 값을 Bundle에 자동 자정해줍니다. 만약 일반 타입이 아닌 사용자가 만든 타입일 경우 custom saver object를 통해 사용할 수 있습니다. 이와 관련된 내용은 다음 포스트를 통해 알아보겠습니다.
주의
ArrayList <T>나 mutableListOf()과 같은 Mutable객체를 State로 사용하는 것은 권장하지 않습니다. 사용자가 보는 화면과 실제 데이터의 불일치를 발생시킬 수 있기 때문입니다. Mutable객체는 관찰할 수 없습니다. Compose는 State를 관찰하며 UI를 갱신해 나가는데 관찰할 수 없는 객체 타입이 들어오면 관찰할 수가 없고, 또한 recomposition이 일어나지 않을 수도 있습니다.
관찰 불가능하 Mutable객체를 쓰는 것보다 Immutable이 listOf() 또는 State <List <T>>와 같은 관찰 가능한 데이터 홀더를 쓰는 것을 권장합니다. SnapShotStateList라는 방법도 있지만 UI 변경에 엄청나게 많은 예외 사항을 만나볼 수 있으므로 지금 상태에선 권장드리지 않습니다.
참고
상태 및 Jetpack Compose | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. 상태 및 Jetpack Compose 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 앱의 상태는 시간이 지남에 따라
developer.android.com
'안드로이드(Compose) > 개념' 카테고리의 다른 글
remember 강제 재실행하는 방법 (0) | 2024.04.11 |
---|---|
Compose에서 State를 복원하는 방법 (0) | 2024.04.04 |
State Hoisting(상태 호이스팅) (0) | 2024.03.27 |
Stateful 과 Stateless (0) | 2024.03.25 |
지원되는 기타 State 유형 (0) | 2024.03.20 |