Android/Kotlin

[Kotlin][Android] ์•ˆ๋“œ๋กœ์ด๋“œ Coroutine Flow ์ ์šฉํ•˜๊ธฐ (2) ( LiveData๋ฅผ Flow๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ : LiveData ์™€ Flow ๋น„๊ตํ•˜๊ธฐ )

๋ށ์š” 2023. 3. 23. 16:04

์•ˆ๋…•ํ•˜์„ธ์š” ๐Ÿ‘‹

์˜ค๋Š˜์€ ์ €๋ฒˆ ํฌ์ŠคํŒ…์— ์ด์–ด LiveData๋ฅผ Flow๋กœ ์ด์ „ํ•˜๋Š” ๊ณผ์ •์„ ํฌ์ŠคํŒ…ํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, LiveData์™€ Flow์˜ ์ฐจ์ด์ ์— ๋Œ€ํ•ด์„œ๋„ ํ•จ๊ป˜ ๋‹ค๋ค„๋ณผ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค ๐Ÿ˜ƒ

์ด์ „ ํฌ์ŠคํŒ…์€ ์•„๋ž˜ ๋งํฌ๋ฅผ ํ†ตํ•ด ์ฐธ๊ณ  ๋ฐ”๋ž๋‹ˆ๋‹ค. ๐Ÿ™

 

 

[Kotlin][Android] ์•ˆ๋“œ๋กœ์ด๋“œ Coroutine Flow ์ ์šฉํ•˜๊ธฐ (1) (๋„คํŠธ์›Œํฌ ํ†ต์‹  Flow๋กœ ์ด์ „ํ•˜๊ธฐ)

์•ˆ๋…•ํ•˜์„ธ์š” ๐Ÿ‘‹ ์˜ค๋Š˜์€ ์•ˆ๋“œ๋กœ์ด๋“œ MVVMํŒจํ„ด์—์„œ Flow๋ฅผ ์–ด๋–ป๊ฒŒ ์ ์šฉํ•˜๋Š”์ง€๋ฅผ ์•Œ์•„๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค ๐Ÿ˜Ž Flow์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ํฌ์ŠคํŒ…์€ ์•„๋ž˜ ๋งํฌ๋ฅผ ์ฐธ๊ณ  ๋ฐ”๋ž๋‹ˆ๋‹ค ๐Ÿ™ [Kotlin] ์ฝ”ํ‹€๋ฆฐ Coroutine์— ๋Œ€ํ•˜์—ฌ (3) -

devyo-111commit.tistory.com

 

LiveData๋ฅผ Flow๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ์— ์•ž์„œ ๋‘˜์˜ ์ฐจ์ด์ ๊ณผ ์™œ Flow๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


LiveData

LiveData๋Š” ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ data holder class์ž…๋‹ˆ๋‹ค.

 

๋˜ํ•œ, LiveData๋Š” ๋‹ค๋ฅธ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ตฌ์„ฑ์š”์†Œ์—์„œ ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ์ง‘ํ•ฉ์„ ๋ณด์œ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์ธ์‹ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€์ฐฐํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ํŒŒ๊ดด๋˜๊ฑฐ๋‚˜ ํ™œ์„ฑํ™”๋˜์ง€ ์•Š์œผ๋ฉด ํ•ด๋‹น observer์—๊ฒŒ ๋ฐ์ดํ„ฐ ๊ฒŒ์‹œ๋ฅผ ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค. 

 

LiveData์˜ ์žฅ์ 

- ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ์—†๋‹ค.

- ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ์ธ์‹ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ค‘์ง€๋œ ์ปดํฌ๋„ŒํŠธ๋กœ ์ธํ•œ ์ถฉ๋Œ์ด ์—†๊ณ , ์ˆ˜๋™์œผ๋กœ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•ด ์ค„ ํ•„์š”๊ฐ€ ์—†๋‹ค.

- UI์™€ Data์˜ ์ผ์น˜์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์™œ Flow๋ฅผ ์‚ฌ์šฉํ•˜๋ ค ํ• ๊นŒ์š”? ๐Ÿค”

 

LiveData ๊ฐœ์š”  |  Android ๊ฐœ๋ฐœ์ž  |  Android Developers

LiveData๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ์ธ์‹ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

developer.android.com

