경험의 기록

2021.04.05 - [Android/기본] - [Android] 안드로이드 SharedPreference 사용하여 데이터 저장하기

 

[Android] 안드로이드 SharedPreference 사용하여 데이터 저장하기

안드로이드에서는 설정같은 Key/Value 형태로 데이터를 저장할 수 있는 간단한 정보를 저장하기 위하여 SharedPreference 를 사용할 수 있다. 선언하기 // 모드 0 = Context.MODE_PRIVATE val pref = this.getPref..

hanyeop.tistory.com

예전에 안드로이드에서는 Key - Value 형태로 간단히 저장할 수 있는 가벼운 데이터들을 저장할 때

SharedPreference 를 사용했다.

 

하지만 SharedPreference 는 더이상 사용하는 것이 권장되지 않는다.

그대신 동일한 기능을 하며, 여러 장점이 존재하는 DataStore 에 대해 알아보려고 한다.

 

 


🔴 DataStore 란 ?

Key - Value 형태 또는 프로토콜 버퍼를 사용하여 특정 타입 Object를 저장할 수 있는 데이터 저장소

 

SharedPreference 와 유사하게 가벼운 데이터를 저장할 때 사용할 수 있으며

2개의 종류로 나뉜다.

 

1️⃣ Preferences DataStore : Key - Value 형태 저장

2️⃣ Proto DataStore : 프로토콜 버퍼를 정의하여 특정 타입 Object 저장

 

위 설명만 보면 Preferences DataStore  SharedPreference 와 동일해보이지만 몇가지 큰 차이가 있다.

 

  • Flow와 Coroutine을 활용하여 비동기 방식으로 동작한다.
  • MainThread 에서 호출되어도 안전하다. (Dispather.IO 하위에서 동작)
  • Runtime Exception으로부터 안전

 

위와 같은 장점이 있다.

 

또한 Proto DataStore

스키마를 정의하여 특정 타입 Object를 저장하기 때문에 타입 안전성을 보장한다는 장점이 있다.

 

이처럼 DataStoreSharedPreference 에 비해 많은 장점을 가지고 있다.

하지만 특정 타입의 Object를 저장하는 Proto DataStore는 별도의 스키마를 정의해야하고, 그 과정에서 복잡해질 경우 Room과 같은 DB를 사용하는 것이 더 나을 수 있기 때문에 일반적으로 자주 사용하기 어렵다는 생각이 들었다.

 

따라서 간단한 데이터 (Key-Value) 를 저장할 때 사용하는 Preferences DataStore 만을 사용해보고자 한다.

 

 

 

🔴 사용해보기

테스트를 위해

EditView에 텍스트를 작성하고 Save 버튼 클릭 시 EditView의 내용이 DataStore에 저장되고,

하단의 "저장된 텍스트" 부분에서 DataStore에 저장되어 있는 텍스트를 불러와 보여주는 코드를 작성해보려고 한다.

 

Dependency 추가

// Preferences DateStore
    implementation "androidx.datastore:datastore-preferences:1.0.0"

 

DataStore 생성

 private val Context.dataStore:
            DataStore<Preferences> by preferencesDataStore(name = "settings")

데이터스토어는 싱글톤으로 한번만 생성하여 불러와 사용해야한다.

따라서 Application 수준에서 객체를 생성하거나 DI(의존성 주입) 등을 활용하여 관리하는 것이 좋다.

예시코드이므로 메인액티비티에 작성하였다.

 

데이터 저장

private val stringKey = "key"

임의의 key 값을 설정해주고

 

// 데이터 저장
    private suspend fun save(value: String){
        val key = stringPreferencesKey(stringKey)
        dataStore.edit {
            it[key] = value
        }
    }

DataStore의 작업은 비동기로 코루틴에서 작동하므로

suspend로 선언해준다.

 

DataStore에서 사용하는 키의 타입은

타입PreferencesKey("Key 이름") 의 형태로 선언해야한다.

 

그 후 edit를 활용하여 Key - Value 형태의 값을 저장할 수 있다.

binding.button.setOnClickListener {
            lifecycleScope.launch {
                save(binding.editView.text.toString())
            }
        }

이제 버튼클릭 시

코루틴에서 save 메소드가 작동하도록 한다.

 

 

데이터 불러오기

// 데이터 불러오기
    private suspend fun load() : Flow<String> {
        val key = stringPreferencesKey(stringKey)
        return dataStore.data.catch { e ->
            if (e is IOException) {
                emit(emptyPreferences())
            } else {
                throw e
            }
        }.map {
            it[key] ?: "기본 텍스트입니다."
        }
    }

dataStore에 접근하여 예외 처리를 해주고

정상적으로 실행 된 경우 key값에 해당하는 value 값을 리턴한다.

 

lifecycleScope.launch {
            load().collect {
                binding.SavedText.text = it
            }
        }

저장과 동일하게 코루틴으로 작동하도록 하며,

flow를 collect하도록 한다.

 

전체 코드

class MainActivity : AppCompatActivity() {
    private lateinit var binding : ActivityMainBinding
    private val Context.dataStore:
            DataStore<Preferences> by preferencesDataStore(name = "settings")
    private val stringKey = "key"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 데이터바인딩
        binding = DataBindingUtil.setContentView(this,R.layout.activity_main)

        lifecycleScope.launch {
            load().collect {
                binding.SavedText.text = it
            }
        }

        binding.button.setOnClickListener {
            lifecycleScope.launch {
                save(binding.editView.text.toString())
            }
        }
    }

    // 데이터 저장
    private suspend fun save(value: String){
        val key = stringPreferencesKey(stringKey)
        dataStore.edit {
            it[key] = value
        }
    }

    // 데이터 불러오기
    private suspend fun load() : Flow<String> {
        val key = stringPreferencesKey(stringKey)
        return dataStore.data.catch { e ->
            if (e is IOException) {
                emit(emptyPreferences())
            } else {
                throw e
            }
        }.map {
            it[key] ?: "기본 텍스트입니다."
        }
    }
}

 

이제 실행해보면

최초 실행시에 key에 해당하는 데이터가 저장되어 있지 않으므로

"기본 텍스트입니다" 가 출력되고

 

값 저장시 하단의 텍스트가 변하게 되며

 

 

어플리케이션을 종료 후 재실행해도 데이터가 유지되는 것을 확인할 수 있다.

 

 

 

전체코드는 이곳에서 확인할 수 있습니다.

https://github.com/HanYeop/AndroidStudio-Practice2/tree/master/DataStoreEx

 

GitHub - HanYeop/AndroidStudio-Practice2: (2021.05.20~) 안드로이드 학습 내용 저장소

(2021.05.20~) 안드로이드 학습 내용 저장소. Contribute to HanYeop/AndroidStudio-Practice2 development by creating an account on GitHub.

github.com

 

 


안드로이드는 기존에 존재하던 기술들이 자주 대체되는 것 같다.

DataStore 가 나온지 꽤 오래되었지만, 데이터를 저장할 때 거의 DB만 사용했어서 SharedPreference 가 대체된지도 몰랐다.

꾸준히 개발 동향을 살펴, 대체되는 새로운 기술들이 나올 때 마다 바로 적용하려 노력해야겠다는 생각이 들었다.

 

 

 

참고

https://developer.android.com/reference/kotlin/android/content/SharedPreferences

https://developer.android.com/topic/libraries/architecture/datastore

반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading