SharedPreference VS DataStore

반달·2024년 4월 23일

VS

목록 보기
1/1
post-thumbnail

들어가기에 앞서..

단잠 앱이 로그인 기능을 구현함으로 JWT token을 관리해주는 기능이 필연적으로 들어갔습니다.
때문에 refresh token을 저장할 공간이 필요합니다.
과거 201 앱의 경우는 SharedPreference를 사용하다가 보안적인 이슈로 EncryptedSharedPreference로 바꿔서 구현하였습니다.

물론 큰 문제는 없이 사용은 하였지만, 잠재되어있는 문제가 있을 수 있고 또한 아래와 같이 공식문서에 기재되어 있다보니 이번 기회에 차이를 알아보도록 하겠습니다.

현재 SharedPreferences를 사용하여 데이터를 저장하고 있다면 대신 Datastore로 이전하는 것이 좋습니다.
안드로이드 공식문서 발췌
필요에 따라 사용도 아닌 그냥 이전하는 것이 좋답니다.

그저 공식문서가 쓰지말라해서 사용하지 않는 것 보단 제가 설명할 수 있어야 한다 생각하기 때문에 이 글을 통해 정리할겸 작성하게 되었습니다.

SharedPreference?

SharedPreference는 API 레벨 1 부터 지원한 Key-Value 구조로 데이터 저장이 가능한 API입니다.
예를들어, 앱내 환경설정 값을 저장하는데 자주 사용되어왔습니다.

장점

  • 비교적 낮은 러닝커브를 가집니다.
  • Key-Value 구조로 쉽게 원하는 데이터를 찾아 올수 있습니다.

단점

- 스레드 안정성

SharedPreference는 특히 환경설정 값의 변경을 모니터링하기 위한 OnSharedPreferenceChangeListener를 통해 어느 정도의 비동기 지원을 제공하지만, 이 메커니즘에는 한계가 있습니다.
특히 OnSharedPreferenceChangeListener의 콜백은 메인 스레드에서 실행됩니다. 이러한 설계는 콜백 내에서 상당한 처리가 이루어지면 UI의 응답성을 방해하여 잠재적으로 애플리케이션 응답 없음(ANR) 오류가 발생하거나 사용자 상호 작용이 눈에 띄게 지연될 수 있음을 의미합니다.

- 에러 핸들링

SharedPreferences는 메서드에서 잡거나 선언할 필요가 없는 확인되지 않은 런타임 예외를 던지기 쉽습니다.
이러한 예외의 한 가지 일반적인 예는 ClassCastException입니다.
이 예외는 예상 데이터 유형과 SharedPreferences에서 검색된 실제 데이터 간에 불일치가 있을 때 발생합니다.
이러한 런타임 예외는 제대로 처리하지 않으면 충돌로 이어질 수 있으므로 앱 안정성에 심각한 위험을 초래할 수 있습니다.

- 데이터 일관성

SharedPreference의 근본적인 문제점은 원자성이 보장되지 않아 데이터 저장에 잠재적인 불일치가 발생할 수 있다는 점입니다.
SharedPreference은 트랜잭션에서 이루어진 모든 변경 사항이 분할 불가능한 단일 작업으로 함께 저장되도록 보장하지 않기 때문에 이 점에서 부족합니다.
이러한 단점은 쓰기 작업 중에 애플리케이션이 충돌하거나 디바이스가 꺼지면 일부 변경 사항은 저장되지만 다른 변경 사항은 손실될 수 있다는 것을 의미합니다.
결과적으로 앱의 데이터는 예상되는 수정 사항의 일부만 저장된 환경설정에 반영되는 일관되지 않은 상태가 될 수 있습니다. 이러한 불일치는 상태를 유지하거나 중요한 작업을 수행하기 위해 여러 관련 환경설정에 의존하는 애플리케이션에서 특히 문제가 됩니다.

DataStore?

Jetpack Datastore는 프로토콜 버퍼를 사용하여 키-값 쌍 또는 유형이 지정된 객체를 저장할 수 있는 데이터 저장소 솔루션입니다.
Datastore는 Kotlin 코루틴 및 Flow를 사용하여 비동기적이고 일관된 트랜잭션 방식으로 데이터를 저장합니다.

참고: 복잡한 대규모 데이터 세트, 부분 업데이트, 참조 무결성을 지원해야 할 경우에는 Datastore 대신 Room을 사용하는 것이 좋습니다. DataStore는 소규모 단순 데이터 세트에 적합하며 부분 업데이트나 참조 무결성은 지원하지 않습니다.

본격적으로 장단점을 살펴보기에 앞서 DataStore는 두 가지 형태로 나뉩니다.
알아보고 갑시다!

Preferences DataStore

Preferences DataStore는 키를 사용하여 데이터를 저장하고 데이터에 액세스합니다. 이 구현은 유형 안전성을 제공하지 않으며 사전 정의된 스키마가 필요하지 않습니다.

Proto Datastore

Proto Datastore는 맞춤 데이터 유형의 인스턴스로 데이터를 저장합니다. 이 구현은 유형 안전성을 제공하며 프로토콜 버퍼를 사용하여 스키마를 정의해야 합니다.

장점

비동기 처리

내부적으로 코루틴 플로우를 사용한 비동기처리가 구현되어 있기 때문에
UI 쓰레드가 장시간 블락되어 생길 수 있는 ANR 문제가 생길 위험을 크게 줄였습니다.

타입 안정성

프로토콜 버퍼를 사용하여 스키마를 정의한 Proto DataStore은 타입 안정성을 가질 수 있습니다.

에러 핸들링

IOException를 활용하여 오류 처리를 할 수 있습니다.

단점

Preference DataStore의 타입 안정성 보장 X

물론 SharedPreference보다는 가지는 장점이 많지만, 안타깝게도 스키마를 따로 지정하지 않는 Preference DataStore는 타입 안정성을 가지지 않습니다.

기본으로 제공하는 암호화가 없음(!!!!!)

보안적으로 예민할 수 있는 데이터를 위해 SharedPreference의 경우 구글에서 기본적으로 제공하는 암호화 매커니즘을 가진 EncryptedSharedPreference를 지원합니다.
안타깝게도 DataStore는 이러한 암호화 매커니즘을 기본적으로 제공하지 않습니다.
때문에 개발자가 직접 정의해줘야 하는데, 직접 정의 함으로써 생길수 있는 복잡성과 잠재적인 보안 취약성을 가질 수 있습니다..

결론

이렇게 정리해보고 나니까 왜 안드로이드 공식문서에서 DataStore로 이전하는 것이 좋다고 했는지 이해가 되었습니다.
거의 모든 면에서 DataStoreSharedPreference를 능가하는 모습을 보입니다. (아마 SharedPreference의 문제점을 인식한 구글이 Jetpack 라이브러리인 DataStore를 개발할 때 개선한 것 같습니다.)
하지만.. 이 글의 목적이었던 RefreshToken을 어떤 API를 써 보관할 것인가를 고려해 보았을 때 구글이 암호화 매커니즘을 기본적으로 제공하지 않는 DataStore를 사용하는 것이 맞을까? 하는 고민이 또 다시 생겨버렸습니다.
하지만 또 찾아보니 DataStore에 암호화를 개발자가 직접 정해주는 방법이 존재한다는 걸 알게되어 아마 다음 포스팅은 그 방법에 대해 작성할 것 같습니다.

긴 글 읽어주셔서 감사합니다!
피드백은 언제나 환영입니다!

참고

profile
깊이 있는 안드로이드 개발자가 되기 위해

0개의 댓글