Android Developer์— ๋”ฐ๋ฅด๋ฉด LiveData๋Š” ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„ ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

LiveData๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ณ€๊ฒฝ๋œ ๊ฐ’์„ Main Thread์—์„œ ๊ด€์ฐฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ๋ ˆ์ด์–ด์—์„œ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ๊ด€์ฐฐํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ์—๋Š”

Flow๋ฅผ ์‚ฌ์šฉํ•œ ๋‹ค์Œ asLiveData()๋ฅผ ํ†ตํ•ด ViewModel์—์„œ ๋ณ€ํ™˜ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹๋‹ค๊ณ  ๋‚˜์™€์žˆ์Šต๋‹ˆ๋‹ค.

 

 

[Android] ์•ˆ๋“œ๋กœ์ด๋“œ Clean Architecture์— ๋Œ€ํ•ด์„œ ( 1 ) - Clean Architecture์˜ ๊ฐœ๋…

์•ˆ๋…•ํ•˜์„ธ์š” ๐Ÿ‘‹ ์˜ค๋Š˜์€ Clean Architecture์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์„ ํฌ์ŠคํŒ…ํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๊นŒ์ง€ ํฌ์ŠคํŒ…ํ•˜๊ธฐ์—” ๋‚ด์šฉ์ด ๊ธธ์–ด์งˆ ๊ฒƒ ๊ฐ™์•„ ์˜ค๋Š˜์€ ๊ฐœ๋…์— ๋Œ€ํ•ด์„œ๋งŒ ๊ฐ„๋žตํ•˜๊ฒŒ ํฌ

devyo-111commit.tistory.com

 

๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์•ˆ๋“œ๋กœ์ด๋“œ์˜ Clean Architecture ์ธก๋ฉด์—์„œ Domain Layer์—๋Š” 

์•ˆ๋“œ๋กœ์ด๋“œ ์˜์กด์„ฑ์ด ์—†๋Š” ์ˆœ์ˆ˜ ์ž๋ฐ”/์ฝ”ํ‹€๋ฆฐ ์ฝ”๋“œ๋งŒ์ด ์กด์žฌํ•ด์•ผ ํ•˜๋Š”๋ฐ ํ•ด๋‹น Layer์—์„œ LiveData๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ์—๋Š”

๋ถ€์ ํ•ฉํ•˜๋ฏ€๋กœ Flow๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๋ถ€๋ถ„์„ ํ•ด์†Œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

Android) LiveData์™€ Coroutine Flow ๋น„๊ตํ•ด๋ณด๊ธฐ

์š”์ฆ˜์— ์ฝ”๋ฃจํ‹ด์˜ flow๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์„ ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, LiveData์™€ ํ•จ๊ป˜ flow๋ฅผ ์‚ฌ์šฉ ์‹œ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฑด์ง€์— ๋Œ€ํ•œ ๊ถ๊ธˆ์ฆ์ด ์ƒ๊ฒจ์„œ ์ ํ•ฉํ•œ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊ธ€์„ ์จ๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

yoon-dailylife.tistory.com

ํ•˜์ง€๋งŒ, Flow๋Š” LiveData์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๋ฉฐ ์ƒํƒœ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์•„ ํ˜„์žฌ ๋ฌด์Šจ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ์•Œ๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, Cold Stream์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ collect ํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์•„ ์—ฐ์†ํ•ด์„œ ๋“ค์–ด์˜ค๋Š” ๊ฐ’์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๊ธฐ์— ์œ„ ์ž๋ฃŒ์— ๋”ฐ๋ฅด๋ฉด

 


LiveData๋Š” ๊ตฌ์„ฑ ๋ณ€๊ฒฝ ์‹œ์— ์•ˆ์ •์„ฑ์„ ์ œ๊ณตํ•˜๊ณ  ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋ทฐ๋กœ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

 

 Flow๋Š” UseCase, Repository, DataSources Layer์™€ ๊ธด๋ฐ€ํ•˜๊ฒŒ ์ž‘๋™ํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ ๋ฐ ์ฒ˜๋ฆฌํ•ด์„œ

์„œ๋กœ ๋‹ค๋ฅธ ์ฝ”๋ฃจํ‹ด ๋ฒ”์œ„์—์„œ ์ž‘์—…์„ ์‹คํ–‰ํ•œ๋‹ค.

 

