Android

[안드로이드] DataStore 이해하기

베르_최성훈 2024. 1. 8. 22:11

시작하기 전에

현재 페스타고 앱은 토큰 관리를 EncryptedSharedPreferences 로 하고 있다.

그러나 공식 문서는 SharedPreferences 를 DataStore 로 이전할 것을 권장한다.

이전하기 전에 DataStore 가 무엇인지 공부해보자!

 

이 글은 안드로이드 공식 문서를 바탕으로 작성되었습니다.

https://developer.android.com/topic/libraries/architecture/datastore?hl=ko

https://developer.android.com/codelabs/android-preferences-datastore?hl=ko



DataStore 란?

 

DataStore 는 Protocol Buffer 를 사용해 Key - value 쌍 또는 유형이 지정된 객체를 저장할 수 있는 저장소이다.

Kotlin Coroutine 및 Flow 를 사용해 비동기적이고 일관적인 트랜잭션 방식으로 데이터를 저장한다.

 

아직 어떤 의미인지 와닿지 않는다. 

 

잠깐! Protocol Buffer 란?

Protocol Buffers are language-neutral, platform-neutral extensible mechanisms for serializing structured data.구조화된 데이터 직렬화를 위한 언어 중립적이고 플랫폼 중립적인 확장 가능한 메커니즘이라고 한다.

 

Google에서 개발한 이 프로토콜은 데이터를 효율적으로 저장하고 전송하기 위한 목적으로 디자인 되었다고 한다.

 

 

공식문서에서는 다음과 같이 권장하고 있다.

 

SharedPreferences 를 사용해 데이터를 저장하고 있다면 DataStore 로 이전하는 것이 좋다.

그러나 복잡한 데이터 셋, 부분 업데이트, 참조 무결성을 지원해야한다면 DataStore 대신 Room 을 사용하라.

DataStore  소규모 단순 데이터 셋에 적합하며 부분 업데이트 참조 무결성 지원하지 않는다.

 

 

DataStore 의 종류

- Preferences DataStore : 키를 사용해 데이터를 저장하고 데이터에 엑세스한다. 이 구현은 Type Safety 를 제공하지 않으며 사전 정의된 스키마가 필요하지 않다.

 

- Proto DataStore : 맞춤 데이터 type 의 인스턴스로 데이터를 저장한다. Type Safety 를 제공하며 Protocol buffer 를 사용해 스키마를 정의해야 한다.

 

DataStore 를 올바르게 사용하려면?

1. 같은 프로세스에서 DataStore 인스턴스를 두 개 이상 만들지 않는다. 동일한 프로세스에서 특정 파일의 DataStore 가 여러 개 활성화되어 있다면 데이터 Read  Update  IllegalStateException 을 발생시킨다.

 

2. DataStore  Type 은 변경 불가능해야 한다. DataStore 에 사용된 Type 을 변경하면 모든 보장이 무효화되며 심각한 버그를 야기할 수 있다. 따라서 불변성을 보장하라.

 

3. 동일한 파일에서 SingleProcessDataStore 에서 MultiProcessDataStore 를 사용하지 마라

 

 

SharedPreferences vs DataStore

 

특히 Preferences DataStore  SharedPreferences 와 비슷하지만 여러 차이점이 존재한다.

 

- 데이터 업데이트를 트랜잭션 방식

- 현재 상태를 나타내는 Flow 노출

- 데이터 영구 메서드(apply, commit) 가 없음

- 변경 가능한 참조를 내부 상태에 반환하지 않는다.

- 타입 키가 있는 Map, MutableMap 과 같은 유사한 API 를 노출한다.

 

 요약하자면 SharedPreferences는 UI 스레드에서 호출하기에 안전해 보일 수 있는 동기 API가 있고, 오류 신호를 보내는 메커니즘이 없으며 트랜잭션 API가 없다. 거의 모든 단점을 DataStore 는 해결하며 대체한다. Datastore Kotlin 코루틴과 Flow를 사용하는 완전 비동기 API가 있으며, 데이터 이전을 처리하고, 데이터 일관성을 보장하고, 데이터 손상을 처리한다.

 

DataStore 만들기

DataStore 인스턴스는 preferencesDataStore 위임을 사용한다. 이 위임은 앱에서 이 이름을 가진 DataStore 인스턴스가 하나만 존재함을 보장한다.

 

private val Context.dataStore by preferencesDataStore(name = "user_preferences")

 

 

"production 앱에서 DataStore 인스턴스는 DataStoreFactory 를 사용해 인스턴스 사용을 필요로하는 클래스에 싱글톤으로 삽입되어야 한다."

 

Hilt 를 사용해 Repository 생성시 싱글톤으로 주입해 주면 될 것 같다. 아마 Factory 니깐 provides 로 예상된다.

 

Preferences DataStore 에서 데이터 읽기

Preferences DataStore 는 환경 설정이 변경될 때마다 emit 되는 Flow<Preferences> 에 저장된 데이터를 노출한다.

먼저 key 를 정의해야 한다.

 

private object PreferencesKeys {
  val SHOW_COMPLETED = booleanPreferencesKey("show_completed")
}

 

val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
	.catch { exception ->
    // 데이터 읽는 도중 에러가 발생하면 IOException 발생
    if (exception is IOException) {
        emit(emptyPreferences())
    } else {
        throw exception
    }
}.map { preferences ->
	// 정상적으로 읽었으면 값을 전달한다. Default 는 false 이다.
    val showCompleted = preferences[PreferencesKeys.SHOW_COMPLETED]?: false
    UserPreferences(showCompleted)
}

 

Preferences DataStore 에서 데이터 쓰기

데이터 쓰기를 위해 DataStore.edit(transform: suspend(MutablePreferences) -> Unit) 을 제공한다. 

이 함수는 트랜잭션 방식으로 상태를 업데이트할 수 있는 transform 블록을 허용한다.

 

transform 블록에 전달된 MutablePreferences 는 이전에 실행된 수정 사항으로 최신 상태가 된다. 블록에서 모든 변경 사항은 transform 완료 후 edit 이 완성되기 전에 디스크에 적용된다.

 

"변환 블록 외부에서 MutablePreferences 를 수정하지 마세요."

 

suspend fun updateShowCompleted(showCompleted: Boolean) {
    dataStore.edit { preferences ->
        preferences[PreferencesKeys.SHOW_COMPLETED] = showCompleted
    }
}

 

얘도 마찬가지로 디스크를 읽거나 쓰는 과정에 오류 발생시 IOException 을 발생시킨다.

 



SharedPreferences 에서 Preference DataStore 로 옮기기

 

DataStore 로 이전하려면 dataStore 빌더를 업데이트해 SharedPreferencesMigration 을 전달해야 한다. 자동이전 가능하며 이전은 데이터 엑세스 전에 실행된다. 이전에 성공해야 읽기 및 쓰기가 가능하다.

 

참고: 키들은 SharedPreferences 에서 한번만 이전되므로 이전 이후에는 SharedPreferences 를 사용해선 안된다.



마무리하며

갑자기 급하게 마무리한것 같나요? 

 

맞습니다... 다른 부분은 실제로 적용하면서 공부하는게 낫다고 판단했기 때문입니다.

그럼 이론은 여기까지하고 다음편으로 돌아오겠습니다ㅎㅎ

 

공부할 키워드

1. FULL Guide to Encryption & Decryption in Android (Keystore, Ciphers and more)

2. How to Encrypt DataStore in Android