[Android] StateFlow์™€ LiveData

์ง€๊ธˆ๋ณ„ยท2024๋…„ 2์›” 25์ผ
post-thumbnail

๐Ÿค” StateFlow๋ž€?

์ด ๊ธ€์„ ์ฝ๊ธฐ ์ „์— Flow๋ž€? ๊ธ€์„ ์ฝ์ง€ ์•Š์œผ์…จ๋‹ค๋ฉด ์ฝ๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

Flow๋Š” ํŠน์ • ์‹œ์ ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์•Œ๊ธฐ ์–ด๋ ค์› ๊ณ , ์ƒํƒœ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค UI์—์„œ ๋งค๋ฒˆ ๊ตฌ๋…์„ ํ•˜๊ธฐ์— ๊ฐœ๋ฐœ์— ์–ด๋ ค์›€์ด ์žˆ์Šต๋‹ˆ๋‹ค.
์ด๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด StateFlow๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.

StateFlow๋Š” ํ•ญ์ƒ ํ˜„์žฌ ๊ฐ’์„ ๊ฐ€์ง€๋ฉฐ, ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋Š” ๋ชจ๋“  ๊ตฌ๋…์ž์—๊ฒŒ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์ฆ‰์‹œ ์•Œ๋ ค์ฃผ๊ธฐ์— ๊ตฌ๋…๋งŒ ํ•˜๊ณ  ์žˆ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
์ด๋ฅผ ํ†ตํ•ด ์›ํ•˜๋Š” ์‹œ์ ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
StateFlow๋Š” Hot Stream์ž…๋‹ˆ๋‹ค.
Hot Stream ์ด๋ž€ ๋ฆฌ์•กํ‹ฐ๋ธŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ(๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ)์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฐœ๋…์œผ๋กœ, ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๋ฐ”๋€ ๋‚ด์šฉ์„ ๋ฐ”๋กœ๋ฐ”๋กœ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
์ฆ‰, ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ์ฒ˜๋ฆฌ์— ์ ํ•ฉํ•œ ํŠน์„ฑ์„ ๊ฐ€์ง„ Stream์ž…๋‹ˆ๋‹ค.

๐Ÿ‘โ€๐Ÿ—จ Flow์— ๊ด€ํ•œ ๊ธ€์—์„œ๋„ ์ •์ˆ˜๊ธฐ๋กœ ์˜ˆ๋ฅผ ๋“ค์—ˆ๋Š”๋ฐ ์ด๋ฒˆ์—๋„ ์ •์ˆ˜๊ธฐ๋กœ ์˜ˆ๋ฅผ ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ฌผํƒฑํฌ = ์ƒ์‚ฐ์ž / ์ •์ˆ˜๊ธฐ์˜ ๋ฌผ ์ƒํƒœ ๋ณ€๊ฒฝ ๊ธฐ๋Šฅ = ์ค‘๊ฐœ์ž / ์‚ฌ๋žŒ = ์†Œ๋น„์ž / ๋ฌผ = data

์ด๋ฒˆ์—” ๋ฌผํƒฑํฌ์— ๋ฌผ์ด ์ด๋ฏธ ์ƒ์‚ฐ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
์ •์ˆ˜๊ธฐ๋กœ ๋ฌผ์„ ์˜จ์ˆ˜๋กœ ํ• ์ง€, ๋ƒ‰์ˆ˜๋กœ ํ• ์ง€, ์–ผ์Œ์œผ๋กœ ํ• ์ง€ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
์‚ฌ๋žŒ์ด ๋ฌผ์„ ์†Œ๋น„(collect)ํ•˜๋ฉด ์ •์ˆ˜๊ธฐ์—์„œ ๋ฌผ์ด ๋‚˜์™€ ๋ฌผ์„ ๋งˆ์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์ด๋ฒˆ์—” ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋˜์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

StateFlow๋Š” Hot Stream์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋˜์–ด ์žˆ๊ณ  ์ƒํƒœ๊ฐ€ ์œ ์ง€๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ ์ •์ˆ˜๊ธฐ์˜ ๋ฌผ์„ ์†Œ๋น„(collect)ํ•˜๊ธฐ ์œ„ํ•ด ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €๋”๋‹ˆ ๋ฌผ์ด ๋ฉˆ์ถ”์งˆ ์•Š๊ณ  ๊ณ„์† ๋‚˜์˜ค๋Š” ๊ฒ๋‹ˆ๋‹ค.
(์ด ๋ฌธ์ œ์˜ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์—์„œ ๋‹ค๋ค„ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.)

