ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Hot Observable vs Cold Observable
    RxSwift 2025. 2. 11. 10:24

    안녕하세요. 그린입니다 🍏

    이번 포스팅에서는 Rx에서의 Hot/Cold Observable에 대해 정리해보겠습니다 🙋🏻

     

    먼저, RxSwift에서 Observabled은 데이터 스트림을 다루는 핵심적인 요소이며, Observable의 성격에 따라서 Hot / Cold 종류로 나눠지게 됩니다.

    이 두가지는 구독 시점과 데이터 생성 방식에 따라 차이를 가지고 있어요 🤔

     

    그래서, 이번 포스팅에서는 Hot / Cold Observable의 개념부터 내부 동작 방식과 성능적인 부분 그리고 예시까지 한번 담아보겠습니다!

     


    Hot Observable

    Hot Observable은 구독자가 추가되더라도 기존 스트림을 공유하는 Observable입니다.

    즉, 데이터 생성이 특정 이벤트나 외부 트리거에 의해 시작되며, 구독자가 중간에 추가되더라도 기존 스트림의 현재 상태를 공유해줘요.

     

    특징으론 다음과 같아요.

     

    1️⃣ 이미 실행중인 데이터 스트림을 여러 구독자가 공유

    2️⃣ 새로운 구독자는 중간 데이터를 받음

    3️⃣ 실시간 이벤트 스트림, UI 이벤트, WebSocket, NotificationCenter 등의 경우에 사용

    4️⃣ 구독자가 없어도 데이터 방출 가능

     

    예시를 살짝 볼까요?

     

    let subject = PublishSubject<Int>()
    
    subject.subscribe(onNext: { print("Observer 1: \($0) ") })
    
    subject.onNext(1)
    subject.onNext(2)
    
    subject.subscribe(onNext: { print("Observer 2: \($0) ") })
    
    subject.onNext(3)
    
    // Observer 1: 1
    // Observer 1: 2
    // Observer 1: 3
    // Observer 2: 3

     

    여기서 Observer 2는 onNext(3) 이벤트 이후 구독했기에 1, 2 데이터는 받지 못하고 3부터 수신한걸 볼 수 있습니다.

     

    Hot Observable은 주로, Subject 타입들이 해당됩니다.

    다시 정리해보지만, 실시간 데이터 스트림을 처리하거나 구독자가 언제든지 데이터를 받도록 할 수 있는 특성을 제공해줘요.

    특히, UI 이벤트 처리에 많은 쓰임을 볼 수 있어요.

     

    이제 Cold Observable에 대해 알아볼께요.

     


    Cold Observable

    Cold Observable은 구독자가 생길 때마다 새로운 데이터 스트림을 생성하는 Observable입니다.

    즉, 각 구독자는 독립적인 데이터 스트림을 받으며, Observable을 구독할 때 데이터 생성이 시작됩니다.

     

    특징으로는 다음과 같아요.

     

    1️⃣ 구독이 발생할 때마다 새로운 데이터 스트림 생성

    2️⃣ 각 구독자는 독립적으로 데이터를 수신

    3️⃣ 일반적으로 API 호출, 파일 읽기, 데이터베이스 쿼리 등의 경우에 사용

    4️⃣ 데이터 재사용이 어려움

     

    Cold Observable의 종류로는 일반적인 Just/From/Interval/Timer/Deffered Observable 등이 모두 해당됩니다.

    기본적으로 알고 있는 Observable들은 거의 모두 Cold Observable라 볼 수 있어요.

     

    예시를 한번 볼까요?

     

    let observable = Observable<Int>.create { observer in
        print("데이터 생성 시작")
        observer.onNext(1)
        observer.onNext(2)
        observer.onNext(3)
        observer.onCompleted()
        return Disposables.create()
    }
    
    observable.subscribe(onNext: { print("Observer 1: \($0)") })
    observable.subscribe(onNext: { print("Observer 2: \($0)")
    
    // 데이터 생성 시작
    // Observer 1: 1
    // Observer 1: 2
    // Observer 1: 3
    // 데이터 생성 시작
    // Observer 2: 1
    // Observer 2: 2
    // Observer 2: 3

     

    보시는것처럼 구독자 두개가 생성되었고, 각각 독립적인 데이터 스트림을 받았기에 "데이터 생성 시작"이 두 번 출력된걸 볼 수 있습니다.

     

    여기서 곁다리로 하나 간략히 개념을 정리해볼께요.

     


    Unicast vs Multicast

    우선 Unicast는 Cold Observable처럼 각 구독자가 독립적인 데이터를 받는걸 뜻합니다.

    반대로, Multicast는 Hot Observable처럼 여러 구독자가 동일한 스트림을 공유합니다.

     

    그럼 이제 더 나아가서 Cold Observable을 Hot Observable로 변환하는 방법에 대해 알아볼까 합니다 😁

     


    Cold Observable ➡️ Hot Observable

    Cold Observable을 Hot Observable로 변환하기 위해서는 아래 연산자들을 적절히 사용하면 됩니다.

     

    1️⃣ publish()

    Cold Observable을 Hot Observable로 변환해주는 연산자로 connect()를 호출해야 Observable이 시작됩니다.

    그렇게 되면, 여러 구독자가 동일한 스트림을 공유할 수 있게 되죠.

     

    let hotObservable = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).take(5).publish()
    
    hotObservable.subscribe(onNext: { print("Observer 1: \($0)") })
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        hotObservable.subscribe(onNext: { print("Observer 2: \($0)") })
    }
    
    hotObservable.connect()
    
    // Observer 1: 0
    // Observer 1: 1
    // Observer 1: 2
    // Observer 1: 3
    // Observer 1: 4
    // Observer 2: 3
    // Observer 2: 4

     

    꼭 connect()를 해야 연결되어 멀티캐스트가 됩니다.

     

    2️⃣ share()

    publish().refCount()의 단축 표현으로, 구독자가 있을 때만 데이터 스트림을 유지해줍니다.

    차이는 connect() 없이도 자동으로 연결됩니다.

     

    let sharedObservable = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).take(5).share()
    
    sharedObservable.subscribe(onNext: { print("Observer 1: \($0)") })
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        sharedObservable.subscribe(onNext: { print("Observer 2: \($0)") })
    }
    
    // Observer 1: 0
    // Observer 1: 1
    // Observer 1: 2
    // Observer 1: 3
    // Observer 1: 4
    // Observer 2: 2
    // Observer 2: 3
    // Observer 2: 4

     

    connect가 없으니 이미 실행중인 스트림도 구독 시점에 바로 받죠.

    위 publish와 차이를 볼 수 있습니다.

     

    3️⃣ replay()

    publish()와 비슷하지만, 지정된 개수만큼의 값을 캐싱해 새 구독자에게 제공합니다.

    캐싱된 값을 새 구독자가 받을 수 있도록 보장해줘요.

     

    let replayedObservable = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).take(5).replay(2)
    replayedObservable.connect()
    
    replayedObservable.subscribe(onNext: { print("Observer 1: \($0)") })
    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
        replayedObservable.subscribe(onNext: { print("Observer 2: \($0)") })
    }
    
    // Observer 1: 0
    // Observer 1: 1
    // Observer 1: 2
    // Observer 1: 3
    // Observer 1: 4
    // Observer 2: 3
    // Observer 2: 4

     

    보시는것처럼, 3초후 두번째 구독이 시작되죠.

    이때 마지막 2개의 값을 캐싱하기에 3, 4를 받는걸 볼 수 있어요.

     

    그럼 반대로 Hot Observable을 Cold Observable로 바꾸는것도 있을까요?

     


    Hot Observable ➡️ Cold Observable

    앞서 봤던 Cold -> Hot 처럼 바로 적용하는 연산자는 없지만, 변환하는 방법 자체는 있습니다.

    바로, deferred를 사용해 각 구독자마다 새 Observable 시퀀스를 생성하는 방법이에요.

     

    // Hot Observable (예: Subject)
    let subject = PublishSubject<Int>()
    
    // Cold Observable로 변환
    let cold = Observable.deferred { subject.asObservable() }
    
    // 각 구독자는 독립적인 스트림을 받게 됨
    cold.subscribe(onNext: { print("First subscriber: \($0)") })
    cold.subscribe(onNext: { print("Second subscriber: \($0)") })

     

    이렇게 Hot Observable을 새 Cold Observable로 변환할 수는 있습니다.

    그럼 이제 각 구독자는 자신만의 독립적인 스트림을 가지게 됩니다.

     

    Hot과 Cold의 특성 및 쓰임 차이는 RxSwift 깃헙 공식 도큐먼트에도 간결히 나타나 있습니다.

     

     


    마무리

    Rx 세상에서 Hot Observable과 Cold Observable의 차이를 이해하는것은 메모리 및 성능과 비동기 데이터 흐름을 설계하는데 필수적입니다.

    Hot Observable은 실시간 데이터 처리에 적합하고, Cold Observable은 독립적인 데이터 흐름이 필요한 경우에 적절하죠.

     

    이 특성들을 이해하고 적절히 활용하면 Rx 기반의 코드 안정성과 효율성을 극대화 할 수 있습니다 ☺️

     


    레퍼런스

     

    RxSwift/Documentation/HotAndColdObservables.md at main · ReactiveX/RxSwift

    Reactive Programming in Swift. Contribute to ReactiveX/RxSwift development by creating an account on GitHub.

    github.com

    'RxSwift' 카테고리의 다른 글

    RxSwift - subscribe vs bind vs drive  (1) 2025.02.14
    RxSwift - Driver & BehaviorRelay  (0) 2025.02.06
    RxSwift - BehaviorSubject vs BehaviorRelay  (0) 2025.02.03
    RxSwift로 서버 통신하기  (5) 2022.12.19
    RxSwift - Time Based Operator  (0) 2021.11.17
Designed by Tistory.