๊ทธ๋Ÿฌ๋ฏ€๋กœ ViewModel, View ์‚ฌ์ด์˜ ์ƒํ˜ธ์ž‘์šฉ์€ LiveData, ๋” ๊นŠ์€ ๋ ˆ์ด์–ด์™€ ์“ฐ๋ ˆ๋”ฉ ๊ฐ™์€ ๋” ๋ณต์žกํ•œ ์ฒ˜๋ฆฌ๋Š” Flow๊ฐ€ ์ฒ˜๋ฆฌํ•œ๋‹ค.


 

์ฆ‰, LiveData์™€ Flow๋Š” ์„œ๋กœ์˜ ๋‹จ์ ์„ ๋ณด์™„ํ•ด ์ฃผ๋ฉฐ Repository์—์„œ ์ฒ˜๋ฆฌ๋˜๋Š” ์ผ๋ จ์˜ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์€ Flow๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•˜๊ณ 

์ „๋‹ฌ๋ฐ›์€ View์—์„œ์˜ ์ฒ˜๋ฆฌ๋Š” LiveData๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ๋กœ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๋ณด๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. 

 

ํ•˜์ง€๋งŒ โœ‹

StateFlow์˜ ๋“ฑ์žฅ

StateFlow๋ž€ ํ˜„์žฌ ์ƒํƒœ์™€ ์ƒˆ๋กœ์šด ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜์ง‘๊ธฐ์— ๋‚ด๋ณด๋‚ด๋Š” ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ ํ™€๋” ํ๋ฆ„์œผ๋กœ value ์†์„ฑ์„ ํ†ตํ•ด ํ˜„์žฌ ์ƒํƒœ๊ฐ’์„ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์œ„์—์„  ์–ธ๊ธ‰ํ•œ Flow์˜ ๋‹จ์ ์„ ํ•ด์†Œํ•˜์—ฌ LiveData์™€ ๊ฐ™์ด ๋งŒ๋“  ๊ฒƒ์ด ๋ฐ”๋กœ ์ด StateFlow์ž…๋‹ˆ๋‹ค. 

 

๊ทธ๋ ‡๋‹ค๋ฉด ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ๐Ÿ˜Ž

  • StateFlow๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Read-Only์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” MutableStateFlow๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

  • StateFlow๋Š” LiveData์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ๊ธฐ๋ณธ๊ฐ’์„ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

  • StateFlow๋Š” ๋‹ค๋ฅธ Flow์—์„œ ์ˆ˜์ง‘ํ•˜๋Š” ๊ฒฝ์šฐ LiveData์™€๋Š” ๋‹ค๋ฅด๊ฒŒ View๊ฐ€ stop ๋˜์—ˆ์„ ๋•Œ ์ž๋™์œผ๋กœ ๊ด€์ฐฐ์„ ๋ฉˆ์ถ”์ง€ ์•Š์•„
    repeatOnLifeCycle {} ํ˜น์€ flowWithLifecycle.collect{} ์„ ํ†ตํ•ด ๊ฐ’์„ ์ˆ˜์ง‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

  • repeatOnLifeCycle{} ์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ Flow๋ฅผ ์ˆ˜์ง‘ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉฐ
    flowWithLifecycle.collect {}๋Š” ํ•˜๋‚˜์˜ Flow๋งŒ ์ˆ˜์ง‘ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

 

์ง€๊ธˆ๋ถ€ํ„ฐ๋Š” ์ด์ „ ํฌ์ŠคํŒ…์— ์ด์–ด LiveData๋ฅผ Flow๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ์ž‘์—…์— ๋Œ€ํ•ด์„œ ๋ง์”€๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

 

[MyViewModel.kt] ๋„คํŠธ์›Œํฌ ํ†ต์‹  ๊ฒฐ๊ณผ๋ฅผ ๊ด€์ฐฐํ•˜๋˜ LiveData๋ฅผ stateFlow๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

 

๊ธฐ์กด

