[Kotlin] ์ฝํ๋ฆฐ Coroutine์ ๋ํ์ฌ (3) - Coroutine Flow(ํ๋ก์ฐ)
์๋ ํ์ธ์๐
์ค๋์ ์ฝ๋ฃจํด ๊ด๋ จ ๋ง์ง๋ง ํฌ์คํ ์ผ๋ก
์ฝ๋ฃจํด 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() )ํ์ฌ ์ถ๋ ฅํด์ฃผ๋๋ก ํ์์ต๋๋ค.

์ด๋ฏธ์ง ์ฐธ์กฐ : 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๋ฅผ ํตํด ์ ๋ฌ๋ ๊ฐ์ ์๋์ผ๋ก ๋งคํํ์ฌ ๋ฐํํด์ค๋๋ค.
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()๋ณด๋ค๋ ์กฐ๊ธ ๋ ๋ณต์กํ ์ฐ์ฐ์ ํ ๋ ์ฃผ๋ก ์ฌ์ฉํ๋ค๊ณ ๋ณด๋ฉด ๋ ๊ฒ ๊ฐ์ต๋๋ค.
์ด ์ธ์๋ ๋ง์ ์ค๊ฐ ํจ์๋ค์ด ์กด์ฌํ๋ฉฐ ์ํฉ์ ๋ฐ๋ผ ์ฌ์ฉํด์ค๋ค๋ฉด ๊ฐ๋ ฅํ ๋๊ตฌ๊ฐ ๋ ๊ฒ ๊ฐ์ต๋๋ค.
์ด์์ผ๋ก ์ฝ๋ฃจํด Flow์ ๊ดํ ํฌ์คํ ์ ๋ง์น๊ฒ ์ต๋๋ค. ๐
์ค๋๋ ์ฆ์ฝ ํ์ธ์ :)