RxJava, Kotlin Flow, LiveData, RxSwift, Combine 등 Reactive 프로그래밍에서는 "데이터 흐름"이 핵심입니다.
하지만 이 흐름들이 항상 같은 방식으로 작동하는 건 아니에요.
Reactive에서 가장 중요한 개념 중 하나가 바로 Cold Stream과 Hot Stream입니다.
Cold Stream은 "데이터 생성"과 "데이터 소비"가 완전히 연결되어 있는 스트림입니다.
즉, 누군가 구독(Subscribe or Collect)을 해야만 흐름이 시작됩니다.
이런 스트림은 각 구독자마다 독립적인 실행을 하기 때문에,
같은 스트림을 여러 번 구독하면 매번 새롭게 실행됩니다.
대표적인 예시:
RxJava: Observable, Single, Maybe
Kotlin: Flow, suspend fun
“콜드 스트림은 전자레인지다. 내가 버튼을 눌러야 요리가 시작된다.”
→ 내가 작동시켜야 흐름이 시작됨
Hot Stream은 데이터가 생성되기 시작한 시점과 무관하게 언제든지 소비될 수 있는 스트림입니다.
즉, 데이터는 이미 흐르고 있으며, 구독자는 그 흐름에 "탑승"하는 것이에요.
이 스트림은 여러 구독자와 동일한 데이터 흐름을 공유하고,
어떤 구독자는 이미 지나간 데이터는 놓칠 수도 있습니다.
대표적인 예시:
RxJava: Subject, ConnectableObservable
Kotlin: StateFlow, SharedFlow
“핫 스트림은 "버스". 이미 운행 중이고, 중간 정류장에서 타면 앞 정류장은 놓친다.”
→ 스트림은 계속 흐르고 있고, 구독 시점은 ‘정류장’일 뿐
✅ 예시코드
suspend fun main() {
val coldFlow = flow {
println("Flow started")
emit("Hello")
}
coldFlow.collect { println("Collector 1: $it") }
coldFlow.collect { println("Collector 2: $it") }
}
✅ 결과

coldFlow.collect를 두 번 호출했더니, flow 안의 로직도 두 번 실행되었죠.
즉, 매 구독마다 흐름이 새롭게 시작되며, 이전 실행과는 완전히 독립적입니다.
StateFlow
초기 값을 반드시 지정해야 합니다.
항상 최신 값을 저장하고, 구독자가 collect를 시작하면 즉시 현재 값을 전달합니다.
ViewModel의 상태 관리에 매우 적합합니다.
✅ 예시코드
suspend fun main() {
val state = MutableStateFlow("Initial")
state.value = "Update"
state.collect { println("Collected: $it") }
}
✅ 결과

SharedFlow
suspend fun main() {
val shared = MutableSharedFlow<String>()
// 구독자 없을 때 emit한 값은 전달되지 않음
shared.emit("Old")
shared.collect { println("Collected: $it") }
shared.emit("New")
// 출력: Collected: New
}
Kotlin에서 Flow를 여러 구독자와 공유하고 싶다면 stateIn 또는 shareIn을 사용해서 Hot으로 전환할 수 있어요.
👀 변환 흐름도

✅ 예시코드
fun main() = runBlocking {
println("=== Start ===\n")
val scope = this // runBlocking의 CoroutineScope를 scope로 사용
launchColdFlowExample()
println("\n------------------------\n")
launchStateFlowExample(scope)
println("\n------------------------\n")
launchSharedFlowExample(scope)
println("\n=== End ===")
}
/**
* flow { ... } 블록은 collect()가 호출되기 전까지는 아무 일도 일어나지 않는다.
* collect()를 호출하는 순간 비로소 내부 로직이 실행되고, emit()된 값이 전달된다.
* collect()를 두 번 호출하기 때문에, "Cold Flow started"가 두 번 출력된다.
* */
// ❄️ Cold Flow
suspend fun launchColdFlowExample() {
println("🧊 Cold Flow Example")
val coldFlow = flow {
println("Cold Flow started")
emit("Value from Cold Flow")
}
coldFlow.collect { println("Cold Collector #1: $it") }
coldFlow.collect { println("Cold Collector #2: $it") }
}
/**
* Flow를 stateIn()을 통해 Hot Stream인 StateFlow로 변환하는 과정을 보여주는 예제.
* stateIn()은 Flow의 흐름을 내부적으로 collect하여 최신 값을 보관하는 StateFlow로 전환함.
* started = SharingStarted.Eagerly 설정 때문에, stateFlow는 collect()가 호출되지 않아도 즉시 실행을 시작.
* 따라서 delay(1000) 이후 collect()를 호출했음에도, "Value from Cold Flow (for StateFlow)"가 미리 생성되어 즉시 전달.
* */
// 🔥 StateFlow 변환
suspend fun launchStateFlowExample(scope: CoroutineScope) {
println("🔥 StateFlow Example")
val coldFlow = flow {
println("Generating data for StateFlow")
delay(500)
emit("Value from Cold Flow (for StateFlow)")
}
val stateFlow = coldFlow.stateIn(
scope = scope,
started = SharingStarted.Eagerly,
initialValue = "Initial State"
)
delay(1000) // emit 완료까지 대기
stateFlow.take(1).collect {
println("StateFlow Collector: $it")
}
}
/**
* Flow를 shareIn()을 통해 Hot Stream인 SharedFlow로 전환하는 과정.
* shareIn()은 Flow를 내부적으로 collect하여, 이벤트를 여러 구독자에게 broadcast할 수 있도록 변환.
* started = SharingStarted.Eagerly 덕분에 sharedFlow는 scope가 시작되자마자 실행되며 값을 emit.
* replay = 1 설정을 통해 최근 1개의 값을 보관하므로, 구독자가 뒤늦게 collect하더라도 가장 최근 값을 받을 수 있게됨.
* */
// 🔥 SharedFlow 변환
suspend fun launchSharedFlowExample(scope: CoroutineScope) {
println("🔥 SharedFlow Example")
val coldFlow = flow {
println("Generating data for SharedFlow")
delay(500)
emit("Value from Cold Flow (for SharedFlow)")
}
val sharedFlow = coldFlow.shareIn(
scope = scope,
started = SharingStarted.Eagerly,
replay = 1
)
delay(1000) // emit 완료 후 collect 시작
sharedFlow.take(1).collect {
println("SharedFlow Collector: $it")
}
}
✅ 결과

정리
면접 때 만약 차이점을 물어본다면 저는 아래와 같이 답변할 것 입니다.
Cold Stream은 내가 요청할 때 비로소 데이터를 주는 스트림입니다.
매번 요청하면 새로운 흐름이 생성되고, 다른 사람과 공유하지 않습니다.
Hot Stream은 이미 누군가가 실행시킨 흐름을 나도 함께 구독하는 구조입니다.
흐름은 중단되지 않고 계속되며, 구독자들은 실시간 또는 최근의 일부 값만 볼 수 있습니다.
Cold, Hot Stream은 단순한 개념 같지만, 실제 코드와 연결되면 많은 실수의 원인이 됩니다.
이 개념을 정확히 이해하면 효율적인 데이터 흐름, 리소스 절약, 의도한 UI 상태 처리가 가능해져요.
안드로이드에서 비동기 흐름을 다룰 때, "지금 내가 만들고 있는 흐름은 Cold일까, Hot일까?"
이 질문 하나로 앱의 동작이 훨씬 명확해질 수 있습니다.