본문 바로가기

IT/Android

Android DataStore

728x90
반응형

출처 : https://medium.com/androiddevelopers/introduction-to-jetpack-datastore-3dc8d74139e7

Introduction to Jetpack DataStore

DataStore 는 preference 또는 application state 같은 작은 양의 data 를 안전하고 일관되게 저장하기 위한 방법을 제공하는 Jetpack library 입니다. DataStore 는 asynchronous data 저장을 가능하게 하는 Kotlin coroutineFlow를 기반으로 합니다. Thread-safe 하고 non-blocking 이기 때문에 SharedPreferences 를 대체하는 것이 목적입니다. DataStore 는 두가지 다른 implementations 를 제공합니다. Proto DataStore는 typed objects(backed by protocol buffers) 저장하고, Preferences DataStore 는 key-value pairs 를 저장합니다. 이후로는 별도의 언급이 없는 한 DataStore 는 두 implementation 모두를 말합니다.

이 post 에서 DataStore 가 어떻게 동작하는지, 제공하는 implementation 과 개별적인 사례 등을 살펴보겠습니다. 또한, SharedPreference에 비해 어떤 이점과 개선점을 제공하는지, DataStore 를 사용할 가치가 있는 이유에 대해서도 살펴보겠습니다.

 

DataStore vs SharedPreferences

대부분 SharedPreference 를 앱 구현시 사용했을 것입니다. 또한 SharedPreference 를 사용하면서 재현하기 어려운 문제를 경험해봤을 수도 있습니다. 여기서 재현하기 어려운 문제는 uncaught exception 때문에 발생한 이상한 crash들을 analytics 에서 발견하거나, 호출할 때 UI thread 를 block 하거나 일관되지 않은 persisted data 가 발생하는 이런 문제들입니다. DataStore 는 이런 모든 이슈들을 해결하기 위해 만들어졌습니다.

SharedPreference 와 DataStore 의 직접적인 비교를 살펴봅시다.

 

Comparing DataStore implementations with SharedPreferences

 

Async API

대부분의 data storage API 들에서, data 가 수정되었을 때 자주 asynchronously 알림을 받을 필요가 있습니다. SharedPreference 일부 async 를 제공하지만, 오직 OnSharedPreferenceChangeListener 를 통해 바뀐 값에 대한 update 만 제공합니다. 하지만, 이러한 callback 은 여전히 main thread 에서 호출됩니다.마찬가지로, 만약 파일저장 작업을 background 로 넘기려면 SharedPreference 의 apply() 를 사용할 수 있지만, fsync() 에서 UI thread 가 block 되고 이는 잠재적으로 버벅거림 및 ANR 을 유발시킵니다. 이것은 언제든지 service 가 start 또는 stop되거나 activity 가 pause 또는 stop 될 때마다 발생할 수 있습니다. 이에 비해, DataStore 는 Kotlin coroutine 과 Flow 의 강력함을 사용하여, data 를 조회하고 저장하기 위한 완전한 asynchronous API 를 제공하여 , UI thread blocking 의 위험을 줄입니다. Kotlin FLow에 친숙하지 않은 사람들에게는 asynchronously 계산할 수 있는 값의 stream 일 뿐입니다.

 

Synchronous work

SharedPreference API 는 즉시 사용 가능한 synchronous 작업을 지원합니다. 하지만, SharedPreference 의 persisted data 를 수정하기 위한 synchronous commit() UI thread 에서 호출하는 것이 안전해 보일 수 있지만, 실제로는 heavier I/O operations 을 수행합니다. 이것은 ANR 및 UI 버벅거림을 만드는 , 발생할 수 있고, 자주 발생하는 , 위험한 scenario 입니다. 이것을 막기 위해, DataStore 는 즉시 사용 가능한 synchronous 지원을 제공하지 않습니다. DataStore 는 preference 를 파일에 저장하고 별도로 지정하지 않는 한 내부적으로 Dispatchers.IO 에서 모든 data 작업을 수행하여 UI thread 를 unblock 상태로 유지합니다.

하지만, 나중에 살펴보겠지만 corotine builder 의 약간의 도움으로 DataStore 와 synchronous 작업을 결합하는 것이 가능합니다.

 

Error handling

SharedPreference 는 parsing error 를 runtime exception 으로 throw 하여 앱이 crash 에 취약해질 수 있습니다. 예를 들면, ClassCastException 은 잘못된 data type 이 API 에 요청되었을 때 발생하는 흔한 exception 입니다. DataStore 는 Flow 의 error signalling mechanism 에 의존하여 data 를 읽거나 쓸 때 발생하는 exception 을 catching 하는 방법을 제공합니다.

 

Type safety

data 를 저장하거나 가져오기 위해 Map 을 이용한 key-value pair 는 type safety protection 을 제공하지 않습니다. 하지만, Proto DataStore 를 사용하면 data model 에 대한 schema 를 미리 정의하고 full type safety(전체 유형 안전성) 의 추가 이점을 얻을 수 있습니다.

 

