DataStore: 개념, 데이터 저장 기본 사항 및 SharedPreferences 대체

저자: IT Sectr 게시일: 2026-03-12 읽는 시간: 9 분

DataStore는 Android 애플리케이션에서 소량의 데이터를 저장하기 위해 설계된 Jetpack 라이브러리의 구성 요소입니다. SharedPreferences와 달리 비동기적으로 작동하며 동시 액세스 시 데이터 일관성을 보장합니다. Google, 2024에 따르면 DataStore는 Kotlin Coroutines와 Flow를 사용하여 메인 스레드에 안전하고 반응형 아키텍처에 적합합니다.

핵심 포인트

  • DataStore — 비동기 API와 Protocol Buffers를 통한 데이터 유형을 갖춘 SharedPreferences 대체
  • Preferences DataStore — Flow 및 트랜잭션을 통한 읽기를 지원하는 간단한 키-값 저장소
  • Proto DataStore — 자동 스키마 마이그레이션이 있는 형식화된 저장소
  • SharedPreferences — 대용량에서 UI 스레드를 차단하는 동기 API
  • 마이그레이션은 SharedPreferencesMigration 인터페이스를 통해 데이터 손실 없이 수행됩니다

DataStore란?

DataStore는 Android에서 로컬 데이터 저장을 위한 Google의 솔루션으로, 2020년에 SharedPreferences의 대안으로 발표되었습니다. 두 가지 모드를 지원합니다: Preferences DataStore(간단한 키-값 쌍)와 Proto DataStore(Protocol Buffers 기반 형식화된 스키마).

주요 장점은 완전한 비동기성입니다: 모든 읽기 작업은 Kotlin Coroutines에서 Flow를 반환하고 쓰기는 코루틴 컨텍스트에서 수행됩니다. 이는 대용량 데이터를 처리할 때 SharedPreferences의 일반적인 문제였던 메인 스레드 차단을 제거합니다.

DataStore는 작업의 원자성을 보장합니다: 트랜잭션 모델 덕분에 동시 쓰기가 데이터 손실을 초래하지 않습니다. 두 구성 요소가 동시에 동일한 값을 수정하면 DataStore는 compare-and-swap 메커니즘을 통해 충돌을 올바르게 처리합니다.

Google I/O 2023에 따르면 DataStore는 새로운 Android 프로젝트의 40%에서 사용되며, Google은 설정 저장 안정성이 필요한 모든 애플리케이션에서 SharedPreferences에서 마이그레이션할 것을 권장합니다.

DataStore 아키텍처

DataStore의 핵심에는 SingleProcessDataStore가 있습니다 — 단일 프로세스 내에서 작동하는 구현입니다. 파일 수준 잠금을 사용한 파일 기반 스토리지를 사용합니다: 데이터를 쓸 때 파일이 잠겨 동시 액세스 시 손상을 방지합니다.

DataStore는 역직렬화 오류를 자동으로 처리합니다: 파일이 손상된 경우 기본값을 반환하고 파일을 덮어씁니다. 이 동작은 DataStore 생성 시 설정할 수 있는 corruptionHandler를 통해 구성 가능합니다.

DataStore가 해결하는 SharedPreferences의 문제

SharedPreferences는 세 가지 근본적인 문제를 가지고 있습니다: 메인 스레드에서의 동기 디스크 읽기, 동시 쓰기에 대한 원자성 보장 부족, 변경 사항을 반응적으로 추적할 수 없는 점. DataStore는 세 가지를 모두 해결합니다: 관찰에는 Flow, 원자성에는 파일 잠금, 스레드 안전성에는 비동기 API.

Android에서 DataStore는 어떻게 작동하나?

DataStore는 기기 내부 저장소의 파일에 데이터를 저장합니다. Preferences DataStore는 SharedPreferences와 유사한 파일 형식을 사용하지만 무결성 검사를 위한 추가 메타데이터가 있습니다. Proto DataStore는 바이너리 Protocol Buffers 형식을 사용하여 파일 크기를 줄이고 직렬화 속도를 높입니다.

데이터를 읽을 때 DataStore는 파일 전체를 한 번에 메모리에 로드한 후 구독자가 Flow를 통해 현재 상태를 받습니다. 변경 사항은 모든 활성 구독자에게 자동으로 브로드캐스트됩니다 — SharedPreferences처럼 수동으로 리스너를 등록할 필요가 없습니다.

Preferences DataStore 작동 방식

Preferences DataStore는 Map 기반의 내장 직렬화 메커니즘을 사용합니다. 각 항목은 문자열과 기본 유형(Int, Boolean, Float, Long, String, Set)의 쌍입니다. 데이터는 SharedPreferences와 유사한 XML 파일에 저장되지만 파일 잠금을 통한 원자적 쓰기가 적용됩니다.

