Tutorial/tutorial

[Kotlin] ์ฝ”ํ‹€๋ฆฐ Coroutine์— ๋Œ€ํ•˜์—ฌ (3) - Coroutine Flow(ํ”Œ๋กœ์šฐ)

๋ށ์š” 2022. 10. 13. 19:22

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

์˜ค๋Š˜์€ ์ฝ”๋ฃจํ‹ด ๊ด€๋ จ ๋งˆ์ง€๋ง‰ ํฌ์ŠคํŒ…์œผ๋กœ

์ฝ”๋ฃจํ‹ด Flow์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์„ ํฌ์ŠคํŒ…ํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

 


1. Coroutine Flow๋ž€?

Flow๋Š” ์ˆœ์ฐจ์ ์œผ๋กœ ๊ฐ’์„ ๋‚ด๋ณด๋‚ด๊ณ  ์ •์ƒ์ ์œผ๋กœ ๋˜๋Š” ์˜ˆ์™ธ๋กœ ์™„๋ฃŒ๋˜๋Š” ๋น„๋™๊ธฐ์ ์ธ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์ž…๋‹ˆ๋‹ค.

 

Flow์—๋Š” ์ด 3๊ฐ€์ง€ ์—ญํ• ์ด ์กด์žฌํ•˜๋ฉฐ ๊ฐ๊ฐ์˜ ์„ค๋ช…์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Producer (์ƒ์‚ฐ์ž)

๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ(flow)์„ ์ƒ์‚ฐํ•˜๋ฉฐ ์ฝ”๋ฃจํ‹ด์„ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ƒ์‚ฐ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด ์คŒ

InterMediary(์ค‘๊ฐœ์ž)

Producer๊ฐ€ ๋‚ด๋ณด๋‚ด๋Š” ๊ฐ๊ฐ์˜ ๊ฐ’์ด๋‚˜ ์ŠคํŠธ๋ฆผ ์ž์ฒด๋ฅผ ์ˆ˜์ •, ์‚ญ์ œ,๋ณ‘ํ•ฉ ๋“ฑ์˜ ์กฐ์ž‘ ์ž‘ ์—…์„ ์ˆ˜ํ–‰

consumer(์†Œ๋น„์ž , ๊ตฌ๋…์ž)

์ŠคํŠธ๋ฆผ์„ ์†Œ๋น„ํ•˜๋Š” ๊ณณ( Android์—์„œ๋Š” ์ฃผ๋กœ UI ์ž‘์—…)

 


2. Cold Flow์™€ Hot Flow

Cold Flow๋Š” ์†Œ๋น„์ž(๊ตฌ๋…์ž)๋งˆ๋‹ค ๊ฐœ๋ณ„ Flow ์ƒ์„ฑ์ด ์‹œ์ž‘๋˜์–ด ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋ฉฐ Flow ์ƒ์„ฑ ํ›„ ์†Œ๋น„(collect {...})๋ฅผ(collect{...}) ํ•˜์ง€ ์•Š์œผ๋ฉด ์–ด๋–ค ์—ฐ์‚ฐ๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. (์ฆ‰, ์†Œ๋น„ํ•˜์ง€ ์•Š์œผ๋ฉด ์–ด๋–ค ์—ฐ์‚ฐ๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.) 

๋ฐ˜๋ฉด, Hot Flow๋Š” Flow๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ๋ฐ”๋กœ ์—ฐ์‚ฐ์ด ์‹œ์ž‘๋˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋ฉฐ ๋‹ค์ˆ˜์˜ ์†Œ๋น„์ž๊ฐ€ ๋™์ผ ํ•œ Flow์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

โœ”๏ธ Flow์—์„œ ์ค‘๊ฐ„์—ฐ์‚ฐ์ž(์ค‘๊ฐœ์ž)๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด Flow๋Š” Cold ํ•ด์ง‘๋‹ˆ๋‹ค. (์ค‘๊ฐ„ ์—ฐ์‚ฐ์ž๊ฐ€ ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค Producer ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ)

 

 

๊ทธ๋ ‡๋‹ค๋ฉด Flow๋Š” ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ๊นŒ์š”?? ๐Ÿค”

 

๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒ์‚ฐ์ž์—์„œ ๋ฐ์ดํ„ฐ์˜ ํ๋ฆ„์„ ๋ฐฉ์ถœ ( emit() )ํ•˜๊ณ  ์†Œ๋น„์ž์—์„œ ๊ทธ ํ๋ฆ„์„ ๋ชจ์•„์„œ ์†Œ๋น„( collect() )ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์„ค๋ช… ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

 

import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking

fun coroutineFlowAction(): Flow<Int> { return flow {
    (1..5).forEach { delay(1000)
        /**
         * ํ˜ธ์ถœํ•œ ์ชฝ(์†Œ๋น„์ž,๊ตฌ๋…์ž)์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฉ์ถœํ•œ๋‹ค */
        emit(it)
    } }
}
fun main() = runBlocking {
// ํ๋ฆ„์„ ๋ชจ์€๋‹ค(collect) 
 coroutineFlowAction().filter {
    it % 2 != 0 }.collect{
    println(it) }
}

 

