[Project Reactor] 7. Cold와 Hot: Reactor 스트림의 온도 개념 이해하기

y001·2025년 5월 1일

Reactive Programming

목록 보기
7/30
post-thumbnail

리액티브 프로그래밍에서 흔히 마주치는 개념 중 하나가 바로 ColdHot이다. 이 용어는 데이터 스트림의 생성 방식과 구독 시점에 따른 처리 차이를 설명하는데 사용된다. 단어 자체는 어렵지 않지만, 실제 동작 방식은 충분히 실험해보지 않으면 오해하기 쉽다. 이 글에서는 Cold와 Hot의 의미를 익숙한 IT 용어와 함께 살펴보고, Reactor에서 어떤 방식으로 적용되는지를 실습 코드와 함께 자세히 설명한다.


1. 'Hot'이라는 용어가 의미하는 것

IT 세계에서 'Hot'이라는 표현은 이미 실행 중이거나, 재시작 없이 즉시 작동 가능한 상태를 의미한다.

  • Hot Swap: 시스템을 끄지 않고 장비를 교체
  • Hot Deploy: 서버 재시작 없이 코드 변경 반영
  • Hot Wallet: 인터넷 연결 상태에서 바로 사용할 수 있는 지갑

핵심 요약: 시스템을 다시 시작하지 않고, 현재 상태 그대로 이어서 작동한다는 개념


2. 'Cold'는 새롭게 다시 시작되는 흐름

반면 'Cold'는 매번 처음부터 다시 시작하는 흐름이다.

  • Cold Wallet: 오프라인 상태에서 안전하게 보관되며, 사용할 때 연결을 새로 설정해야 함

핵심 요약: 매번 새로 시작하며, 같은 작업을 반복하게 됨


3. Reactor에서의 Cold Sequence

Reactor에서 Flux, Mono는 기본적으로 Cold Sequence이다. 구독이 일어날 때마다 Publisher는 데이터 생성을 처음부터 시작한다.

Flux<String> coldFlux = Flux.fromIterable(Arrays.asList("A", "B", "C"))
                             .map(String::toLowerCase);

coldFlux.subscribe(it -> log.info("# subscribe1 : {}", it));
Thread.sleep(2000L);
coldFlux.subscribe(it -> log.info("# subscribe2 : {}", it));

출력 결과:

# subscribe1 : a
# subscribe1 : b
# subscribe1 : c
# subscribe2 : a
# subscribe2 : b
# subscribe2 : c

→ 두 번째 구독에서도 데이터가 처음부터 다시 emit됨을 확인할 수 있다.


4. Reactor에서의 Hot Sequence

Hot Sequence는 이미 실행 중인 Publisher를 여러 Subscriber가 공유하는 방식이다. 한 번 emit된 데이터는 다시 emit되지 않으며, 구독자는 실시간으로 흘러가는 데이터에 중간에 합류한다.

Flux<String> hotFlux = Flux.fromArray(new String[]{"A", "B", "C"})
                           .delayElements(Duration.ofSeconds(1))
                           .share(); // Hot 변환

hotFlux.subscribe(it -> log.info("# subscribe1 : {}", it));
Thread.sleep(2000L);
hotFlux.subscribe(it -> log.info("# subscribe2 : {}", it));

출력 예시:

# subscribe1 : A
# subscribe1 : B
# subscribe1 : C
# subscribe2 : C

subscribe2A, B가 emit된 이후에 구독했기 때문에 C만 받음
.share() 연산자를 통해 Flux가 Hot으로 전환되어 스트림이 공유됨

이러한 Hot 특성은 다음과 같은 경우에 적합하다:

  • 실시간 알림 시스템
  • 센서 데이터 스트리밍
  • 채팅 메시지 수신
  • 과거 데이터가 아닌, 현재 시점 이후의 데이터만 처리할 때

5. HTTP 요청 예제로 보는 Cold vs Hot

📌 Cold: 매번 새롭게 요청

Mono<String> mono = WebClient.create()
                             .get()
                             .uri(url)
                             .retrieve()
                             .bodyToMono(String.class)
                             .map(response -> {
                                 DocumentContext jsonContext = JsonPath.parse(response);
                                 return jsonContext.read("$.datetime");
                             });

mono.subscribe(response -> log.info("#1 datetime1 : {}", response));
Thread.sleep(2000);
mono.subscribe(response -> log.info("#2 datetime2 : {}", response));

mono는 Cold Sequence이기 때문에 구독할 때마다 HTTP 요청이 발생한다.
→ 동일한 요청을 반복하게 되며, 응답 시점에 따라 결과가 달라질 수도 있다.


📌 Hot: 캐시를 통해 응답 공유

Mono<String> mono = WebClient.create()
                             .get()
                             .uri(url)
                             .retrieve()
                             .bodyToMono(String.class)
                             .map(response -> {
                                 DocumentContext jsonContext = JsonPath.parse(response);
                                 return jsonContext.read("$.datetime");
                             })
                             .cache(); // Hot으로 전환

mono.subscribe(response -> log.info("#1 datetime1 : {}", response));
Thread.sleep(2000);
mono.subscribe(response -> log.info("#2 datetime2 : {}", response));

→ 이 경우 WebClient는 한 번만 요청하고, 응답을 캐시하여 이후 구독자에게 재사용한다.
→ HTTP 요청의 중복을 방지하고, 동일한 결과를 공유할 수 있어 토큰 인증 등에서 매우 유용


6. 정리

구분Cold SequenceHot Sequence
기본 동작구독마다 새로 시작스트림 공유, 중간 합류
HTTP 요청구독마다 재요청최초 요청 결과 재사용 가능
연산자 예시기본 상태, Mono, Flux.share(), .cache()
적합한 사용정확한 재처리, 각자 독립적 요청실시간 알림, 중복 방지 캐시, 스트리밍

Cold와 Hot의 차이는 단순한 성능 문제가 아니라, 스트림의 의미를 어떻게 해석할 것인가에 대한 설계 철학과도 맞닿아 있다. 실시간인지, 이력 기반인지, 결과를 공유할 필요가 있는지 등을 고려하여 상황에 맞는 전략을 선택하는 것이 중요하다.

0개의 댓글