SharedPreferences의 한계점
Datastore가 나오기 전까지 안드로이드에서는 가벼운 데이터를 key-value 쌍으로 저장하기 위해 SharedPreferences를 사용했다. SharedPreference는 다양한 한계점이 있었다. SharedPreference는 비동기 작업을 제대로 해주지 않으면 ANR을 발생시킬 수 있으며, 오류가 생길 시 확인이 불가능했으며, 런타임에 예외가 생기면 런타임 애러가 발생해 잘못 사용하면 앱이 강제 종료될 수도 있었다. 또한 strong consistency가 보장되는 api가 없어 다중 스레드 환경에서 다른 결과값이 생길 수 있었다. 이러한 문제 외에도 type safety가 보장되지 않아 어떤 데이터가 저장되고 추출되는지를 일일히 데이터로 type converting(형 변환) 해주어야 했다.
SharedPreference의 한계점
1. UI스레드에서 호출 할 수 있도록 API가 설계되었지만, UI 스레드를 블로킹 해 ANR을 발생시킬 수 있다.
2. strong consistency가 보장되지 않아 다중 스레드 환경에서 안전하지 않다.
3. type safety가 보장되지 않는다.
이러한 한계점으로 인해 SharedPreference는 안드로이드에서 데이터를 저장하기 위해 다양한 문제를 일으켰다. Datastore가 등장하기 이전에는 이에 대한 대안이 없어서 안전하게 처리하기 위해 다양한 처리를 했어야 했다. Datastore은 SharedPreference의 한계점을 극복하기 위해 등장했다.
Datastore의 경쟁력
Datastore은 SharedPreference의 다양한 문제를 해결함은 물론 동작을 개선했다. 첫 째, Datastore은 Coroutine을 사용해 동시성 프로그래밍에 최적화된 API를 제공한다. 둘 째 다중 스레드 환경에서 Strong Consistency를 보장한다. 마지막으로 Proto Datastore에 한해 가장 중요한 특성인 Type Safety를 보장한다. 이들 가각에 대해 아래에서 다룬다.
Coroutine의 사용
Datastore은 경량 스레드 모델을 구현하는 Coroutine을 사용해 내부를 구현함으로써 더욱 효율적으로 데이터를 저장할 수 있도록 하였다. Datastore은 기존에 UI 스레드에서 호출되어 ANR을 발생시킬 수 있었던 SharedPreference와 달리 그 내부에서 Coroutine의 IO Dispatcher을 사용해 IO 를 담당하는 스레드풀에서 데이터를 조작하도록 강제했다. 이 때문에 Datastore에서는 IOException이 발생할 수 있다. [그림1]은 DataStore 인터페이스 내부의 코드로 IO Exception이 발생할 수 있다고 써져있는 것을 확인할 수 있다.
또한 Datastore은 [그림2]에서 볼 수 있듯이 안드로이드 ORM라이브러리인 Room과 같이 Coroutines의 Flow을 사용해 데이터를 추출할 수 있도록 만듦으로써 데이터를 입력하는 것은 물론 데이터를 출력하는 것까지 모두 Coroutine을 사용할 수 있도록 만들었다.
class OnBoardingStateRepository @Inject constructor(private val configSettings: DataStore<OnBoardingState>) {
fun getConfigSettings(): Flow<OnBoardingState> = configSettings.data
}
위의 코드는 Proto Datastore에서 데이터를 출력하는 예제로 OnBoardingState로 타입이 선언된 데이터가 Flow로 출력 됨을 볼 수 있다.
Strong Consistency 보장
또한 Datastore은 strong consistency가 보장되는 Transaction API를 제공한다. strong consistency란 다중 스레드 환경에서 동일한 데이터가 추출될 수 있도록 만드는 것을 뜻한다. 이를 통해 다중 스레드 환경에서 안전하게 데이터를 입력하거나 조회할 수 있다. 이는 동시성 프로그래밍에서 가장 중요한 요소이다.
Type Safety 보장
마지막으로 Datastore은 Type Safety를 지원한다. Type Safety란 데이터가 클래스(타입)를 기준으로 입력되고 출력될 수 있는지에 대한 것이다. Type Safety를 지원한다는 것은 저장될 객체를 직접 직렬화 하지 않고도 저장할 수 있다는 것을 뜻하며 안전하게 데이터를 Datastore로부터 꺼내올 수 있다는 것을 뜻한다. Type Safety 하나만으로도 Datastore을 써야만 할 이유가 될 정도로 중요한 특성이다. 하지만 두가지 Datastore인 Preference Datastore과 Proto Datastore 중 Proto Datastore만 Type Safety를 지원한다.
정리
위의 내용을 표로 정리하면 다음과 같다.
SharedPreference | Preference Datastore | Proto Datastore | |
데이터 추출 | 선언적으로 하나씩 추출 | Coroutines Flow로 데이터 스트림으로 받음 | Coroutines Flow로 데이터 스트림으로 받음 |
동시성 프로그래밍 방식 | 직접 설계 해야함 | Coroutines | Coroutines |
UI 스레드 호출 안정성 | ANR 발생 가능 | Dispatchers.IO에서 호출되도록 강제하여 안전 | Dispatchers.IO에서 호출되도록 강제하여 안전 |
Strong Consistency | 보장하지 않음 | 보장 | 보장 |
Type Safety | 보장하지 않음 | 보장하지 않음 | 보장 |