์œ„ ์˜ˆ์ œ๋Š” ์†Œ์ˆ˜๋ฅผ ๊ตฌํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋กœ Flow <T> ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” coroutineFlowAction() ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ flow builder๋ฅผ ํ†ตํ•ด

flow๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ main ํ•จ์ˆ˜์—์„œ ํ•ด๋‹น Flow๊ฐ์ฒด ์ค‘ ์†Œ์ˆ˜๋งŒ์„ filter ํ•˜์—ฌ ์†Œ๋น„( collect() )ํ•˜์—ฌ ์ถœ๋ ฅํ•ด์ฃผ๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

์†Œ์ˆ˜๋งŒ ์ถœ๋ ฅ๋œ ํ™”๋ฉด

 

Flow์˜ ํ๋ฆ„ ์ดํ•ด๋„

 ์ด๋ฏธ์ง€ ์ฐธ์กฐ : https://myungpyo.medium.com/

 

์œ„ ์ด๋ฏธ์ง€๋Š” ํ•ด๋‹น ํฌ์ŠคํŒ…์—์„œ ์ฐธ์กฐํ•˜์˜€์œผ๋ฉฐ Flow์˜ ํ๋ฆ„์— ๋Œ€ํ•ด ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ์„ค๋ช…๋˜์–ด ์žˆ์–ด ์ฐธ๊ณ  ์ž๋ฃŒ๋กœ ์ฒจ๋ถ€ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


3. Flow์˜ ์ค‘๊ฐ„ ํ•จ์ˆ˜

์ค‘๊ฐ„ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด Flow์—์„œ emit()ํ•œ ๊ฐ’๋“ค์„ ์ „๋‹ฌ๋ฐ›์•„ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ฐ”๋กœ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

map()

์ธ๋ฐ”์šด๋“œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์—…์ŠคํŠธ๋ฆผ(map)์„ ํ†ตํ•ด ๊ฒฐ๊ณผ(๋‹ค์šด์ŠคํŠธ๋ฆผ)์™€ ๋งคํ•‘ํ•˜๋Š” ํ•จ์ˆ˜

import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.runBlocking

suspend fun flowRequest(inputValue: Int): String { delay(1000)
    return "์‘๋‹ต : $inputValue" }
fun main() {
    runBlocking {
        (1..5).asFlow() //Flow ์ƒ์„ฑ
            .take(3).map(::flowRequest).collect(::println)
    }
}

 

์œ„ ์˜ˆ์ œ๋Š” 1~5๊นŒ์ง€ ์ˆ˜๋ฅผ Flow๋กœ ๋ฐœํ–‰ํ•˜์—ฌ 3ํšŒ ๋™์•ˆ mapํ•จ์ˆ˜๋ฅผ ๊ฑฐ์ณ ๋ฐ˜ํ™˜๋œ ๊ฐ’์„ print ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค.

์œ„์™€ ๊ฐ™์ด mapํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋งคํ•‘ํ•  ๋™์ž‘์„ ์ •์˜ํ•ด์ฃผ๋ฉด Flow๋ฅผ ํ†ตํ•ด ์ „๋‹ฌ๋œ ๊ฐ’์„ ์ž๋™์œผ๋กœ ๋งคํ•‘ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ด์ค๋‹ˆ๋‹ค.

 

map ์‹คํ–‰ ๊ฒฐ๊ณผ

 

transform()

๋‹จ์ˆœ ๋ณ€ํ˜•(map,๋ณด๋‹ค๋Š” ๋ณต์žกํ•œ ๋ณ€ํ˜•์— ์ ํ•ฉ

import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.runBlocking

fun main() {
    runBlocking { (1..10).asFlow()
    .transform { delay(1000)
        if (it % 2 != 0) { emit(it)
            emit(it * it)
            emit(it * it * it) }
    }
    .collect { println(it) } }
}

 

์œ„ ์˜ˆ์ œ๋Š” 1~10๊นŒ์ง€์˜ ๊ฐ’์„ Flow๋กœ ๋ฐœํ–‰ํ•˜์—ฌ transform์„ ํ†ตํ•ด collect()ํ•  ๋•Œ ์ œ๊ณฑ๊ณผ 3 ์ œ๊ณฑ์„ ํ•จ๊ป˜ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค. ๊ฒฐ๋ก ์ ์œผ๋กœ๋Š” map๊ณผ ๋™์ผํ•˜๊ฒŒ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ด์ฃผ์ง€๋งŒ,

map()๋ณด๋‹ค๋Š” ์กฐ๊ธˆ ๋” ๋ณต์žกํ•œ ์—ฐ์‚ฐ์„ ํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๋ณด๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

transform ์‹คํ–‰ ๊ฒฐ๊ณผ

 


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

 

์ด์ƒ์œผ๋กœ ์ฝ”๋ฃจํ‹ด Flow์— ๊ด€ํ•œ ํฌ์ŠคํŒ…์„ ๋งˆ์น˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿ‘‹

 

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