Preferences DataStore 생성 예: Context의 preferencesDataStore 확장이 파일 이름으로 싱글톤을 생성합니다. 반복 호출 시 동일한 인스턴스가 반환됩니다 — 이는 파일 중복과 다른 저장소 인스턴스 간의 혼란을 제거합니다.

Proto DataStore 작동 방식

Proto DataStore는 .proto 파일을 통한 데이터 스키마 정의와 protobuf 플러그인을 사용한 컴파일이 필요합니다. 생성된 Java 클래스는 모든 필드의 단일 진입점으로 사용되어 SharedPreferences에서 흔한 키 오타를 제거합니다.

Proto DataStore 스키마는 한 번 정의되며 이전 데이터를 잃지 않고 새 필드 추가를 지원합니다. 앱의 새 버전이 기본값이 있는 필드를 추가하면 이전 파일이 올바르게 역직렬화됩니다 — 하위 호환성이 프로토콜에 내장되어 있습니다.

Preferences DataStore와 Proto DataStore 비교

Preferences DataStoreProto DataStore 중 선택은 데이터 복잡성과 유형 지정 요구 사항에 따라 달라집니다. 두 옵션 모두 비동기 및 트랜잭션이지만 유형 안전성과 직렬화 성능에서 다릅니다.

특성Preferences DataStoreProto DataStore
유형 지정약함 (키-값)강함 (생성된 클래스)
직렬화XML (내장)Protocol Buffers (protobuf)
파일 크기큼 (읽기 가능한 XML)작음 (바이너리)
복잡성낮음 (.proto 불필요)중간 (.proto 필요)
스키마 마이그레이션스키마 없음자동 (proto)
호환성SharedPreferences (마이그레이션 통해)Proto DataStore만

Preferences DataStore를 선택해야 하는 경우

Preferences DataStore는 간단한 설정에 적합합니다: 기능 플래그, 인증 토큰 문자열, 앱 실행 횟수. 데이터가 적고(최대 10~15개 키) 엄격한 스키마가 필요하지 않은 경우 Preferences DataStore는 protobuf 플러그인 연결 없이 최소한의 진입 장벽을 제공합니다.

Proto DataStore를 선택해야 하는 경우

Proto DataStore는 데이터 구조가 복잡하거나 앱 버전 간에 변경될 수 있는 경우 정당화됩니다. 예: 사용자 프로필 설정 또는 20개 이상의 필드가 있는 A/B 테스트 구성. Protobuf는 강력한 유형 지정과 자동 마이그레이션을 제공하여 키 불일치로 인한 런타임 오류를 제거합니다.

SharedPreferences에서 DataStore로 마이그레이션하는 방법

Google은 SharedPreferencesMigration 클래스를 통해 내장 마이그레이션 메커니즘을 제공합니다. 마이그레이션은 앱 업데이트 후 첫 실행 시 한 번 수행됩니다: DataStore가 SharedPreferences에서 데이터를 읽어 자체 형식으로 쓰고 마이그레이션을 완료로 표시합니다.

마이그레이션은 사용자 정의 변환을 지원합니다: SharedPreferences의 키가 원하는 DataStore 키와 일치하지 않는 경우 SharedPreferencesMigration을 통해 변환 함수를 지정할 수 있습니다. 이를 통해 마이그레이션 중 키 이름 변경 및 데이터 유형 변경이 가능합니다.

단계별 마이그레이션

첫째, build.gradle에 DataStore를 추가하고 마이그레이션과 함께 DataStore 인스턴스를 생성합니다: SharedPreferencesMigration은 SharedPreferences 파일 이름과 전송할 키 집합을 허용합니다. 둘째, SharedPreferences를 통해 작동하는 모든 코드를 제거하고 DataStore 호출로 대체합니다. 셋째, 마이그레이션을 테스트합니다: 첫 실행 시 DataStore에 데이터가 나타나야 하며 이전 SharedPreferences 파일은 더 이상 사용되지 않아야 합니다.

kotlin
val Context.dataStore by preferencesDataStore(
    name = "settings",
    produceMigrations = { context ->
        listOf(
            SharedPreferencesMigration(context, "old_prefs")
        )
    }
)

코드에서 DataStore 사용 예제

DataStore는 기존 프로젝트에 쉽게 통합됩니다. 아래는 Preferences DataStore와 Proto DataStore에 대한 실용적인 예제입니다 — 둘 다 데이터 읽기, 쓰기 및 반응형 관찰을 보여줍니다.

Preferences DataStore: 설정 읽기 및 쓰기

이 예제에서 Preferences DataStore는 세 가지 설정을 저장합니다: 다크 테마, 사용자 이름, 실행 횟수. 읽기는 Flow를 반환하는 .data 확장을 통해 수행됩니다. 쓰기는 변경의 원자성을 보장하는 .edit 서스펜드 함수를 통해 수행됩니다.

kotlin
val Context.settingsDataStore by preferencesDataStore(name = "settings")