๊ทธ๋ฆฌ๊ณ  ์ •์ˆ˜๊ธฐ์—์„œ ๋‚˜์˜จ ๋ฌผ์„ ์ •์ˆ˜๊ธฐ์— ๋„ฃ์œผ๋ฉด ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋ฉด ๋”ฐ๋ผ๋†“์€ ๋ฌผ(ํŠน์ • ์‹œ์ ์˜ data)์„ ๊ฐ‘์ž๊ธฐ ์–ผ์Œ์œผ๋กœ ๋จน๊ณ  ์‹ถ๋‹ค๋ฉด ๋ฌผ์„ ์ •์ˆ˜๊ธฐ์— ๋„ฃ์œผ๋ฉด ์–ผ์Œ์œผ๋กœ ๋ณ€ํ™˜์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.





๐ŸŸก StateFlow์™€ LiveData?

StateFlow์™€ LiveData๋Š” ๋น„์Šทํ•œ ์ ์ด ๋งŽ์Šต๋‹ˆ๋‹ค.
๋ชจ๋‘ ๋ฐ์ดํ„ฐ ํ™€๋” ํด๋ž˜์Šค๋กœ ์ƒํƒœ๋ฅผ ๊ด€์ฐฐํ•˜์—ฌ ์ƒํƒœ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ ๋ฉ๋‹ˆ๋‹ค.
์ƒํƒœ๋ฅผ ๊ด€์ฐฐํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ตœ์‹ ์˜ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ LiveData๋Š” ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์ธ์‹ํ•˜๊ณ  ์žˆ์–ด์„œ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ํŒŒ๊ดด๋  ๋•Œ ๊ฐ™์ด ํŒŒ๊ดด๋˜์ง€๋งŒ StateFlow๋Š” ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋Š” StateFlow๋ฅผ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ด์œ ๋ฅผ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1. ์Šค๋ ˆ๋“œ ๋ฌธ์ œ
LiveData๋Š” ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ๋งŒ ๊ฐ’์„ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด ๋œป์€ Livedata๋Š” Main ์Šค๋ ˆ๋“œ๋ฅผ ๋ฒ—์–ด๋‚˜ Input/Output ์Šค๋ ˆ๋“œ ์ƒํƒœ ์ฆ‰ ์ž…๋ ฅ์ด ์ผ์–ด๋‚  ๋•Œ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

2. ๋ฐฑ ํ”„๋ ˆ์…”(Back Pressure) ๋ฌธ์ œ
LiveData๋Š” ๋ฐฑ ํ”„๋ ˆ์…” ๋ฌธ์ œ๋ฅผ ๋‹ค๋ฃจ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
๋ฐฑ ํ”„๋ ˆ์…”๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์‚ฐํ•˜๋Š” ์†๋„๊ฐ€ ์†Œ๋น„ํ•˜๋Š” ์†๋„๋ณด๋‹ค ๋น ๋ฅผ ๋•Œ๋ฅผ ์–˜๊ธฐํ•˜๋Š”๋ฐ ์œ„์— ์ ํžŒ ์˜ˆ์‹œ๋กœ ์„ค๋ช…ํ•˜์ž๋ฉด ๋ฌผํƒฑํฌ๊ฐ€ ๋ฌผ์„ ์ƒ์‚ฐํ•˜๋Š”๋ฐ ๋งŒ์•ฝ ๋ฌผํƒฑํฌ ์šฉ๋Ÿ‰์ด ์ •ํ•ด์ ธ ์žˆ๋‹ค๊ณ  ํ•  ๋•Œ, ๋ฌผ์„ ๊ณ„์† ์ƒ์‚ฐํ•˜๋Š”๋ฐ ๋ฌผ ๋งˆ์‹œ๋Š” ์‚ฌ๋žŒ์ด ์—†์œผ๋ฉด ๋ฌผํƒฑํฌ๊ฐ€ ํ„ฐ์ ธ ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ StateFlow๋Š” ๋ฐฑ ํ”„๋ ˆ์…” ๋ฌธ์ œ๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

