반응형
컴포저블 함수 상태 관리
상태를 갖지 않는 컴포저블과 상태를 갖는 컴포저블 간의 차이점 이해
- 언제 어떠한 것을 선택할지 살펴본다.
상태를 갖거나 갖지 않는 컴포저블 함수 이해
상태 : 시간이 흐름에 따라 변하는 데이터
- UI는 항상 현재 데이터를 보여줘야만 한다는 것이 중요
- 따라서 값이 변경되면 반드시 UI에 알려야 함.
- 옵저버블 타입을 사용
State는 기본 인터페이스로, value라는 이름을 갖는 프로퍼티에 특정 타입의 값을 저장하는 객체인 값 홀더(value holder)를 정의
- 컴포저블 함수가 실행되는 동안 value가 변경될 때마다 재구성될 것이다.
- 내부적으로 RecomposeScope 인터페이스가 값의 변경 사항을 구독하고 있을 것이기 때문
- 값이 변경될 수 있게 하려면 MutableState의 구현체로 돼야만 한다
컴포저블 함수에서 상태 사용
컴포저블 함수가 값을 유지(기억)할 경우 상태를 가진(stateful)다고 이야기 한다.
@Composable
@Preview
fun SimpleStateDemo1() {
val num = remember { mutableStateOf(Random.nextInt(0, 10)) }
Text(text = num.value.toString())
}
- remember { }를 호출해 상태를 저장하고 =를 사용해 num에 할당한다.
- val로 저의하긴 했지만 num은 변경할 수 있는 값 홀더(홀더의 value는 읽기 쓰기가 가능)의 참조를 갖고 있기 때문에 num.value로 값을 변경할 수 있다.
@Composable
@Preview
fun SimpleStateDemo2() {
val num by rememeber { mutableStateOf(Random.nextInt(0,10)) }
Text(text = num.toString())
}
- by를 사용해 상태 자신을 num에 할당하지 않고 값을 저장했다.
- 이러한 방식은 .value를 사용하지 않게 해서 코드를 더욱 간결하고 이해하기 쉽게 만들어준다.
- 그러나 num을 변경하고자 한다면 val → var로 수정해야 한다.
remember { }
@Composable
inline fun <T> remember(crossinline calculation: @DisallowComposableCalls () -> T): T =
currentComposer.cache(false, calculation)
- 최상위 읽기 전용 currentComposer 프로퍼티
- Composer 인스턴스를 참조
- 컴포즈 코틀린 컴파일러 플러그인의 대상이 되며 코드 생성 도우미에서 사용
- 런타임은 컴파일러에서 호출을 생성할 것이라고 가정하고, 유효성 로직을 포함하지 않기 때문에 직접 호출해서는 안된다.
- Cache()
- Composer의 확장 함수
- 컴포지션의 데이터에 있는 값을 저장한다.
- 따라서 remember{ }는 내부 상태를 생성한다.
- 결국 remember{ }를 포함하는 컴포저블 함수는 상태를 갖게 된다.
- calculation
- 기억할 값을 생성하는 람다 표현식
- 이 표현식은 구성되는 동안 단 한번만 평가된다.
- 그 다음 remember{ }를 호출하면 항상 이 값이 반환된다.
계산을 다시해야 한다면?
- key값을 이용하여 재평가 되도록 한다.
- remember{ }에는 다수의 키를 전달할 수 있다.
- 이중 하나가 변경됐다면 계산은 재평가된다.
상태를 갖지 않는 컴포저블 함수 작성
@Composable
fun SimpleStatelessComposable2(text: State<String>) {
Text(text = text.value)
}
- text를 매개변수로 상태를 전달받지만 저장하지 않고 다른 상태를 기억하지도 않는다.
- 동일한 매개변수로 여러번 호출하면 동일한 결과를 얻는다.
- 이러한 함수를 멱등성을 가졌다고 한다.
컴포저블 함수는 다음사항을 준수해야 한다.
빠름 :
- 무거운 연산을 하지말아야 한다.
- 웹서비스나 I/O도 호출해서는 안된다.
- 데이터는 전달받는 형식이 돼야 한다.
부수 효과에서 자유로움 :
- 전역 프로퍼티를 수정하거나 의도치 않은 옵저버블 효과를 생상하지 말아야 한다.
멱등성 :
- remember{ }를 사용하지 않고 전역 프로퍼티에도 접근하지 않으며 예측 불가능한 코드를 호출하지 말아야 한다.
결론 :
remember { }나 상태를 기억하는 다른 함수에 의존하지 않는, 상태를 갖지 않는 컴포저블을 만들도록 노력
상태 호이스팅과 이벤트 전달
상태 호이스팅은 상태를 갖지 않는 컴포저블로 만들고자 상태를 상위로 이동시키는 패턴이다.
이벤트는 프로그램의 일부에서 어떤 일이 발생했음을 알린다.
환경설정 변경에도 데이터 유지
- 액티비티가 종료됐다가 금세 다시 생성되는 경우
- 예) 디바이스 회전, 언어 변경 등
- 이 경우 데이터는 보존돼야 한다.
기존방식
- onSavedInstanceState() - 액티비티가 (일시적으로) 조욜될 때 호출된다.
- onRestoreInstanceState() - 이전에 상태 인스턴스가 저장된 적이 있는 경우에만 호출
반면 컴포저블은 일반적으로 최상위 함수로 구현
- 컴포즈 앱에서는 임시로 상태를 저장하고자 rememberSaveable { } 을 사용
- 팩토리 함수에서 생성된 값을 기억한다.
- 내부적으로는 savedInstanceState매커니즘이 사용
ViewModel사용
ViewModel 클래스는 생명주기를 고려하면서 UI 와 관련된 데이터를 저장하고 관리한다
- 화면 회전과 같은 환경 설정 변경에서도 유지 된다는 의미
컴포저블 함수 내부에서 ViewModel 클래스에 접근하려면 viewModel() 컴포저블을 호출해야 한다.
val viewModel: MyViewModel = viewModel()
LiveData는 다음과 같은 방식으로 상태로 변환해 사용할 수 있게 된다.
val state3 = viewModel.text.observeAsState()
@Composable
fun <T> LiveData<T>.observeAsState(): State<T?> = observeAsState(value)
- observeAsState()는 LiveData의 확장 함수다.
- LiveData 인스턴스의 value 프로퍼티를 매개변수를 허용하는 다른 observeAsState()에 전달
- State<T?>를 확인 했는가?
- 앞절 예제에서 State<String?>을 전달받게 정의한 이유.
- State<String>을 remember{ }, remembeerSaveable{ } 에서 사용할 수 있게하려면 형변환을 해야함. as State<String>
- 필자는 State<String?>을 사용하는게 더 유리하다고 생각한다고 함.
- 규모가 큰 앱에서는 ViewModel을 사용한다고 함.
참조 : 젯팩 컴포즈로 개발하는 안드로이드 UI
반응형
'코틀린 & Java > 컴포즈 Compose' 카테고리의 다른 글
컴포즈(compose) 컴포저블 함수 상태 관리 (0) | 2024.01.29 |
---|---|
컴포즈(compose) 앱 스타일링 (0) | 2024.01.29 |
컴포즈(Compose) UI 요소 배치 (0) | 2023.08.23 |
컴포즈(Compose) 컴포즈 핵심 원칙 자세히 알아보기 (0) | 2023.08.16 |
컴포즈(Compose) 선언적 패러다임 이해 (0) | 2023.08.16 |