@HiltViewModel
class MyViewModel @Inject constructor(
    private val getBeachCongestionList: GetBeachCongestionList
) : ViewModel(){
    private val _networkResult = MutableLiveData<NetworkResult<BeachCongestionList>>()
    val networkResult : LiveData<NetworkResult<BeachCongestionList>>
    get() = _networkResult

    private val _beachCongestionList = MutableLiveData<BeachCongestionList>()
    val beachCongestionList : LiveData<BeachCongestionList>
    get() = _beachCongestionList
    fun setBeachCongestionList(beachCongestionList: BeachCongestionList){
        _beachCongestionList.value = beachCongestionList
    }

    private val _toastMessage = MutableLiveData<String>()
    val toastMessage : LiveData<String>
    get() = _toastMessage

    fun getBeachInfo(){
        viewModelScope.launch {
            getBeachCongestionList.invoke().collect{ result ->
                _networkResult.value = result
            }
        }
    }

    fun onError(message : String) {
        _toastMessage.value = message
    }
}

 

๋ณ€๊ฒฝ ํ›„

@HiltViewModel
class MyViewModel @Inject constructor(
    private val getBeachCongestionList: GetBeachCongestionList
) : ViewModel(){
    private val _networkResult = MutableStateFlow<NetworkResult<BeachCongestionList>>(NetworkResult.Success(BeachCongestionList(arrayListOf())))
    val networkResult : StateFlow<NetworkResult<BeachCongestionList>>
    get() = _networkResult

    private val _beachCongestionList =  MutableLiveData<BeachCongestionList>()
    val beachCongestionList : LiveData<BeachCongestionList>
    get() = _beachCongestionList
    fun setBeachCongestionList(beachCongestionList: BeachCongestionList){
        _beachCongestionList.value = beachCongestionList
    }

    private val _toastMessage = MutableLiveData<String>()
    val toastMessage : LiveData<String>
    get() = _toastMessage

    fun getBeachInfo(){
        viewModelScope.launch {
            getBeachCongestionList.invoke().collect{ result ->
                _networkResult.value = result
            }
        }
    }

    fun onError(message : String) {
        _toastMessage.value = message
    }
}

 

[MainActivity.kt] ์ „๋‹ฌ๋ฐ›์€ ๊ฐ’์„ collect ํ•˜์—ฌ ๊ฐ’์„ ๊ด€์ฐฐํ•œ๋‹ค.

 

๊ธฐ์กด

private const val TAG = "MainActivity"

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private lateinit var binding : ActivityMainBinding
    private val viewModel : MyViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater).also {
            setContentView(it.root)
        }
        observeData()
    }

    override fun onResume() {
        super.onResume()
        viewModel.getBeachInfo()
    }

    private fun observeData() {
        with(viewModel){
            networkResult.observe(this@MainActivity){ result ->
                when(result){
                    is NetworkResult.Success -> setBeachCongestionList(result.data) // ์„ฑ๊ณต์‹œ
                    is NetworkResult.Failure -> onError(result.code.toString()) // ์‹คํŒจ์‹œ
                    is NetworkResult.Exception -> onError(result.errorMessage) // ์˜ˆ์™ธ ๋ฐœ์ƒ์‹œ
                    is NetworkResult.Loading -> { // ํ†ต์‹ ์ค‘
                        // ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ํ• ์ผ์ด ์—†์Œ
                    }
                }
            }

            beachCongestionList.observe(this@MainActivity){
                Log.d(TAG, "observeData: $it")
            }
            toastMessage.observe(this@MainActivity){
                Toast.makeText(this@MainActivity , it , Toast.LENGTH_SHORT).show()
            }
        }
    }
}

 

๋ณ€๊ฒฝ ํ›„ ( flowWithLifecycle.collect {}๋ฅผ ์‚ฌ์šฉํ•œ ๋ฐฉ๋ฒ• )

private const val TAG = "MainActivity"

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private lateinit var binding : ActivityMainBinding
    private val viewModel : MyViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater).also {
            setContentView(it.root)
        }
        observeData()
    }

    override fun onResume() {
        super.onResume()
        viewModel.getBeachInfo()
    }

    private fun observeData() {
        with(viewModel){
            lifecycleScope.launch{
                networkResult.flowWithLifecycle(lifecycle , Lifecycle.State.STARTED).collect{ result -> // ํ•˜๋‚˜์˜ Flow๋งŒ collectํ• ๋•Œ ์‚ฌ์šฉ
                    when(result){
                        is NetworkResult.Success -> setBeachCongestionList(result.data)
                        is NetworkResult.Failure -> onError(result.code.toString())
                        is NetworkResult.Exception -> onError(result.errorMessage)
                        is NetworkResult.Loading -> {
                            // Noting to do
                        }
                    }
                }
            }

            beachCongestionList.observe(this@MainActivity){ list ->
                Log.d(TAG, "observeData: $list")
            }

            toastMessage.observe(this@MainActivity){ message ->
                Log.d(TAG, "observeData: $message")
            }
        }
    }
}

 

