[Android] DataStore

Minji Jeongยท2022๋…„ 4์›” 26์ผ
0

Android

๋ชฉ๋ก ๋ณด๊ธฐ
34/39
post-thumbnail

๐Ÿ™„ ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ ํ›„์˜ ๊ณ ์ฐฐ

๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•œ ํ›„, ์‚ฌ์šฉ์ž์˜ ์ด๋ฉ”์ผ์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ค ๋ฐฉ์‹์„ ์จ์•ผ ํ• ๊นŒ ๊ณ ๋ฏผํ–ˆ๋‹ค. ์ฒ˜์Œ์—” Bundle์— ์ด๋ฉ”์ผ ๊ฐ’์„ ๋„ฃ์–ด ํ”„๋ž˜๊ทธ๋จผํŠธ ๊ฐ„ ์ด๋™์‹œ์— ์ „๋‹ฌํ•ด์•ผ๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์„ ํ–ˆ์—ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ํ”„๋ž˜๊ทธ๋จผํŠธ๋‚˜ ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ํ•œ๋‘๊ฐœ๋„ ์•„๋‹ˆ๊ณ , ํ•˜๋‚˜์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ๊ฒƒ๋„ ์•„๋‹ˆ๊ณ  ์—ฌ๋Ÿฌ๊ตฐ๋ฐ์„œ ์จ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋„ˆ๋ฌด ๋น„ํšจ์œจ์ ์ผ ๊ฒƒ ๊ฐ™์•˜๋‹ค. ์–ด์ฐจํ”ผ ์ด๋ฉ”์ผ๋งŒ ์ €์žฅํ•˜๋ฉด ๋˜๋‹ˆ๊นŒ, ๋ณต์žกํ•œ ๊ฒƒ ํ•„์š” ์—†์ด SharedPreferences๋‚˜ DataStore์„ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๊ฒ ๋‹ค๋Š” ํŒ๋‹จ์ด ๋“ค์—ˆ๊ณ , SharedPreferences๋Š” ๋‚˜์˜จ์ง€ ์ข€ ์˜ค๋ž˜๋œ ์• ๊ณ  DataStore๊ฐ€ ์•ˆ์ •์„ฑ ๋ฉด์—์„œ ํ›จ ์ข‹๊ธฐ ๋•Œ๋ฌธ์— ์ด์ฐธ์— DataStore๋ฅผ ์ œ๋Œ€๋กœ ์จ๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

โœ… DataStore

DataStore๋Š” ํ‚ค-๊ฐ’ ์Œ ํ˜•์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ์ปฌ์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” Jetpack์˜ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ ์†”๋ฃจ์…˜์ด๋‹ค. Coroutine ๋ฐ Flow๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋ฉฐ, SharedPreferences์™€ ๋‹ฌ๋ฆฌ UI ์Šค๋ ˆ๋“œ ํ˜ธ์ถœ ๋ฐ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋กœ๋ถ€ํ„ฐ ์•ˆ์ „ํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค. DataStore๋Š” ๋ณดํ†ต ๊ฐ„๋‹จํ•œ ๋ฌธ์ž์—ด๊ณผ ๊ฐ™์€ ์†Œ๊ทœ๋ชจ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค(๋ณต์žกํ•˜๊ฑฐ๋‚˜ ๋Œ€๊ทœ๋ชจ์˜ ๋ฐ์ดํ„ฐ๋Š” Room ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•œ๋‹ค). DataStore๋Š” 2๊ฐ€์ง€๋กœ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

โœ” Preferences DataStore vs Proto DataStore

Preferences DataStore๋Š” ํ‚ค๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ์ฝ์–ด์˜จ๋‹ค. ์Šคํ‚ค๋งˆ๋ฅผ ์ •์˜ํ•  ํ•„์š”๋Š” ์—†์ง€๋งŒ Proto DataStore์™€ ๋‹ฌ๋ฆฌ ์•ˆ์ „์„ฑ์„ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. Proto DataStore๋Š” ํ”„๋กœํ† ์ฝœ ๋ฒ„ํผ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํ‚ค๋งˆ๋ฅผ ์ •์˜ํ•ด์•ผ ํ•˜๋ฉฐ, ์•ˆ์ „์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค. ๋” ์•ˆ์ „ํ•˜๋‹ค๋Š” ๋ฉด์—์„œ Proto DataStore๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚ซ๊ฒ ์ง€๋งŒ, ๋น ๋ฅด๊ฒŒ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ  ์‹ถ์—ˆ๋˜ ๋‚˜๋Š” ์ด์ „์— ์ฐ๋จนํ•ด๋ดค๋˜ Preferences DataStore๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

1. ์‚ฌ์šฉ ์ค€๋น„
https://kangmin1012.tistory.com/47 ๋‹˜์˜ ๊ธ€์„ ์ฐธ๊ณ ํ•ด์„œ ์›ํ™œํ•˜๊ฒŒ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค ๐Ÿ‘

DataStore๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „, build.gradle(module)์— preferences datastore์— ๋Œ€ํ•œ ์ข…์†์„ฑ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

dependencies {
  implementation("androidx.datastore:datastore-preferences:1.0.0")
    }
2. ์ฝ”๋“œ ์ž‘์„ฑ

