본문 바로가기
Android/Coroutine

[Android/Coroutine] Cold Stream vs Hot Stream 쉽게 이해하기: 언제 어떤 것을 써야 할까?

by quessr 2025. 3. 28.

 

코루틴을 사용하다 보면 Flow, StateFlow, SharedFlow와 같은 다양한 스트림 타입을 접하게 됩니다.
이때 함께 따라오는 개념이 바로 Cold Stream과 Hot Stream입니다.

처음에는 문서에 나와 있는 특징들을 정리하며 각각을 구분해보려 했지만,
막상 실제 코드에 적용해 보려 하니 어떤 상황에서 무엇을 선택해야 할지 명확하게 감이 오지 않았습니다.

그래서 이 개념들을 좀 더 쉽게, 감각적으로 이해할 수 있는 방법이 없을까 고민하게 되었고,
그 과정에서 일상에서 흔히 겪는 상황들을 비유로 떠올려 보며 Cold Stream과 Hot Stream의 차이를 정리해 보았습니다.


Cold Stream이란?

Cold Stream은 collect가 시작되어야 비로소 동작하는 스트림입니다. 대표적으로 Kotlin의 Flow가 여기에 해당합니다.

  • collect()를 호출하는 시점에 흐름이 시작됩니다.
  • emit된 값은 오직 그 순간 collect 중인 구독자에게만 전달됩니다.
  • 구독자가 없으면 흐름은 실행되지 않습니다.
  • 구성 변경 등으로 collect가 끊기면 emit된 값은 전달되지 않고 사라집니다.

Hot Stream이란?

Hot Stream은 collect 여부와 관계없이 이미 동작 중인 스트림입니다. 대표적으로 StateFlow와 SharedFlow가 여기에 해당합니다.

  • 값은 스트림 내부에서 항상 존재하거나, emit된 후 흘러갑니다.
  • 구독자가 새로 들어와도 최근 값을 즉시 받을 수 있습니다.
  • 구성 변경 이후에도 ViewModel이 살아 있다면 이전 상태나 이벤트를 다시 전달받을 수 있습니다.
  • 여러 구독자에게 동시에 값을 공유할 수 있습니다.

카드 수령 vs 택배 보관함: 일상적인 비유

Cold Stream과 Hot Stream의 차이는 카드 수령택배 수령처럼
일상에서 자주 접할 수 있는 상황으로 비유하면 한결 이해가 쉬워집니다.

상황 스트림 개념 설명
카드 직접 수령 Cold Stream (Flow) 택배 기사가 왔을 때 내가 집에 있어야 직접 받을 수 있습니다. 그 순간 자리에 없으면 반송되고, 다시 받으려면 새로 신청해야 합니다.
무인 택배 보관함 Hot Stream (StateFlow, SharedFlow) 택배가 보관함에 들어가면 내가 집에 없더라도 나중에 언제든 꺼내 쓸 수 있습니다. 값은 보존되어 있으며 구성 변경 이후에도 수령이 가능합니다.

구성 변경과 생명주기

UI에서 Cold Stream을 사용할 때 자주 문제가 되는 상황 중 하나는 구성 변경입니다.
예를 들어 사용자가 버튼을 눌렀는데 그 순간 화면을 회전하면, Fragment는 파괴되고 다시 생성됩니다.
이 과정에서 collect가 중단되면, emit된 값은 사라지고 다시 collect하더라도 이전 값을 받을 수 없습니다.

반면 Hot Stream은 ViewModel이 유지되기 때문에, 구성 변경 이후에도 상태가 보존되고 값을 다시 받을 수 있습니다.
값이 살아 있는 흐름이라는 점에서 UI와의 궁합이 훨씬 좋습니다.


Cold Stream은 왜 필요한가?

실시간처럼 동작하는 Hot Stream이 있는데, 굳이 Cold Stream을 써야 하는 이유는 무엇일까요?

핵심 차이는 다음과 같습니다.

  • Cold Stream은 collect하는 시점에 새로 흐름을 시작하고, 값을 직접 가져옵니다.
  • Hot Stream은 이미 emit된 값을 들고 있으며, 누군가가 값을 업데이트해줘야만 최신 상태로 유지됩니다.

즉, Cold Stream은 수신자가 요청해서 값을 가져오는 구조(pull),
Hot Stream은 발신자가 값을 만들어 흘려주는 구조(push)입니다.


