#1 Cold Flow์ Hot Flow
#1-1 ๊ฐ์
๋ฐ์ดํฐ ์คํธ๋ฆผ์ ํฌ๊ฒ Cold Flow์ Hot Flow๋ก ๋๋ ์ ์๋ค. ์ด ๋ถ๋ฅ ๊ธฐ์ค์ ๋ํด ์์๋ณธ๋ค. ๋, Kotlin์ Coroutines Flow๋ฅผ ํ์ฉํด ๊ฐ๋จํ Cold Flow ๋ฐ Hot Flow๋ฅผ ๊ตฌํํด๋ณธ๋ค.
#1-2 ํ๋ฅญํ ๋น์
What is the hot flow and cold flow in coroutines and the difference between them?
I am mastering Kotlin coroutines and trying to figure out 1- what is hot flow and cold flow ? 2- what is the main difference between them? 3- when to use each one?
stackoverflow.com
์ ๋งํฌ๋ Cold Flow์ Hot Flow์ ์ฐจ์ด๋ฅผ ๋ฌป๋ ์ง๋ฌธ๊ธ์ด๋ค. ์ด ์ง๋ฌธ์ ๋ํ ๋ต๋ณ ์ค ์์ฃผ ํ๋ฅญํ ๋น์ ๋ฅผ ํ ๋ต๋ณ์ด ์๋ค. ๊ทธ ๋ต๋ณ์ ๋ฒ์ญํ๋ฉด,
์ฝ๋ ํ๋ก์ฐ(Cold flow): ๋ฆฌ์ค๋(Listener)๊ฐ ์กด์ฌํด์ผ๋ง ๋ฐ์ดํฐ๊ฐ ๋ฐฉ์ถ๋จ.
์ค์ํ ์์
๋น์ ์ ๊ฑฐ์ค TV๋ก ์คํ์ด๋๋งจ: ์ดํฌ๋ก์ค ๋ ์ ๋๋ฒ์ค๋ฅผ ๋ณด๋ ค๊ณ ํ๋ค. ๊ฑฐ์ค TV๋ IPTV๋ผ์, ์ด ์ํ๋ ๋ด๊ฐ ์ํ ๋ ์ฆ ์ํ์ ๊ฒฐ์ ํด์ ์ฌ์ ๋ฒํผ์ ๋๋ฅผ ๋ ์์๋๋ค.
ํซ ํ๋ก์ฐ(Hot flow): ๋ฐ์ดํฐ๊ฐ ๋ฆฌ์ค๋(Listener)์ ์กด์ฌ ์ฌ๋ถ์ ๊ด๊ณ์์ด ๋ฐฉ์ถ๋จ.
์ค์ํ ์์
๋น์ ์ ์ํ ๋ฐ๋ฒคํ์ด๋จธ๋ฅผ ๋ณด๋ฌ ๊ฐ๋ ค๊ณ ์ํ๊ด์ผ๋ก ์ฐจ๋ฅผ ๋ชฐ์๋ค. ๊ทธ๋ฌ๋ ๊ฐ๋ ๋์ค ๊ตํต ์ฒด์ฆ์ ๊ฑธ๋ ค, ์ ์๊ฐ์ ๋์ฐฉํ์ง ๋ชปํ๋ค. ๊ทธ๋ ๋ค๊ณ ์ง๊ฐํ ๋น์ ์ ์ํด ์ํ ์์์ด ๋ฏธ๋ค์ง๋ ์ผ์ ์์ ๊ฒ์ด๋ค.
Cold Flow๋ VOD์ ๊ฐ๊ณ , Hot Flow๋ ์ํ๊ด๊ณผ ๊ฐ๋ค. ์ค์ํ ํค์๋๋ ๋ฆฌ์ค๋์ ์กด์ฌ๋ค. ๋ด๊ฐ ๊ด์ฐฐํด์ผ๋ง ์งํ๋๋ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ์ฝ๋ ํ๋ก์ฐ๋ค. ๋ฐ๋ฉด, ๋ด๊ฐ ์์ด๋ ์์์ ์ฐฉ์ฐฉ ์งํ๋๋ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ํซ ํ๋ก์ฐ๋ค.
#1-3 ๊ฐ๊ฐ์ ์ฉ๋
Cold Flow ๋ฐ Hot Flow๋ ๊ทธ ํน์ฑ์ ๋ง๊ฒ, ์๋ก ์ฌ์ฉ๋๋ ์ฉ๋๊ฐ ๋ค๋ฅด๋ค. Cold Flow๋ ๋ฆฌ์ค๋(Listener, ๋๋ Subcriber) ๊ฐ๊ฐ์ธ์ด ๋ณด๋ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค Query ์์ฒญ์ด ๋ํ์ ์ด๋ค. Hot Flow๋ ์จ๋ผ์ธ RPG ๊ฒ์์์ ํ๋์ ์์ฑ(๋ฆฌ์ )๋๋ ๋ชฌ์คํฐ๋ฅผ ์๋ก ๋ค ์ ์๋ค. ๋ด๊ฐ ํ๋๋ก ๋๊ฐ์ง ์๊ณ ๋ง์์๋ง ์์์์ง๋ผ๋, ํ๋์๋ ๋ชฌ์คํฐ๊ฐ ์ฐจ ์์ด์ผ ํ๋ค. ๋ค๋ฅธ ์ฌ๋๋ค๊ณผ ๋์ ์ ์ํ๋ ์จ๋ผ์ธ RPG๋๊น ๋ง์ด๋ค
์ฌ๋ด
์์ฆ์๋ ์๋ฒ์ ์์ ๋ญ๋น๋ฅผ ๋ง๊ธฐ ์ํด์ ํ๋์ ํ๋ ์ด์ด๊ฐ ํ ๋ช ์ด์ ์กด์ฌํด์ผ ๋ชฌ์คํฐ๋ฅผ ์์ฑํ๋ ๊ฒ์๋ ๋ง๋ค. ์ด ๊ฒฝ์ฐ ๋ชฌ์คํฐ์ ์์ฑ ํธ๋ฆฌ๊ฑฐ๋ Cold Flow์ ๊ด๋ จ์ด ์๊ฒ ๋์ง๋ง, ์์ฑ์ด ์๋ฃ๋ ๋ชฌ์คํฐ ๋ฐ์ดํฐ๋ ์ฌ์ ํ Hot Flow๋ค. ๋ง์ฝ ์์ฑ๋ ๋ชฌ์คํฐ ๋ฐ์ดํฐ๊ฐ Cold Flow๋ผ๋ฉด, ๋ชฌ์คํฐ์ ์ธ์ฐ๋ ๋์ ๋ชจ์ต์ด ๋ค๋ฅธ ํ๋ ์ด์ด ์ ์ฅ์์๋ ํ๊ณต์ ์นผ์ง์ ํ๋ ๋ชจ์ต์ผ๋ก ๋ณด์ผํ ๋ ๋ง์ด๋ค.
#2 ์ฝ๋
#2-1 build.gradle.kts์ dependencies์ Coroutines ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ
plugins {
...
}
...
repositories {
...
}
dependencies {
...
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}
tasks.test {
...
}
kotlin {
...
}
#2-2 ๋ฐ #2-3์์ ์ฌ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ค์ด๋ก๋ํ๋ค.
#2-2 Cold Flow์ ์์
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking
fun main() {
val coldFlow = flow {
emit("1")
delay(1000)
emit("2")
delay(1000)
emit("3")
// ๋ ์ด์ ์ํ๋ emit()์ด ์์ผ๋ฏ๋ก ์ฌ๊ธฐ์์ flow๋ ์ข
๋ฃ๋๋ค.
}
runBlocking {
coldFlow.collect { value ->
println("(๋ฆฌ์ค๋ 1) Collected value: $value")
}
println("(๋ฆฌ์ค๋ 1) \'์์ง\' ์๋ฃ")
}
// ํ๋ฒ ๋ collect()
runBlocking {
coldFlow.collect { value ->
println("(๋ฆฌ์ค๋ 2) Collected value: $value")
}
println("(๋ฆฌ์ค๋ 2) \'์์ง\' ์๋ฃ")
}
}
/* ↑ ↑ ↑ ์ถ๋ ฅ ๊ฒฐ๊ณผ
(๋ฆฌ์ค๋ 1) Collected value: 1
(๋ฆฌ์ค๋ 1) Collected value: 2
(๋ฆฌ์ค๋ 1) Collected value: 3
(๋ฆฌ์ค๋ 1) '์์ง' ์๋ฃ
(๋ฆฌ์ค๋ 2) Collected value: 1
(๋ฆฌ์ค๋ 2) Collected value: 2
(๋ฆฌ์ค๋ 2) Collected value: 3
(๋ฆฌ์ค๋ 2) '์์ง' ์๋ฃ
*/
collect()ํ ๋๋ง๋ค ๋ฆฌ์ค๋๊ฐ ์๋ก ๋ฑ๋ก๋๋ค. ์ฆ, #1-2์ ์์์ ์๋ VOD ์๋น์ค๊ฐ 2๋ฒ ์์ฒญ๋ ์
์ด๋ค.
#2-3 Hot Flow์ ์์ (SharedFlow)
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow
fun main() {
/* MutableSharedFlow<Int>(replay = 3)๊ณผ ๊ฐ์ด ๋งค๊ฐ๋ณ์๋ฅผ ๋ฃ์ด์ค ์๋ ์๋ค.
* replay์ ์ธ์๋ฅผ ์ ๋ฌํ๋ฉด,
* ๋ฆฌ์ค๋๊ฐ ์๋ก ์๊ฒจ๋ ๋,
* replay ๊ฐ๋งํผ ์ต๊ทผ์ ๋ฐฉ์ถ(emit)๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๋ฐ์ ์ ์๋ค.
* ๋ง ๊ทธ๋๋ก 'replay'๋ค.
*/
val hotFlow = MutableSharedFlow<Int>()
CoroutineScope(Dispatchers.Default).launch {
hotFlow.collect { value ->
println("(๋ฆฌ์ค๋ 1) Collected value: $value")
}
}
runBlocking {
delay(1000) // emit(1) ์ ๊น์ง ์์ ์๋ collect() ๊ตฌ๋ฌธ์ด ๋๋ํ ์๋ฃ๋๋๋ก, delay๋ฅผ ๋ฃ์ด์ค.
hotFlow.emit(1)
delay(1000)
hotFlow.emit(2)
delay(1000)
hotFlow.emit(3)
delay(1000)
/* ๋ ์ด์ ์ํ๋ emit()์ด ์์ง๋ง,
* Hot Flow๋ ์ข
๋ฃ๋์ง ์๋๋ค.
* ์๋ํ๋ฉด ๋ฏธ๋์ ๋ค์ emit()๋ ๊ฐ๋ฅ์ฑ์ด ์๊ธฐ ๋๋ฌธ์ด๋ค.
*/
}
// ํ๋ฒ ๋ collect()
CoroutineScope(Dispatchers.Default).launch {
hotFlow.collect { value ->
println("(๋ฆฌ์ค๋ 2) Collected value: $value")
}
}
runBlocking {
delay(1000)
hotFlow.emit(4)
delay(1000)
hotFlow.emit(5)
delay(1000)
}
}
/* ↑ ↑ ↑ ์ถ๋ ฅ ๊ฒฐ๊ณผ
(๋ฆฌ์ค๋ 1) Collected value: 1
(๋ฆฌ์ค๋ 1) Collected value: 2
(๋ฆฌ์ค๋ 1) Collected value: 3
(๋ฆฌ์ค๋ 1) Collected value: 4
(๋ฆฌ์ค๋ 2) Collected value: 4
(๋ฆฌ์ค๋ 1) Collected value: 5
(๋ฆฌ์ค๋ 2) Collected value: 5
*/
์ฌ๊ธฐ์ ์ฌ์ฉ๋ Flow๋ SharedFlow๋ก, ์ฝํ๋ฆฐ์ ๋ํ์ ์ธ Hot Flow ์ธํฐํ์ด์ค๋ค. #2-2์ ์ฝ๋์๋ ๋ค๋ฅด๊ฒ '์์ง' ์๋ฃ๋ผ๋ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ ์ฝ๋๊ฐ ์๋ค. Hot Flow๋ ๋ฌดํํ ํ์ฑ์ ์ง๋ ๊ณ ๋ฌด์ค์ฒ๋ผ ๊ทธ ๋์ด ์๊ธฐ ๋๋ฌธ์ด๋ค. ์๋ฅผ ๋ค์ด, ์ ์ฝ๋์ ๋ง์ง๋ง ๋ถ๋ถ์ hotFlow.emit(6)๋ฅผ ๋ฃ์ผ๋ฉด ๋ ๋ฆฌ์ค๋์ ์๋ช
์ ๊ทธ๋งํผ ๋ ๋์ด๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ #2-2์ ์ฝ๋์๋ ๋ค๋ฅด๊ฒ collect()๋ฅผ runBlocking { ... }์ด ์๋๋ผ CoroutineScope.launch { ... } ๋ธ๋ก์ ๋ฃ์ ๊ฒ์ด๋ค. #2-2์ ์๋ runBlocking { ... } ์์ ๋ค์ด์๋ ๊ฒ์ Cold Flow์ collect()๋ผ์ ์ธ์ ๊ฐ ๋๋๋ฉฐ, ๋ฐ๋ผ์ runBlocking { ... } ๋ธ๋ก์ ํ์ถํ ๊ฒ์ ๊ธฐ๋ํ ์ ์๋ค. ํ์ง๋ง ๊ทธ ์์ Cold Flow๊ฐ ์๋ Hot Flow์ collect()๊ฐ ๋ค์ด์๊ฒ ๋๋ค๋ฉด, ๊ทธ runBlocking { ... }์ ์์ํ ๋๋์ง ์๋๋ค.
์ค์ ๋ก runBlocking { ... } ๋ธ๋ก์ Hot Flow์ collect()๋ฅผ ๋ฃ์ผ๋ฉด ๊ทธ runBlocking { ... } ๋ธ๋ก ๋ค์ ์๋ ๋ชจ๋ ์ฝ๋์ "Unreachable code"๋ผ๋ ๊ฒฝ๊ณ ๋ฉ์์ง๊ฐ ๋ฌ๋ค.
#3 ์์ฝ
Cold Flow๋ VOD์ ๊ฐ๊ณ , Hot Flow๋ ์ํ๊ด๊ณผ ๊ฐ๋ค.
'๊นจ์ ๊ฐ๋ ๐ > ๊ธฐํ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ผ๋ก ๋จธ์คํฌ์ ์ฌ๊ณ ๋ฒ๊ณผ ์ถฉ๊ณ (0) | 2025.01.24 |
---|---|
Unit Testing - Test double (0) | 2024.06.30 |
์์กด์ฑ ์ฃผ์ (Dependency Injection) (0) | 2024.06.20 |
REST API (REpresentational State Transfer Application Programming Interface) (0) | 2024.05.26 |
API (Application Programming Interface) (0) | 2023.12.18 |