[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
---
|
||||
id: android-datastore-patterns
|
||||
title: Android DataStore — SharedPreferences 의 후계자
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [android, datastore, preferences, vibe-coding]
|
||||
tech_stack: { language: "Kotlin / Jetpack DataStore", applicable_to: ["Android"] }
|
||||
applied_in: []
|
||||
aliases: [Preferences DataStore, Proto DataStore, EncryptedSharedPreferences]
|
||||
---
|
||||
|
||||
# Android DataStore
|
||||
|
||||
> SharedPreferences 는 deprecated. **Preferences DataStore** (Map<String, Any>) 또는 **Proto DataStore** (typed) 사용. Coroutines + Flow 기반. 비밀은 **EncryptedSharedPreferences** 또는 Keystore.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- Preferences DataStore: key-value, type-unsafe. SharedPreferences 1:1 대체.
|
||||
- Proto DataStore: 타입 안전 schema. 복잡 객체.
|
||||
- 비밀: EncryptedSharedPreferences 또는 SQLCipher Room.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### Preferences DataStore
|
||||
```kotlin
|
||||
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||
|
||||
class SettingsRepo(private val ds: DataStore<Preferences>) {
|
||||
private val keyTheme = stringPreferencesKey("theme")
|
||||
private val keyOnboarded = booleanPreferencesKey("onboarded")
|
||||
|
||||
val theme: Flow<String> = ds.data.map { it[keyTheme] ?: "system" }
|
||||
val onboarded: Flow<Boolean> = ds.data.map { it[keyOnboarded] ?: false }
|
||||
|
||||
suspend fun setTheme(t: String) {
|
||||
ds.edit { prefs -> prefs[keyTheme] = t }
|
||||
}
|
||||
suspend fun setOnboarded(v: Boolean) {
|
||||
ds.edit { it[keyOnboarded] = v }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Proto DataStore (타입 안전)
|
||||
```protobuf
|
||||
// app/src/main/proto/settings.proto
|
||||
syntax = "proto3";
|
||||
option java_package = "com.example.app";
|
||||
option java_multiple_files = true;
|
||||
|
||||
message Settings {
|
||||
string theme = 1;
|
||||
bool onboarded = 2;
|
||||
int32 launch_count = 3;
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
object SettingsSerializer : Serializer<Settings> {
|
||||
override val defaultValue = Settings.getDefaultInstance()
|
||||
override suspend fun readFrom(input: InputStream): Settings = Settings.parseFrom(input)
|
||||
override suspend fun writeTo(t: Settings, output: OutputStream) = t.writeTo(output)
|
||||
}
|
||||
|
||||
val Context.settings: DataStore<Settings> by dataStore(
|
||||
fileName = "settings.pb",
|
||||
serializer = SettingsSerializer
|
||||
)
|
||||
|
||||
// 사용
|
||||
val theme: Flow<String> = ctx.settings.data.map { it.theme }
|
||||
ctx.settings.updateData { it.toBuilder().setTheme("dark").build() }
|
||||
```
|
||||
|
||||
### Encrypted (비밀)
|
||||
```kotlin
|
||||
val masterKey = MasterKey.Builder(ctx).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
|
||||
|
||||
val securePrefs = EncryptedSharedPreferences.create(
|
||||
ctx, "secure_prefs", masterKey,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||
)
|
||||
securePrefs.edit().putString("token", "...").apply()
|
||||
```
|
||||
|
||||
(EncryptedDataStore 는 정식 X — 위 패턴 또는 SQLCipher.)
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 데이터 | 저장 |
|
||||
|---|---|
|
||||
| 단순 설정 (theme, locale) | Preferences DataStore |
|
||||
| 복잡 객체 (다수 필드, nested) | Proto DataStore |
|
||||
| 검색 / 쿼리 필요 | Room |
|
||||
| 비밀 (token) | EncryptedSharedPreferences / Keystore |
|
||||
| 큰 파일 | File API |
|
||||
| 인메모리 임시 | ViewModel 또는 SavedStateHandle |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **SharedPreferences 신규 사용**: deprecated. DataStore.
|
||||
- **DataStore 작업을 main thread**: suspend / Flow 만 — 자동.
|
||||
- **MutableStateFlow 로 캐시 + DataStore 둘 다 변경**: 동기화 깨짐. 한 진실원.
|
||||
- **큰 데이터를 Preferences DataStore**: 매 변경 전체 rewrite. Proto 또는 Room.
|
||||
- **비밀을 Preferences DataStore**: 평문. Encrypted.
|
||||
- **runBlocking 으로 동기 read**: 메인 스레드 block. Flow + collectAsStateWithLifecycle.
|
||||
- **migration 안 함 (SharedPrefs → DataStore)**: 옛 데이터 사라짐. SharedPreferencesMigration 사용.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- 신규 = DataStore. 비밀은 EncryptedSharedPreferences.
|
||||
- 복잡 schema 면 Proto.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Android_Room_Patterns]]
|
||||
- [[Android_Hilt_DI_Patterns]]
|
||||
Reference in New Issue
Block a user