๊ณต๋ถ€/Android

[Android] AAC - LiveData

ENAN 2021. 3. 12. 16:01

LiveData๋ž€?

๐Ÿค”
Lifecycle์„ ์•Œ๊ณ  ์žˆ๋Š”, Observe ํ•  ์ˆ˜ ์žˆ๋Š” Data holder class

LiveData๊ฐ€ ๊ฐ–๊ณ  ์žˆ๋Š” ๋ฐ์ดํ„ฐ์— ์–ด๋– ํ•œ ๋ณ€ํ™”๊ฐ€ ์ผ์–ด๋‚  ๊ฒฝ์šฐ, ๋“ฑ๋ก๋œ Observer ๊ฐ์ฒด์— ๋ณ€ํ™”๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

๋ผ์ด๋ธŒ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜์Œ ์ ‘ํ•ด ๋ณธ๋‹ค๋ฉด ๋ฌด์Šจ ๋ง์ธ์ง€ ๋ฐ”๋กœ ์ดํ•ดํ•˜๊ธฐ ํž˜๋“ค ์ˆ˜ ์žˆ๋‹ค. ๋ผ์ด๋ธŒ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Observer Pattern, Lifecycle ๋‘ ๊ฐ€์ง€์— ๋Œ€ํ•ด ๋จผ์ € ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.

์˜ต์ €๋ฒ„ ํŒจํ„ด(Observer Pattern)์ด๋ž€?

๐Ÿค”
์–ด๋–ค ๊ฐ์ฒด์˜ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ๊ด€์ฐฐํ•˜๋Š” Observer๋“ค์˜ ๋ชฉ๋ก์„ ๊ฐ์ฒด์— ๋“ฑ๋กํ•˜์—ฌ, ์ƒํƒœ ๋ณ€ํ™”๊ฐ€ ์žˆ์„ ๋•Œ๋งˆ๋‹ค ๋“ฑ๋ก๋œ Observer์—๊ฒŒ ํ†ต์ง€ํ•˜๋„๋ก ํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด

LiveData๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ–๊ณ  ์žˆ๊ณ , ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ์ด LiveData๋ฅผ ๊ด€์ฐฐ(observe)ํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋•Œ, livedata๋Š” ๋ˆ„๊ฐ€ ์ž์‹ ์„ ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋Š”์ง€ ์ž์‹ ์˜ Observer๋“ค์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค LiveData๊ฐ€ ๊ฐ–๊ณ  ์žˆ๋Š” ๋ฐ์ดํ„ฐ์— ์–ด๋–ค ๋ณ€ํ™”๊ฐ€ ์ผ์–ด๋‚  ๊ฒฝ์šฐ, ๋“ฑ๋ก๋œ Observer๋“ค์—๊ฒŒ ๋ณ€ํ™”๋ฅผ ํ†ต์ง€ํ•ด ์คŒ์œผ๋กœ์จ Observer๋“ค์€ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜๊ฒŒ ๋œ๋‹ค.

ํ”ํžˆ ์‹ ๋ฌธ ๊ตฌ๋…์„ ์˜ˆ์‹œ๋กœ ๋งŽ์ด ๋“œ๋Š”๋ฐ, ํŠธ๋ Œ๋“œ์— ๋งž๊ฒŒ ์œ ํŠœ๋ธŒ์˜ ๊ฒฝ์šฐ๋„ ์ƒ๊ฐํ•ด ๋ณด์ž. ์‹œ์ฒญ์ž๋“ค์ด ์–ด๋–ค ์œ ํŠœ๋ธŒ ์ฑ„๋„์„ ๊ตฌ๋…(๊ด€์ฐฐ)ํ•˜๋ฉด, ์œ ํŠœ๋ฒ„(livedata)๊ฐ€ ์˜์ƒ์„ ์˜ฌ๋ ธ์„ ๋•Œ(๊ฐ–๊ณ  ์žˆ๋Š” ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”๊ฐ€ ์žˆ์„ ๋•Œ) ๊ตฌ๋…ํ•œ ์‹œ์ฒญ์ž๋“ค์—๊ฒŒ ์•Œ๋ฆผ์„ ๋ณด๋‚ด ์ค€๋‹ค. observer pattern์ด ์ด์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

๋ณด๋ฉด ๊ตฌ๋… ์„œ๋น„์Šค์™€ ๋งŽ์ด ๋‹ฎ์€ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“œ๋Š”๋ฐ, ์ด์ฒ˜๋Ÿผ observer ํŒจํ„ด์„ ๋ฐœํ–‰/๊ตฌ๋… ํŒจํ„ด์ด๋ผ๊ณ ๋„ ํ•œ๋‹ค.