블로그 조회수: 관리자 페이지 vs 메인 화면

이 개념을 고민하면서 떠올린 저의 경험 중 하나는 블로그 조회수 확인입니다.

메인 페이지에서 글의 조회수를 보면 겉보기엔 실시간으로 반영되는 것처럼 보이지만,
실제로는 자동으로 일정 주기마다 업데이트되기 때문에 제가 확인하고 싶은 시점과는 약간 차이가 날 때가 종종 있습니다.

그래서 저는 정확한 수치를 보고 싶을 때면 관리자 페이지로 이동해서 새로고침을 눌러 직접 확인하곤 했습니다.

이 두 가지 방식의 차이는 Cold Stream과 Hot Stream의 구조와 흡사해서 이해하기 좋은 예시라고 생각했습니다.

상황 스트림 개념 설명
관리자 페이지에서 수동 새로고침으로 조회수 확인 Cold Stream 지금 시점의 정확한 조회수를 직접 요청해서 받아옵니다. 요청하는 순간에 최신 데이터를 가져오는 구조입니다.
메인 페이지에서 자동 반영되는 조회수 Hot Stream 조회수는 일정 주기마다 자동으로 갱신됩니다. 실시간처럼 보이지만 정확히 지금 시점의 값은 아닐 수 있습니다.

Git GUI vs 수동 Fetch

개발자에게 익숙한 상황으로는 Git에서 원격 브랜치 상태를 확인할 때가 있습니다.

상황 스트림 개념 설명
Git GUI에서 자동 동기화된 브랜치 상태 Hot Stream GUI는 원격 저장소의 상태를 주기적으로 반영합니다. 하지만 Push 직후에는 최신 상태가 바로 반영되지 않아, 실제와 시간차가 날 수 있습니다.
수동 Fetch 버튼 클릭 Cold Stream 사용자가 Fetch 버튼을 눌러 현재 시점의 원격 저장소 상태를 강제로 가져옵니다. 이때 항상 최신 값을 보장받을 수 있습니다.

두 방식 모두 최신 값을 받을 수는 있지만,
Cold Stream은 지금 이 순간을 기준으로 값을 직접 요청하고,
Hot Stream은 언제 emit되었는지를 신뢰해야만 최신 값을 받을 수 있습니다.


언제 어떤 스트림을 써야 할까?

상황 추천 스트림 이유
서버/DB에서 한 번만 요청하고 결과를 받는 경우 Flow collect 시점마다 흐름이 새로 시작되므로, 매번 최신 값을 직접 요청 가능
화면 상태를 계속 유지하고 싶을 때 StateFlow 가장 최근 상태를 항상 유지하며, 구성 변경 후에도 바로 반영 가능
버튼 클릭, Toast 메시지 등 일회성 이벤트 처리 SharedFlow 이벤트를 여러 구독자에게 동시에 전달 가능하며, 값을 유지할 필요 없음

정리

구분 Flow StateFlow SharedFlow
생명주기 안전성 낮음 (collect 중단 시 손실) 높음 높음
초기값 필요 여부 필요 없음 필요함 필요 없음
값 유지 유지하지 않음 항상 최신 값을 유지 replay 개수만큼 유지 가능
구독자 수 1:1 (개별 흐름) 여러 구독자 공유 여러 구독자 공유
주도권 수신자 (collect) 발신자 (emit) 발신자 (emit)
적합한 사용처 1회성 요청, 서버 호출 UI 상태 관리 UI 이벤트, 알림 등

마무리

처음엔 Hot Stream이 더 좋아 보였지만,
실제로 어떤 스트림을 써야 할지는
상황에 따라 아래와 같은 질문들을 하나씩 떠올리며 판단하면 좋을 것 같다는 생각이 들었습니다.

  • 값이 항상 유지되어야 하는가?
  • 값이 바뀔 때마다 push해야 하는가, 필요할 때 pull하면 되는가?
  • 여러 구독자가 동시에 값을 공유해야 하는가?

이런 고민을 해보면서 Cold Stream과 Hot Stream의 쓰임을 비교해볼 수 있었고,

이 글이 저처럼 처음 개념이 모호하게 느껴졌던 분들에게 조금이나마 이해에 도움이 되면 좋겠습니다.

반응형