3. ์ƒ๋ช…์ฃผ๊ธฐ ๋ณด์™„
StateFlow๋Š” ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ์ธ์‹ํ•˜์ง€ ๋ชปํ•œ๋‹ค๊ณ  ํ•˜์ง€๋งŒ lifecycleScope๋ฅผ ํ†ตํ•ด ๋ณด์™„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.





๐Ÿ’ป StateFlow ์˜ˆ์ œ

์˜ˆ์‹œ์—์„œ ๋ฌผ์ด ๊ณ„์† ํ๋ฅด๋Š” ๊ฑธ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•๊ณผ ์ •์ˆ˜๊ธฐ๋กœ ๋ฐ›์€ ๋ฌผ์„ ์–ด๋–ป๊ฒŒ ๋ณ€ํ™˜ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•๋„ ๊ฐ™์ด ์•Œ์•„ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1. LiveData๋ฅผ StateFlow๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ๊ฒฐ๊ณผ ๊ฐ’ ์ถœ๋ ฅ

	// ViewModel
    
    /** LiveData ํ˜•ํƒœ **/
    private var mutableLiveData = MutableLiveData<Int>()
    val liveData : LiveData<Int>
        get() = mutableLiveData


    /** StateFlow ํ˜•ํƒœ **/
    val mutableStateFlow = MutableStateFlow(0)

    fun setData(value : Int)
    {
        mutableStateFlow.value = value
    }

	// Activity

	/** ๊ฒฐ๊ณผ๊ฐ’ **/
	resultBtn.setOnClickListener {
		viewModel.setData(editValue.text.toString().toInt())
		lifecycleScope.launch {
			viewModel.mutableStateFlow.collect {
				resultText.text = it.toString()
			}
		}
	}



2. ๋ฌผ์ด ๊ณ„์† ํ๋ฅด๋Š” ํ•ด๊ฒฐ์ฑ…? = repeatOnLifecycle

  • repeatOnLifecycle์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฌผ์ด ๊ณ„์† ํ๋ฅด๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ์ฆ‰, ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ํ•ด๊ฒฐํ•ด ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ex) repeatOnLifecycle(Lifecycle.State.STARTED) : ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ์‹œ์ž‘ ๋ ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€์ฐฐํ•˜๊ณ  ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ํŒŒ๊ดด๋ ๋•Œ ์†Œ๋น„์ž๋„ ๊ตฌ๋…์„ ํ•˜์ง€ ์•Š๋Š” ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
	// Activity
	/** ๊ฒฐ๊ณผ๊ฐ’ **/
	resultBtn.setOnClickListener {
		viewModel.setData(editValue.text.toString().toInt())
		lifecycleScope.launch {
			repeatOnLifecycle(Lifecycle.State.STARTED){
				viewModel.mutableStateFlow.collect {
					resultText.text = it.toString()
				}
			}
		}
	}



3. ๋ฐ›์€ ๋ฌผ์„ ๋ณ€ํ™˜ ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•? = ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ

  • map, filter, reduce, transform ๋‹ค์–‘ํ•œ ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
	// viewModel
	val transformationStateFlow = mutableStateFlow.asStateFlow().map { it * it }
	// Activity
	/** ๋ณ€ํ™˜๋œ ๊ฒฐ๊ณผ ๊ฐ’ **/
	transformationBtn.setOnClickListener {
		viewModel.setData(editValue.text.toString().toInt())
		lifecycleScope.launch {
			repeatOnLifecycle(Lifecycle.State.STARTED) {
				viewModel.transformationStateFlow.collect {
					resultText.text = it.toString()
				}
			}
		}
	}





๐Ÿ˜Š ๋งˆ์น˜๋ฉฐ

์ด๋ฒˆ ์‹œ๊ฐ„์— StateFlow์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.
LiveData๋ณด๋‹ค StateFlow๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋” ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.



๐Ÿ“• ์ฐธ๊ณ ์ž๋ฃŒ

์•ˆ๋“œ๋กœ์ด๋“œ ๊ณต์‹ ๋ฌธ์„œ
StateFlow Github

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