์˜ต์ €๋ฒ„์˜ ์ถ”๊ฐ€, ์ œ๊ฑฐ๊ฐ€ ์ž์œ ๋กญ๊ณ  ๊ฐ์ฒด์™€ ์˜ต์ €๋ฒ„๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์  ๋“ฑ ์˜ต์ €๋ฒ„-๊ฐ์ฒด ๊ฐ„ ์ƒํ˜ธ์˜์กด์„ฑ์„ ์ตœ์†Œํ™” ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ณ€๊ฒฝ์— ์œ ์—ฐํ•ด์ง„๋‹ค๋Š” ์žฅ์ ์„ ๊ฐ€์ง„๋‹ค. ์ด๋Š” MVVM ๋””์ž์ธ ํŒจํ„ด์—์„œ ์–˜๊ธฐํ•˜๋Š” ๋ชฉํ‘œ, ์ฆ‰ ๊ณ„์ธต ๊ฐ„ ์˜์กด์„ฑ์„ ์ตœ๋Œ€ํ•œ ์—†์•ฐ์œผ๋กœ์จ ์œ ์ง€๋ณด์ˆ˜์—์„œ ์ด์ ์„ ๊ฐ€์ ธ๊ฐ„๋‹ค๋Š” ๋ชฉํ‘œ์™€๋„ ๋งž๋‹ฟ์•„์žˆ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ ๋‹ค.

Lifecycle์„ ์•Œ๊ณ  ์žˆ๋‹ค

์ƒ๋ช… ์ฃผ๊ธฐ(Lifecycle)๋ฅผ ์•Œ๊ณ  ์žˆ๋‹ค๋Š” ๋ง์€, ํ™œ์„ฑ ์ƒํƒœ์— ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ์˜ Observer๋“ค๋งŒ ๊ฐฑ์‹ ํ•˜๋Š” ๊ฒƒ์ด ๋ณด์žฅ๋œ๋‹ค๋Š” ์†Œ๋ฆฌ๋‹ค. ์—ฌ๊ธฐ์„œ ์˜ค๋Š” ์žฅ์ ๋“ค์ด ์–ด๋–ค ๊ฒƒ๋“ค์ด ์žˆ์„๊นŒ ํ•œ๋ฒˆ ์ƒ๊ฐํ•ด ๋ณด์ž.

์šฐ์„  view๊ฐ€ destroy๋˜๋ฉด ์•Œ์•„์„œ observe ์ƒํƒœ๋ฅผ ํ•ด์ œํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์˜ ์œ„ํ—˜์ด ์—†๊ณ , ๋˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋ทฐ์— ์ ‘๊ทผํ•˜๋ฉฐ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์—ฌ์ง€๋„ ์—†๋‹ค. ๋˜ํ•œ ๊ธฐ๊ธฐ ํšŒ์ „ ๋“ฑ์œผ๋กœ ๋ทฐ๊ฐ€ ๋‹ค์‹œ ์ƒ์„ฑ๋˜๋ฉด ์•Œ์•„์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๋ฒˆ ๋” ๋ฐ›์•„์˜ค๋ฉด์„œ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฌผ๋ก  LiveData๊ฐ€ ์•„๋‹ˆ๋ผ Rxjava๋ฅผ ์จ๋„ lifecycle์„ ์ž˜ ์ดํ•ดํ•˜๊ณ  ์ฒ˜๋ฆฌํ•ด ์ฃผ๋ฉด ์ƒ๊ด€ ์—†์ง€๋งŒ, ๋Œ€์‹  ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•ญ์ƒ ์‹ ๊ฒฝ์“ฐ๊ณ  ๊ด€๋ฆฌํ•ด์•ผ ํ•  ์ฑ…์ž„์ด ์ƒ๊ธฐ๊ฒŒ ๋œ๋‹ค. ์‹ค์ˆ˜๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์—†๋Š” ๊ตฌ์กฐ์™€ ์‹ค์ˆ˜ํ•˜์ง€ ์•Š์•„์•ผ ํ•˜๋Š” ๊ตฌ์กฐ์˜ ์ฐจ์ด๋Š” ๊ต‰์žฅํžˆ ํฌ๋‹ค.

์‚ฌ์šฉ ๋ฐฉ๋ฒ•

