📱 Android

[Android] SharedPreferences 대신 DataStore #1

콩드로이드 2025. 11. 23. 16:54

DataStore를 이번에 처음 제대로 써봤습니다..! 

SharedPreferences → EncryptedSharedPreferences → DataStore로 왜 넘어가야 하는지 궁금해서 정리해봤어요!

 


 

SharedPreferences가 왜 더 이상 추천되지 않을까 ?

1. 동기 방식이므로 ANR 위험성이 존재

: commit() / apply() 모두 파일 I/O 기반

  • commit() → 동기(blocking), 완료될 때까지 UI Thread 멈춤
  • apply() → 비동기처럼 보이지만 디스크 I/O는 백그라운드에서 처리

사실 자주 발생하진 않을 것 같은데, 저장량이 많거나 여러 쓰기 요청이 한 번에 몰리면 영향이 있을 수 있습니다 

 

2. 앱 내부 저장소에 XML 파일 형태로 저장되므로 값이 그대로 노출

: 이를 해결하기 위해 공식으로 지원되는 EncryptedSharedPreferences 가 있었는데 Deprecated됨 

 

-> 이러한 이유들로 현재 구글에서 공식적으로 권장하는 방법은 DataStore를 사용하는 것입니다 

 

 


DataStore란 ? 

SharedPreferences의 XML 기반·동기 저장 방식을 개선한 Jetpack 기반 비동기 Key-Value 저장소입니다 (Binary 포맷으로 저장)

사용법은 아래와 같습니다

 

1️⃣ library 연동

//toml 파일에 선언
datastore = { group = "androidx.datastore", name = "datastore-preferences", 
	version.ref = "datastore"} // 1.1.1 사용
    
//build.gradle(app) 선언
implementation(libs.datastore)

 

2️⃣ Context기반으로 접근가능한 datastore 생성

val Context.dataStore by preferencesDataStore(
    name = "test_datastore" // datastore 명칭
)
 

 

3️⃣ 비동기 형태로 save, get 함수 생성 

DataStore는 기본적으로 비동기 + 코루틴 기반이라, 값을 저장할 때도 suspend, 가져올 때는 Flow로 동작

suspend fun <T> save(
    context: Context,
    key: Preferences.Key<T>,
    value: T
) {
    context.dataStore.edit { prefs ->
        prefs[key] = value
    }
}


fun <T> get(
    context: Context,
    key: Preferences.Key<T>
): Flow<T?> {
   return context.dataStore.data.map { prefs ->
       prefs[key]
   }
}

 

 

4️⃣ 저장, 불러오기 

//위에서 생성한 함수 사용
/
save(context, stringPreferencesKey("token"), "토큰값")

get(context, stringPreferencesKey("token"))

 

사용법은 생각보다 꽤 간단했어요!

 


정리해보자면, SharedPreference와 DataStore의 차이는 아래와 같습니다

  SharedPreference DataStore
구조 XML 파일 Binary file
I/O 방식 동기 완전 비동기(Coroutine + Flow)
스레드 안전성 완벽히 안전하지 않음 철저히 스레드-safe
대용량 처리 비효율적 Stream 기반으로 안전하게 처리
타입 안정성
key-string 기반 , 비교적 느슨 키 자체에 타입 존재 

 

여기서 각각 타입 안정성만 좀 확인해보자면, 

[SharePreference] : 다른 타입 가져오려고 하면 런타임에서 오류 발생 

val prefs = context.getSharedPreferences("test", Context.MODE_PRIVATE)

prefs.edit()
    .putString("age", "20")   // age에 스트링 저장 
    .apply()


//값을 가져올 때 
val age: Int = prefs.getInt("age", 0)  //런타임에서 ClassCastException

 

[DataStore] : 타입 미일치 시 컴파일에서 오류 발생

왜냐하면, DataStore는 키가 타입을 정의하기 때문에, 저장할 때도, 읽을 때도 컴파일 단계에서 오류를 잡아줍니다! 

object PrefKeys {
    val AGE = intPreferencesKey("age")
    val NICKNAME = stringPreferencesKey("nickname")
}

prefs[PrefKeys.AGE] = "20"  //int에 string 넣으려고 해서 컴파일타임에서 오류

 


하지만, DataStore도 암호화가 자동으로 지원되진 않기 때문에 민감 정보 저장 시 권장사항은 Crypto API 와 DataStore를 같이 쓰는 것입니다

모든 정보를 암호화할 필욘 없지만 토큰과 같은 민감정보들, 그리고 개인정보들은 암호화를 사용해야겠죠..! 

암호화 방법에 대해서는 2탄 포스팅으로 돌아오겠습니다!