사용자 도구

사이트 도구


android:datastore
datastore

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

다음 판
이전 판
android:datastore [2025/04/27 02:59] – 만듦 이거니맨android:datastore [2025/05/01 14:52] (현재) – 데이터스토어로 저장하는 예제 이거니맨
줄 1: 줄 1:
-====== 개요 =====+===== 서론 ===== 
 + 
 + 
 + 
 +앱과 관련된 데이터를 램이 아닌 낸드와 같은 영구저장장치(보조저장장치)에 저장한 후 이를 재사용하고 싶을 때가 있다. 
 + 
 +이를테면 앱의 테마 색깔을 계속 유지하고 싶다거나, 아니면 세이브 파일 부터 이어서 게임을 하고 싶다거나 등의 경우가 생길 수 있다.  
 + 
 +이렇게 앱이 종료된 이후에도 계속하여 데이터를 보조저장장치에 저장한 후에 앱이 재사용될 때에 불러오는 것은 세가지 방법을 떠올릴 수 있을 것이다.  
 + 
 + 
 +==== 1. 별도의 파일에 데이터를 쓰고 저장하기 ==== 
 + 
 +전통적인 방법은 파일을 직접 제어하여 문자열을 저장한 후에, 이를 다시 불러오는 것이다.  
 + 
 +단, [[android:pdfdocument:filesave_in_android|파일로 저장]]할 때에 [[android:pdfdocument:filesave_in_android|파일의 저장]] 폴더를 [[https://developer.android.com/training/data-storage?hl=ko|내부저장소]]로 지정해 주어야 외부에서 접근하지 못할 것이다.  
 + 
 +이렇게 파일을 저장할 때에는 [[android:json파싱하기|JSON을 이용]]하여 정보를 체계화하여 저장하는 것도 가능할 것이다.  
 + 
 +참고로 이렇게 별도의 파일로 저장하는 방법으로 오랫동안 사용된 것은 ini파일이다. 베데스다의 폴아웃 프랜차이즈나 엘더스크롤 프랜차이즈는 모두 인 설정 파일을 ini로 저장하였다.  
 + 
 +심지어 베데스다 소프트웍스는 가장 최신의 게임인 스타필드마저 게임 설정에 ini파일을 이용하였다.  
 + 
 +{{:android:starfieldprefsini.png?400|Starfield Prefs.ini}} 
 + 
 + 
 +==== 2. 데이터베이스 이용하기 ====  
 + 
 +데이터베이스를 이용하여 데이터를 영구적으로 저장할 수도 있을 것이다. 안드로이드에서는 [[android:room을사용하여데이터보관|룸데이터베이스]]를 사용하면 된다.  
 + 
 +그런데 데이터베이스는 레코드가 수없이 많이 쌓이므로 그 중에 어느 레코드를 설정파일로 사용할 것인지 지정해주어야 한다.  
 + 
 +이를테면 isUser라는 필드를 하나 만들어서 'SELECT * From (USERSETTING) WHERE isUser = true'라는 식으로 테이블 중에서 단 하나의 레코드만 선택할 수 있도록 트릭을 설정해야 한다.  
 + 
 +따라서 간단한 설정 저장에 데이터베이스를 쓰는 것은 닭잡는 데에 소잡는 칼을 쓰는 것과 같다.  
 + 
 + 
 +==== 3. Datastore ====  
 + 
 +[키-키값] 쌍으로 구성되어 있어서 설정파일만 간단하게 불러오는 것을 안드로이드에서는 Datastore라고 한다.    
 + 
 +구글은 다음과 같이 설명하고 있다. 
 + 
 +<WRAP center round tip 95%> 
 +Jetpack Datastore는 [[https://protobuf.dev/|프로토콜 버퍼]]를 사용하여 키-값 쌍 또는 유형이 지정된 객체를 저장할 수 있는 데이터 저장소 솔루션입니다. Datastore는 Kotlin 코루틴 및 Flow를 사용하여 비동기적이고 일관된 트랜잭션 방식으로 데이터를 저장합니다. 
 +</WRAP> 
 + 
 + 
 +어찌되었건 대충 이해하자면 Android에서 설정 등을 기록하고 불러오는 기능을 말한다. 과거에 shared preference라는 이름으로 제공하던 api가 현재는 [[https://developer.android.com/topic/libraries/architecture/datastore?hl=ko|Datastore]]라는 이름으로 변경되다고 보면 된다. 
  
-Android에서 설정 등을 기록하고 불러오는 기능을 말한다. 과거에 shared preference라는 이름으로 제공하던 api가  
  
-현재는 [[https://developer.android.com/topic/libraries/architecture/datastore?hl=ko|Datastore]]라는 이름으로 변경되었다.  
  
 ===== 필요한 의존성 파일 =====  ===== 필요한 의존성 파일 ===== 
  
-datastore api와, lifecycle api가 필요하다. 음과 같다. +datastore api와, lifecycle api가 필요하다. 음과 같다. 
  
 모듈단계의 그래들 파일에 다음의 의존성을 추가한다.  모듈단계의 그래들 파일에 다음의 의존성을 추가한다. 
 +
 <code kotlin> <code kotlin>
     // DataStore     // DataStore
줄 46: 줄 93:
     private val FILTER_ON = booleanPreferencesKey("FilterOn") // string 저장 키값     private val FILTER_ON = booleanPreferencesKey("FilterOn") // string 저장 키값
  
 +    // Datastore 읽기
     // Flow : coroutines.flow import 해야됨     // Flow : coroutines.flow import 해야됨
     val valueOfFilter : Flow<Boolean> = context.dataStore.data     val valueOfFilter : Flow<Boolean> = context.dataStore.data
줄 54: 줄 102:
  
  
-    // String값을 stringKey 키 값에 저장+    // Datastore 쓰기
     suspend fun setFilterSwitch(switch : Boolean){     suspend fun setFilterSwitch(switch : Boolean){
         context.dataStore.edit { preferences->         context.dataStore.edit { preferences->
줄 64: 줄 112:
 } }
 </file>  </file> 
 +
 +==== 2. 인스턴스 생성하기 ====
 +
 +"appPreferences"란 이름의 Datastore 인스턴스를 다음과 같이 생성한다. Datastore는 한 앱당 하나의 인스턴스만 싱글톤으로 사용되어야 하므로 호출에 있어서는 굳이 이름이 필요 없다고 언뜻 생각할 수도 있다. 다만, 유지보수 관점에서 기존의 Datastore와는 다른 새로운 Datastore 인스턴스를 만들어야 할 필요도 있을 것이므로 이름은 필요할 것이다. 
 +
 +<code kotlin>
 +    private val Context.dataStore : DataStore<Preferences> by preferencesDataStore(name = "appPreferences")
 +</code>
 +
 +==== 3. 키값의 제목 설정하기 ==== 
 +
 +Datastore는 [키, 밸류]의 쌍으로 데이터를 저장한다. 이를테면 C#의 Dictionary와 매우 비슷한 구조인 것이다. 
 +
 +즉, 각 키별로 형식이 존재한다. 이를테면 int값을 저장할 때에는 intPreferencesKey라고 하고, boolean값을 저장할 때에는 booleanPreferencesKey라고 한다. 자세한 것은 [[https://developer.android.com/reference/kotlin/androidx/datastore/preferences/core/package-summary?_gl=1*1gfysu8*_up*MQ..*_ga*NjkxNzMyNDU2LjE3NDYwNzY0NjQ.*_ga_6HH9YJMN9M*MTc0NjA3NjQ2NC4xLjAuMTc0NjA3NjQ2NC4wLjAuMTU0MDYzMTYwOQ..#top-level-functions|Datastore에 대한 Top-Level functions]]를 참조하라. 
 +
 +우리는 boolean값을 "FILTER_ON" 이란 이름으로 저장할 것이다. 따라서 다음과 같이 키를 정의하게 된다. 
 +
 +<code kotlin>
 +    private val FILTER_ON = booleanPreferencesKey("FilterOn") // string 저장 키값
 +</code> 
 +
 +==== 4. 키 값 읽기 ====
 +
 +키값은 context.datastore.data.map으로 읽는다. 
 +
 +<code kotlin>
 +// Datastore 읽기
 +// Flow : coroutines.flow import 해야됨
 +val valueOfFilter : Flow<Boolean> = context.dataStore.data
 +    .map {preferences ->
 +        preferences[FILTER_ON] ?: false
 +    }
 +</code> 
 +
 +반환값이 Flow임을 유의하자 
 +
 +==== 5. 키 값 쓰기 ==== 
 +
 +context.datastore.edit로 키 값을 쓴다. 
 +
 +<code kotlin>
 +// Datastore 쓰기
 +suspend fun setFilterSwitch(switch : Boolean){
 +    context.dataStore.edit { preferences->
 +        preferences[FILTER_ON] = switch
 +    }
 +}
 +
 +</code> 
 +
 +<WRAP center round tip 90%>
 +자동적으로 다음이 임포트 되지 않을 수가 있다. 만약 안드로이드 스튜디오가 컴플레인을 하면 다음을 임포트하자 
 +
 +import androidx.datastore.preferences.core.edit
 +</WRAP>
 +
 +
  
  
 ===== 싱글턴으로 만들기 =====  ===== 싱글턴으로 만들기 ===== 
  
 +위의 datastore 인스턴스를 싱글톤으로 단 하나만 존재하게 하기 위하여 MainApplication을 다음과 같이 만들어준다. 
  
 +다음 예제는 database도 생성하는 코드가 같이 있는 것이니 이를 염두에 두고 살펴보자
  
-===== 실제 compose내에서 사용하기 +<file kotlin "MainApplication.kt"> 
 +import android.annotation.SuppressLint 
 +import android.app.Application 
 +import android.content.Context 
 +import androidx.datastore.core.DataStore 
 +import androidx.datastore.preferences.core.Preferences 
 +import androidx.datastore.preferences.preferencesDataStore 
 +import androidx.room.Room.databaseBuilder 
 +import com.dklaw.memorize.database.DataStoreMemorize 
 +import com.dklaw.memorize.database.ScoreDatabase 
 + 
 +class MainApplication : Application() { 
 + 
 +    init { 
 +        instance = this 
 +    } 
 + 
 +    companion object { 
 +        private var instance: MainApplication? = null 
 + 
 +        fun applicationContext() : Context { 
 +            return instance!!.applicationContext 
 +        } 
 + 
 +        lateinit var scoreDatabase: ScoreDatabase 
 + 
 +        // Datastore 변수 
 +        @SuppressLint("StaticFieldLeak"
 +        private lateinit var dataStore: DataStoreMemorize 
 + 
 +        fun getThis() : MainApplication { 
 +            return instance!! 
 +        } 
 + 
 +        fun  getDateStore() : DataStoreMemorize = dataStore 
 +    } 
 + 
 +    override fun onCreate() { 
 +        super.onCreate() 
 +        // initialize for any 
 + 
 +        // Use ApplicationContext. 
 +        val context: Context = MainApplication.applicationContext() 
 + 
 +        // Datastore single Instance 
 +        dataStore = DataStoreMemorize(this) 
 + 
 +        // Database Initialize 
 +        scoreDatabase = databaseBuilder( 
 +            applicationContext, 
 +            ScoreDatabase::class.java, 
 +            ScoreDatabase.NAME 
 +        ).build() 
 +    } 
 +
 +</file> 
 + 
 +===== 실제 compose내에서 사용하기 =====  
 + 
 +==== 1. 읽기 ==== 
 + 
 +다음과 같이 싱글톤으로 불러온 다음 헬퍼클래스의 읽이 함수를 호출한다.  
 + 
 +<code kotlin> 
 +    // 데이터 스토어 세팅 값 
 +    val isFiltered by MainApplication.getDateStore().valueOfFilter.collectAsState(initial = false) 
 +</code>  
 + 
 + 
 +Flow로 받아오므로 colletAsState로 값을 받아온 것을 알 수 있다.  
 + 
 +==== 2. 쓰기 ==== 
 + 
 +다음과 같이 코루틴을 통해 저장한다.  
 + 
 +<code kotlin> 
 +// 스위치 버튼, 코루틴 
 +@Composable 
 +fun switchButton(modifier: Modifier) { 
 + 
 +    val coroutineScope = rememberCoroutineScope() 
 +    val isFiltered by MainApplication.getDateStore().valueOfFilter.collectAsStateWithLifecycle(initialValue = false) 
 + 
 +    Row(modifier = modifier, horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) { 
 +        Text(text = if (isFiltered) {"필터"} else {"모두"}, style = MaterialTheme.typography.titleMedium, modifier = Modifier 
 +            .padding(vertical = 4.dp) 
 +            .weight(0.5f), textAlign = TextAlign.Center) 
 +        Switch( 
 +            checked = isFiltered, 
 +            onCheckedChange = { 
 + 
 +                coroutineScope.launch { MainApplication.getDateStore().setFilterSwitch(!isFiltered) } 
 + 
 +            }, 
 +            modifier = Modifier 
 +                .weight(0.5f) 
 +                .semantics { this.contentDescription = "Filter by completion" }, 
 +            thumbContent = if (isFiltered) { 
 +                { 
 +                    Icon( 
 +                        imageVector = Icons.Filled.Check, 
 +                        contentDescription = "Checked", 
 +                        modifier = Modifier.size(SwitchDefaults.IconSize), 
 +                    ) 
 +                } 
 +            } else { 
 +                {   Icon( 
 +                    imageVector = Icons.Filled.Close, 
 +                    contentDescription = "UnChecked", 
 +                    modifier = Modifier.size(SwitchDefaults.IconSize), 
 +                )} 
 +            } 
 +        ) 
 +    } 
 +
 +</code>
android/datastore.1745690364.txt.gz · 마지막으로 수정됨: 2025/04/27 02:59 저자 이거니맨