--- 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) 또는 **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 by preferencesDataStore(name = "settings") class SettingsRepo(private val ds: DataStore) { private val keyTheme = stringPreferencesKey("theme") private val keyOnboarded = booleanPreferencesKey("onboarded") val theme: Flow = ds.data.map { it[keyTheme] ?: "system" } val onboarded: Flow = 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 { 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 by dataStore( fileName = "settings.pb", serializer = SettingsSerializer ) // 사용 val theme: Flow = 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]]