๊ธฐ๋ณธ์ ์œผ๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด lifecycleOwner, Observer๊ฐ์ฒด์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ๋‹ค. lifecycleOwner๋Š” Observer๊ฐ€ ๋”ฐ๋ผ๊ฐˆ ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ๋งํ•˜๋Š”๋ฐ ๋ณดํ†ต์˜ ๊ฒฝ์šฐ observer๊ฐ€ ์„ ์–ธ๋œ ๋ทฐ๋ฅผ owner๋กœ ๊ฐ€์ง„๋‹ค. MainActivity๋ฅผ lifecycleowner๋กœ ๊ฐ–๊ณ  ์žˆ๋Š” observer๋Š” MainActivity๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ ๊ฐ™์ด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋œ๋‹ค.

  • viewModel์—์„œ liveData ์„ ์–ธ, postValue๋ฅผ ํ†ตํ•ด ๊ฐ’ ๋ณ€๊ฒฝ
val itemLiveData = MutableLiveData<List<Item>>()
// ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹œ
// ex) val stores = some data (List<Item> ํ˜•ํƒœ)
itemLiveData.postValue(stores)
//observer
viewModel.itemLiveData.observe(this@MainActivity) { it->
    // ๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ–ˆ์„ ๋•Œ ์‹คํ–‰๋  ํ•จ์ˆ˜๋ฅผ ๋žŒ๋‹ค์‹์œผ๋กœ ์˜ต์ €๋ฒ„์— ์ „๋‹ฌ
    // ex) storeAdapter.updateItems(it) 
})

Activity๋ณด๋‹ค๋Š” viewModel์—์„œ ์ •์˜ํ•˜๊ณ  ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ๊ทธ๋ž˜์•ผ Activity๋Š” data๋ฅผ displayํ•˜๋Š” ์—ญํ• , ์ฆ‰ view์˜ ์—ญํ• ๋งŒ์„ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋˜ํ•œ LiveData๋ฅผ ๋‹จ๋…์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ Databinding, RoomDB ๋“ฑ AAC์˜ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ํ›จ์”ฌ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ex) Room๊ณผ ํ•จ๊ป˜, DB๋‚ด์šฉ์„ ๊ด€์ฐฐํ•ด์„œ ์ž๋™์œผ๋กœ ๊ฐฑ์‹ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ

  • Dao์˜ getAll ํ•จ์ˆ˜ ๋ณ€๊ฒฝ (๋ฐ˜ํ™˜ type์„ List<Todo> โ†’ LiveData<List<Todo>>)
    @Dao
    interface TodoDao {
        @Query("SELECT * FROM Todo")
    //    fun getAll(): List<Todo>
        fun getAll(): LiveData<List<Todo>>
    		. . .
    }
  • DB์— insert ์‹œ ์ž๋™์œผ๋กœ UI ๊ฐฑ์‹ 
    db.todoDao().getAll().observe(this, Observer {todoList ->
                tvTodoList.text = todoList.toString()
            })
    
    btnTodo.setOnClickListener {
                db.todoDao().insert(Todo(etTodo.text.toString()))
                // tvTodoList.text = db.todoDao().getAll().toString()
            }

vs RxJava(or RxKotlin, RxAndroid)

LiveData์™€ RxSomething๋“ค์€ ๋‘˜๋‹ค reactiveํ•œ ๊ฐœ๋ฐœ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ์–ด๋–ป๊ฒŒ ๋ณด๋ฉด ๋น„์Šทํ•œ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ถ„๋ช…ํžˆ ์ฐจ์ด์ ์€ ์กด์žฌํ•œ๋‹ค. ์˜ˆ์ „๋ถ€ํ„ฐ ์ด ์ฐจ์ด๊ฐ€ ๊ถ๊ธˆํ•ด์„œ ์ข€ ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ณ  ์ ๊ณ  ์‹ถ์—ˆ๋˜ ์ฃผ์ œ์ด์ง€๋งŒ, ์–˜๊ธฐ๊ฐ€ ๊ธธ์–ด์งˆ ๊ฒƒ ๊ฐ™์•„ ๋‹ค์Œ ๊ธ€์— ์ ๊ฒ ์Šต๋‹ˆ๋‹ค!

์ฐธ๊ณ 

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

https://ko.wikipedia.org/wiki/์˜ต์„œ๋ฒ„_ํŒจํ„ด

https://jistol.github.io/software engineering/2018/04/11/observer-pubsub-pattern/

https://terry-some.tistory.com/28

https://kscory.com/dev/design-pattern/observer