๋จผ์ € ๋‚˜๋Š” ์‚ฌ์šฉ์ž์˜ ์ด๋ฉ”์ผ๊ณผ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๋กœ๊ทธ์ธํ–ˆ๋Š”์ง€(์ผ๋ฐ˜ ๋กœ๊ทธ์ธ์ธ์ง€ SNS ๊ณ„์ •์„ ์ด์šฉํ•œ ๋กœ๊ทธ์ธ์ธ์ง€) ์—ฌ๋ถ€์— ๋Œ€ํ•ด ์ €์žฅํ•  ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ DataStore๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๊ฐ ๊ฐ’์„ ์ €์žฅํ•˜๊ณ  ์ฝ๊ธฐ ์œ„ํ•œ key ๋ณ€์ˆ˜์— ๋Œ€ํ•ด ์„ ์–ธํ–ˆ๋‹ค. key์˜ type์€ ์ €์žฅํ•˜๊ณ ์ž ํ•˜๋Š” ๋ณ€์ˆ˜ ํƒ€์ž…์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์„ ์–ธํ•ด์ค€๋‹ค.

class DataStoreModule(private val context : Context){

	//preferences DataStore ์ƒ์„ฑ
    //DataStore๋ฅผ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํŒŒ์ผ ์ตœ์ƒ๋‹จ์— ๋ฐฐ์น˜, ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด DataStore๋ฅผ ํ•˜๋‚˜๋งŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Œ
	private val Context.datastore by preferencesDataStore(name = "datastore")
    
    
    private val emailKey = stringPreferencesKey("USER_EMAIL") //์‚ฌ์šฉ์ž์˜ ์ด๋ฉ”์ผ
    private val loginTypeKey = intPreferencesKey("LOGIN_TYPE") //๋กœ๊ทธ์ธ ์—ฌ๋ถ€
    
}

๊ฐ’์„ ์ฝ์–ด์˜ค๊ธฐ ์œ„ํ•ด DataStore.data๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. map()์„ ์ด์šฉํ•ด DataStore์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ๊ฐ’์„ ๋ฏธ๋ฆฌ ์ง€์ •ํ•ด๋‘” ํ‚ค๋ฅผ ํ†ตํ•ด ๊ฐ€์ ธ์˜จ๋‹ค. ์ด ๋•Œ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ’์€ Flow์ด ๋œ๋‹ค.
๋˜ํ•œ ๊ฐ’์„ ์ฝ์–ด์˜ค๋Š”๋ฐ์— ์‹คํŒจํ•˜๋ฉด IOException์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, catch()๋ฌธ์„ ์ด์šฉํ•ด exception์ด ๋ฐœ์ƒํ–ˆ์„ ๊ฒฝ์šฐ ๋นˆ ๊ฐ’์„ ์ „๋‹ฌํ•˜๋„๋ก ํ•œ๋‹ค.

val email : Flow<String> = context.datastore.data
	.catch{ exception ->
    	if(exception is IOException){
        	emit(emptyPreferences())
        }else{
        	throw exception
        }
     }.map{
     	it[emailKey] ?: ""
	}
     

DataStore์— ๊ฐ’์„ ์ €์žฅํ•  ๋•Œ๋Š” edit()์„ ์‚ฌ์šฉํ•œ๋‹ค. DataStore์— ๊ฐ’์„ ์ €์žฅํ•  ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ๋น„๋™๊ธฐ๋กœ ์ž‘์—…ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, suspend๋ฅผ ํ†ตํ•ด ๊ฐ’์„ ์ž‘์„ฑํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ์ฝ”๋ฃจํ‹ด ์˜์—ญ์—์„œ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

suspend fun setEmail(email : String){
	context.datastore.edit{
        it[emailKey] = email
	}
}

DataStore๋Š” ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ด€๋ฆฌ๋˜์–ด์•ผ ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ Application()์„ ์ƒ์†ํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ , DataStore ํด๋ž˜์Šค์˜ context๋กœ AppicationContext๋ฅผ ๋„˜๊ฒจ์ค€๋‹ค.

class App : Application() {

    private lateinit var datastore : DataStoreModule

    companion object{
        private lateinit var app : App
        fun getInstance() : App = app
    }

    override fun onCreate() {
        super.onCreate()
        app = this
        datastore = DataStoreModule(this)
    }

    fun getDataStore() : DataStoreModule = datastore
}

์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ DataStore์˜ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ๋Š” DataStore ํด๋ž˜์Šค์—์„œ ์„ ์–ธํ•œ ๋ณ€์ˆ˜์— ์ ‘๊ทผํ•˜์—ฌ Flow() ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜๋ฐ›์•„ ๊ฐ’์„ ์ฝ์–ด์™€์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํ•ด๋‹น ์ž‘์—…์€ CoroutineScope ๋‚ด์—์„œ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค. ๋งŒ์•ฝ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ํƒ€์ด๋ฐ์— ํ•œ ๋ฒˆ๋งŒ ๊ฐ’์„ ๋ฐ›์•„์™€์„œ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด first()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋œ๋‹ค. ๊ฐ’์„ ์ €์žฅํ•˜๋Š” ๊ฒƒ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ CoroutineScope ๋‚ด์—์„œ ์‹คํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

//๊ฐ’ ์ฝ์–ด์˜ค๊ธฐ
CoroutineScope(Dispatchers.Main).launch{
	val loginType = App.getInstance().getDataStore().email.first()
}
//๊ฐ’ ์ €์žฅํ•˜๊ธฐ
CoroutineScope(Dispatchers.Main).launch {
	App.getInstance().getDataStore().setEmail("user-email")
}

References

https://developer.android.com/topic/libraries/architecture/datastore?hl=ko
https://kangmin1012.tistory.com/47

profile
Mobile Software Engineer

0๊ฐœ์˜ ๋Œ“๊ธ€