val isDarkMode: Flow<Boolean> = settingsDataStore.data
    .map { preferences ->
        preferences[booleanPreferencesKey("dark_mode")] ?: false
    }

suspend fun toggleDarkMode() {
    settingsDataStore.edit { prefs ->
        val current = prefs[booleanPreferencesKey("dark_mode")] ?: false
        prefs[booleanPreferencesKey("dark_mode")] = !current
    }
}

Proto DataStore: 스키마 및 사용법

Proto DataStore는 .proto 파일 정의가 필요합니다. 컴파일 후 읽기 및 쓰기에 사용되는 UserSettings 클래스가 생성됩니다. 스키마의 버전 마이그레이션은 동일한 .proto 파일에 설명되고 자동으로 적용됩니다.

kotlin
// user_preferences.proto
syntax = "proto3";

message UserPreferences {
    string display_name = 1;
    int32 notification_count = 2;
    bool notifications_enabled = 3;
}

// DataStore에서 읽기
val userPreferencesFlow: Flow<UserPreferences> =
    protoDataStore.data

// 새 값 쓰기
suspend fun updateDisplayName(name: String) {
    protoDataStore.updateData { prefs ->
        prefs.toBuilder()
            .setDisplayName(name)
            .build()
    }
}

변경 사항의 반응형 관찰

DataStore는 ViewModel을 통해 MVVM 아키텍처와 통합됩니다. DataStore의 Flow는 .stateIn을 통해 수집되어 UI에서 사용됩니다. 데이터가 변경될 때마다 UI가 자동으로 업데이트됩니다 — 수동 업데이트나 LiveData가 필요하지 않습니다.

kotlin
class SettingsViewModel(
    private val dataStore: DataStore<Preferences>
) : ViewModel() {

    val uiState: StateFlow<SettingsUiState> =
        dataStore.data
            .map { prefs ->
                SettingsUiState(
                    isDarkMode = prefs[booleanPreferencesKey("dark_mode")] ?: false,
                    counter = prefs[intPreferencesKey("launch_count")] ?: 0
                )
            }
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5000),
                initialValue = SettingsUiState()
            )
}

자주 묻는 질문

DataStore가 SharedPreferences보다 나은 점은?

DataStore는 비동기적으로 작동하며(UI 스레드를 차단하지 않음), 트랜잭션을 통한 동시 액세스를 지원하고 Flow를 통한 변경 사항의 반응형 구독을 가능하게 합니다. SharedPreferences는 대용량 데이터에서 ANR 위험이 있고 내장된 반응형 지원이 없는 동기 API입니다.

DataStore를 Java와 함께 사용할 수 있나?

DataStore는 Kotlin으로 작성되었으며 Kotlin Coroutines가 필요합니다. Java에서 사용하는 것은 가능하지만 불편합니다: CompletableFuture로 래퍼를 만들거나 수동으로 코루틴을 관리해야 합니다. Java 프로젝트의 경우 Google은 SharedPreferences를 유지하거나 모듈에 Kotlin을 추가할 것을 권장합니다.

DataStore는 대용량 데이터 저장에 적합한가?

DataStore는 읽기 시 전체 파일을 메모리에 로드하므로 목록이나 큰 객체를 저장하는 데 적합하지 않습니다. 이러한 시나리오에서는 Room이나 SQLite를 사용하세요. DataStore는 설정 및 최대 수백 KB의 작은 구조화된 데이터에 최적화되어 있습니다.

손상된 DataStore 파일 오류를 처리하는 방법은?

DataStore를 생성할 때 corruptionHandler를 전달할 수 있습니다 — 파일이 손상되었을 때 호출되는 함수입니다. 기본적으로 DataStore는 CorruptionException을 발생시킵니다. corruptionHandler에서 빈 데이터를 반환하면 DataStore가 파일을 올바른 상태로 덮어씁니다.

Proto DataStore는 .proto 파일이 필수인가?

네, Proto DataStore는 .proto 파일에 스키마를 정의하고 protobuf-gradle-plugin을 연결해야 합니다. 프로젝트가 작고 데이터가 단순한 경우 Preferences DataStore를 사용하는 것이 더 쉽습니다 — 추가 빌드 구성이 필요하지 않습니다.

요약

  • DataStore — Kotlin Coroutines 및 Flow와 함께 작동하는 SharedPreferences의 현대적인 대체
  • Preferences DataStore — 스키마 없는 간단한 키-값, 설정에 적합
  • Proto DataStore — protobuf 스키마 및 자동 마이그레이션이 있는 형식화된 저장소
  • 마이그레이션은 SharedPreferencesMigration을 통해 DataStore에 내장됨
  • 스레드 안전성 — 모든 작업이 비동기적이며 UI 차단이 제거됨
  • 반응형 — Flow가 데이터 변경마다 구독자에게 알림
  • 권장 — 모든 새 Android 프로젝트에서 DataStore를 사용하고 설정 작업 시 기존 프로젝트를 마이그레이션하세요

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기