๋ณ€๊ฒฝ ํ›„ ( repeatOnLifecycle {}์„ ์‚ฌ์šฉํ•œ ๋ฐฉ๋ฒ• )

private const val TAG = "MainActivity"

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private lateinit var binding : ActivityMainBinding
    private val viewModel : MyViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater).also {
            setContentView(it.root)
        }
        observeData()
    }

    override fun onResume() {
        super.onResume()
        viewModel.getBeachInfo()
    }

    private fun observeData() {
        with(viewModel){
            lifecycleScope.launch{
                repeatOnLifecycle(Lifecycle.State.STARTED){ // ์—ฌ๋Ÿฌ๊ฐœ์˜ Flow๋ฅผ collect ํ• ๋•Œ ์‚ฌ์šฉ
                    launch {
                        networkResult.collect{ result ->
                            when(result){
                                is NetworkResult.Success -> setBeachCongestionList(result.data)
                                is NetworkResult.Failure -> onError(result.code.toString())
                                is NetworkResult.Exception -> onError(result.errorMessage)
                                is NetworkResult.Loading -> {
                                    // Noting to do
                                }
                            }
                        }
                    }
                    
                    launch{
                        // ๋‹ค๋ฅธ Flow
                    }
                }
            }

            beachCongestionList.observe(this@MainActivity){ list ->
                Log.d(TAG, "observeData: $list")
            }

            toastMessage.observe(this@MainActivity){ message ->
                Log.d(TAG, "observeData: $message")
            }
        }
    }
}

 

(์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ๋ชจ๋“  LiveData๋ฅผ StateFlow๋กœ ๋ณ€๊ฒฝ์‹œํ‚ค์ง€๋Š” ์•Š์•˜๋Š”๋ฐ ๋‹ค๋ฅธ LiveData ๋˜ํ•œ StateFlow๋กœ ๋ณ€๊ฒฝํ•ด๋„ ๋ฌด๊ด€ํ•ฉ๋‹ˆ๋‹ค.)

 

์ด๋ ‡๊ฒŒ LiveData์™€ Flow์˜ ์ฐจ์ด์  ๋ฐ StateFlow๋ฅผ ํ†ตํ•ด LiveData๋ฅผ Flow๋กœ ์ด์ „ํ•˜๋Š” ๋ฐฉ๋ฒ•๊นŒ์ง€ ๋ชจ๋‘ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

StateFlow๊ฐ€ ๋“ฑ์žฅํ•˜๋ฉด์„œ ํ˜„์žฌ ๋งŽ์€ API๊ฐ€ ๊ฐœ๋ฐœ๋˜๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™์€๋ฐ์š”

๋‚˜์ค‘์—๋Š” Kotlin๊ธฐ๋ฐ˜ ์•ฑ์—์„œ๋Š” LiveData๋Œ€์‹  Flow๋ฅผ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์ง€ ์•Š์„๊นŒ ์ƒ๊ฐํ•ด ๋ด…๋‹ˆ๋‹ค. ๐Ÿค”

 

์—ฌ๋Ÿฌ๋ถ„๋“ค๋„ LiveData๋ฅผ ์‚ฌ์šฉํ•˜๋˜ ํ”„๋กœ์ ํŠธ๋ฅผ StateFlow๋กœ ์ด์ „ํ•ด ๋ณด๋Š” ๊ฑด ์–ด๋–จ๊นŒ์š”?

 

๊ธด ๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌ๋“œ๋ฆฌ๋ฉฐ

์ด์ƒ ํฌ์ŠคํŒ…์„ ๋งˆ์น˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์˜ค๋Š˜๋„ ์ฆ์ฝ”ํ•˜์„ธ์š” :)

๋Œ“๊ธ€์ˆ˜0