Data consistency

SharedPreferences의 atomicity(원자성) 보장이 부족하기 때문에 항상 어디서나 반영되는 data 수정에 의존할 수 없습니다. 특히 이 API의 요점은 영구적인 data 저장이기 때문에 위험할 수 있습니다. 이에 비해 DataStore의 완전한 트랜잭션 API는 원자적 읽기-수정-쓰기 작업으로 data가 update 되므로 강력한 ACID 보장을 제공합니다. (ACID : atomicity, consistency, isolation, durability) 또한 완료된 모든 update가 읽기 값에 반영된다는 사실을 반영하여 "쓰기 후 읽기" 일관성을 제공합니다.

 

Migration support

SharedPreferences에는 기본 제공 migration mechanism 이 없습니다 — 지루하고 오류가 발생하기 쉬운 값을 이전 저장소에서 새 저장소로 다시 매핑한 다음 정리하는 것은 사용자의 몫입니다. 이 모든 것은 data type 불일치 문제가 쉽게 발생할 수 있으므로 runtime exceptions 의 가능성을 높입니다. 그러나 DataStore는 SharedPreferences에서 DataStore로의 migration을 위해 제공된 구현과 함께 data 를 쉽게 migration 하는 방법을 제공합니다.

 

Preferences vs Proto DataStore

DataStore가 SharedPreferences보다 어떤 이점을 제공하는지 살펴보았으므로 Preferences와 Proto DataStore의 두 가지 구현 중에서 선택하는 방법에 대해 알아보겠습니다.

 

Preferences DataStore는 schema를 미리 정의하지 않고 key-value 쌍을 기반으로 data 를 읽고 씁니다. SharedPreferences와 유사하게 들릴 수 있지만 DataStore가 제공하는 위에서 언급한 모든 개선 사항을 염두에 두십시오. 이름에 "Preferences” 을 함께 사용하는 것에 속지 마십시오. 이들은 공통점이 없으며 완전히 별개의 두 API에서 제공됩니다.

 

Proto DataStoreProtocol Buffers에 의해 backup 되는 typed objects를 저장하므로 type safety을 제공하고 keys가 필요하지 않습니다. (Protocol Buffers는 구조화된 data 를 직렬화하기 위한 언어 중립적, 플랫폼 중립적 확장 mechanism입니다). Protobufs는 XML 및 기타 유사한 데이터 형식보다 빠르고, 작고, 간단하고, 덜 모호합니다. 이전에 사용하지 않았다면 두려워하지 마십시오! 이것들은 배우기 매우 간단합니다. Proto DataStore를 사용하려면 새로운 직렬화 mechanism 을 배워야 하지만 그 이점, 특히 type safety 은 그만한 가치가 있다고 생각합니다.

 

Comparing DataStore implementations

 

둘 중 하나를 선택할 때 다음 사항을 고려해야 합니다:

  • data 읽기 및 쓰기를 위해 key-value 쌍으로 작업하고 있는 경우, 최소한의 변경만으로 SharedPreferences에서 빠르게 migration 하고 DataStore의 향상된 기능을 활용하며 type safety checks 없이도 충분히 안심할 수 있다면 Preferences DataStore를 사용할 수 있습니다.
  • 향상된 가독성의 이점을 추가로 얻기 위해 프로토콜 버퍼를 학습하고 data 가 enum 이나 lists 와 같은 보다 복잡한 classes 로 작업해야 하며 full type safety 지원을 받으려면 Proto DataStore를 사용해 볼 수 있습니다.

 

 

DataStore vs Room

"음, data를 저장하기 위해 Room을 사용하는 것이 어떨까요?"라고 물을 수 있습니다. 그리고 그것은 공정한 질문입니다! 그럼 이 모든 것에서 Room이 어디에 적합한지 봅시다.

 

수십 KB보다 큰 복잡한 datasets 로 작업해야 하는 경우 여러 data table 간에 부분 update 또는 참조 무결성이 필요할 수 있습니다. 그럴 경우에는 Room 사용을 고려해야 합니다.

하지만 preferences 나 app state 와 같은 더 작고 단순한 datasets 로 작업하므로 부분 update나 참조 무결성이 필요하지 않다면 DataStore를 선택해야 합니다.

 

How to choose between DataStore and Room

 

To be continued

DataStore의 작동 방식, 변경 사항 및 개선 사항, 두 가지 구현 중에서 결정하는 방법 등 DataStore에 대해 더 자세히 알아보았습니다. 다음 두 블로그 게시물에서는 데이터 생성, 읽기, 쓰기, 오류 처리, SharedPreferences에서 migration 하는 방법인 Proto 및 Preferences DataStore에 대해 자세히 설명합니다. 계속 지켜봐 주세요!

728x90
반응형