Jetpack Compose에서 Configuration Changes 후에도 앱의 UI State 유지하는 방법

HEETAE HEO·2023년 1월 15일
0
post-thumbnail
post-custom-banner

해당 글은 How to Retain Your App's UI State Across Configuration Changes In Jetpack Compose를 읽고 자료를 조사하여 작성하는 글입니다.

안드로이드 Jetpack Compose에서 UI 상태를 업데이트 하려면 특정 개체로 표현되는 Composable의 각 상태를 변경해야 합니다. 상태가 업데이트 될 때마다 재구성(리컴포지션)이 실행되고 각 상태가 렌더링 되어 화면에 시각적으로 표현되는 것입니다. 만약 화면 회전과 같은 구성 변경이 일어난다면 이때 이를 막아줄 추가적인 작업을 해주지 않는다면 parents Ativity 또는 프로세스가 다시 생성되고 모든 상태가 원래 상태로 되돌아가 데이터 변경 사항이 손실 됩니다.
그렇기에 리컴포지션에서 상태를 유지할 수 있을 뿐만 아니라 이 문제를 해결하기 위해 앱 프로세스를 재생성 하는 방법에 대해 작성하려고 합니다.

1. Jetpack Compose에서의 상태 보존 방법

리컴포지션에서 상태를 유지하기 위해 remember를 사용할 수 있습니다. 반면 프로세스 전체에서 상태를 유지 하려면 rememberSaveable 를 사용해야합니다. remember과 rememberSaveable은 SaveableStateRegistry로의 추가 변환 없이 각 객체를 직접 저장하는 Saver의 autoSaver 구현을 사용합니다.
saveableStateRegistry는 저장된 인스턴스 상태를 사용합니다. 이 상태는 viewModel 클래스 내에서 상태를 보존하여 이미 알고 있을 수 있으며 상태를 저장하고 복원합니다.

SaveableStateRegistry는 구성 요소가 저장된 인스턴스 상태 메커니즘을 사용하여 상태를 저장하고 복원할 수 있게 해줍니다. 해당 전체 코드는 아래의 링크를 타고 가시면 볼 수 있습니다.
[SaveableStateRegistry](https://androidx.tech/artifacts/compose.runtime/runtime-saveable/1.0.0-beta02-source/androidx/compose/runtime/saveable/SaveableStateRegistry.kt.html

2. Simple Object 저장 방법

간단한 객체를 saveableStateRegistry에 저장하는 방법에 대해 작성하겠습니다.

data class Person(
	val name : String,
    val age : Int,
    val hobby : Boolean
    )

다음과 같은 data class가 존재합니다.
객체를 rememberSaveable을 통해 상태 저장하려면 객체를 Parcelable로 선언해주어야 합니다. bundle에 담을 때 저장하지 못하는 형태가 있을 수 있기 때문에 Parcelize로 바꿔주는 것입니다. ( 예시 : MutableState는 SaveableStateRegistry 저장되지 않음 유형 지원 x)
그렇기 때문에 다음과 같이 코드를 바꿔줍니다.

@Parcelize
data class Person(
	val name : String,
    val age : Int,
    val hobby : Boolean
    ): Parcelable

그렇기에 SaveableStateRegistry로 저장해주기 위해 data class를 Parcelable로 감싸줍니다. 그 후 MutableState로 감싸고 rememberSaveable에 추가할 수 있습니다.

@Composable
private fun PersonComposable(){
	val examPerson = rememberSaveable {
    	mutableStateof(
        	Person (
            	name = "HEETAE",
                age = "25",
                hobby = true
                )
            )
        )
    }
}         

Class를 Parcelable로 선언할 수 없다면 어떻게 해야하나요??

경우에 따라 각 클래스를 Parcelable로 선언할 수 있는 선택이 없을 수 있습니다. 예를 들면 라이브러리에서 제공되어 변경할 수 없는 클래스를 사용하는 경우 이에 해당합니다. Parcelable을 확장할 수 없다는 가정으로 Person 클래스를 작성해보겠습니다. 이러한 조건에서는 rememberSaveable을 사용할 때 autoSaver를 사용할 수 없으므로 Saver 클래스의 자체 구현을 제공해야합니다.
클래스의 자체 구현을 통해 bundle을 저장할 수 있는 작은 Parcelable 부분으로 분할하는 방법을 사용할 수 있습니다.
이를 위해 mapSaver 기능을 사용할 수 있으며 예시 코드를 작성해보겠습니다.

val PersonSaver = run {
  val nameKey = "name"
  val ageKey = "age"
  val hobbyKey = "hobby"
  
  
  mapSaver{
    save = { person : Person -> 
              mapOf(
                nameKey to person.name,
                ageKey to person.age,
                hobbyKey to person.hobby
                )
              },
              restore = { restorationMap: Map<String, Any?> -> 
					Person(
						name = restorationMap[nameKey] as String, 
						age = restorationMap[ageKey] as Int,
						isCute = restorationMap[hobbyKey] as Boolean
						)
					}
    }  
}

보시다시피 각 클래스 매개변수에 대한 키를 선언한 다음 store 함수의 Map<String, Any> 안에 저장해야 합니다. 각 클랫를 복원할 때 해당 맵에서 읽고 복원 기능에서 각 클래스를 다시 빌드할 수 있습니다.

mapSaver외에도 다른 방법이 있습니다. 바로 listSaver 사용하는 것입니다.
mapSaver와 listSaver의 유일한 차이점은 특정 키에 의존하지 않고 인수 위치에 의존한다는 것입니다. 다음 코드 조각에서 listSaver 함수를 이용하여 PersonSaver에 대한 구현을 볼 수 있습니다.

val PersonSaver = run {
  listSaver {
    save = { person : Person ->{
        listOf(person.name, person.age, person.hobby)
      },
        restore = {restorationList : List <Any?> -> 
          Person (
            name = restorationList[0] as String,
            age = restorationList[1] as Int,
            isHobby = restorationList[2] as Boolean
          )
        }
    )
}

결론

이 글에서 리컴포지션뿐만 아니라 Activity 또는 프로세스 재생성에서도 복원하기 위해 앱 상태를 저장하는 방법에 대해 작성해보았습니다. rememberSaveable 함수를 사용하면 viewModels 를 사용하여 알 수 있는 것처럼 저장된 인스턴스 상태 내에 Parcelable 객체를 저장할 수 있습니다. 또한 Parcelable로 직접 변환할 수 없는 개체를 저장하는 방법에 대해서도 살펴보았습니다. rememberSaveable을 사용하게 된다면 더 이상 Configuration changes 이나 프로세스 중단으로 인한 손실 걱정 없이 앱 UI 상태를 변경할 수 있습니다. 그러나 이러한 동작이 꼭 필요로 하는 것은 아니기에 사용하기전 앱에 대한 평가를 하고 난뒤에 구현을 하여야합니다.

profile
Android 개발 잘하고 싶어요!!!
post-custom-banner

0개의 댓글