RxSwift Subject

donotinto·2024년 4월 2일

기본적으로 RxSwift는 Observable을 생성해서 이벤트를 방출하면서 시작됩니다.
이 Observable에는 여러 종류가 있는데요, 그 중 하나가 Subject입니다.

그렇지만 기본적인 Observable과 Subject는 차이점이 존재합니다.


Observable? Subject?

Observable은 이벤트를 방출(emit)하고 Observable을 구독하고 있는 Observer가 이벤트에 대한 처리를 실행합니다.

이렇듯, Observable과 Observer에 역할을 명확하게 구분되어 있습니다.

이전 개념편에서 Observable은 이벤트를 3가지 종류로 방출하여 다음 세 메소드를 실행시키는 것 처럼 이해를 했는데요.

정확하게는 Observable은 이벤트를 방출할 뿐, 실제 onNext, onCompleted, onError는 Observer에서 실행되며 Observable을 Subscribe하여 이벤트를 전달받아 처리하는 것입니다.

이를 Observable에서 실행되는 것으로 착각한 이유는 Observable을 생성해서 Subscribe을 할 때, Observer 생성하고 있었는데 그걸 몰랐던 것이었습니다...뭐이런...


// Observable
public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
        rxAbstractMethod()
}
    
// ObservableType
public static func create(_ subscribe: @escaping (AnyObserver<Element>) -> Disposable) -> Observable<Element> {
        AnonymousObservable(subscribe)
}

// AnyObserver
public struct AnyObserver<Element> : ObserverType {
    /// Anonymous event handler type.
    public typealias EventHandler = (Event<Element>) -> Void

    private let observer: EventHandler
}

아무튼 다시 정리!!


Observable은 이벤트를 방출!
Observer는 Observable를 구독하여, 방출된 이벤트를 받고 처리!

Subject

그래서 Subject는 뭘까...

Subject는 Observable과 Observer의 역할을 모두 할 수 있습니다!!

Observable과 Observer

  • Observable은 다른 Observable이 방출한 이벤트를 받아올 수 없다.
  • Observer는 이벤트를 방출하지 못한다.

대충 융합 짤...

Subject
Subject는 다른 Observable이 방출한 이벤트를 받아올 수 있고(Observer), 이를 구독자(다른 Observer겠죠?)한테 방출할 수도 있다(Observable)!

let observable = Observable.just(10)
let subject = BehaviorSubject(value: 30)
let disposeBag = DisposeBag()

observable.bind { value in
	
    subject.onNext(value)
}.disposed(by: disposeBag)

subject.bind { value in
	print(value)
}

// 10

Subject 종류

Subject는 총 4가지의 종류가 있습니다

  • PublishSubject
  • BehaviorSubject
  • ReplaySubject
  • AsyncSubject

PublishSubject

PublishSubject는 초기값이 없는 상태입니다.

그래서 PublishSubject는 구독을 해도 아무런 이벤트가 전달되지 않습니다.

또 한가지 특징이라면, 구독 이전에 전달받은 Event 역시 방출하지 않기 때문에, PublishSubject는 구독 이 후 전달받은 Event에 대해서만 처리합니다.

let publishSubject = PublishSubject<Int>()
        
publishSubject.onNext(10)
        
publishSubject.subscribe { event in
	print(event.element!)
}.disposed(by: DisposeBag())
        
publishSubject.onNext(20)

// 20

BehaviorSubject

BehaviorSubject는 초기값을 설정할 수 있습니다.

그래서 BehaviorSubject는 구독을 하면 생성 시 저장된 이벤트와 값이 전달됩니다.

BehaviorSubject는 Publish와 다르게 최신 이벤트를 저장해두었다가 구독 시 해당 이벤트를 방출합니다.

let behaviorSubject = BehaviorSubject<Int>(value: 10)
let disposeBag = DisposeBag()
        
behaviorSubject.onNext(15)
        
behaviorSubject.subscribe { event in
    print(event.element!)
}.disposed(by: disposeBag)
        
behaviorSubject.onNext(25)

// 15
// 25

ReplaySubject

ReplaySubject는 하나 이상의 이벤트를 버퍼에 저장해두고, 구독 시 버퍼에 저장된 모든 이벤트를 방출합니다

let replaySubject = ReplaySubject<Int>.create(bufferSize: 3)
let disposeBag = DisposeBag()
        
replaySubject.onNext(0)
replaySubject.onNext(5)
replaySubject.onNext(10)
replaySubject.onNext(15)
        
replaySubject.subscribe { event in
    print(event.element!)
}.disposed(by: disposeBag)
        
replaySubject.onNext(25)
        
replaySubject.subscribe { event in
    print(event.element!)
}.disposed(by: disposeBag)

// 5  10  15   << 첫 구독 시 저장된 3개 이벤트
// 25   << 새로운 이벤트 방출 및 저장
// 10  15  25   << 새로운 구독 시 마지막으로 저장된 3개 이벤트

AsyncSubject

asyncSubject도 초기값이 없지만, 구독 이전 상관없이 최신 이벤트를 저장하고 방출합니다.
다만! 구독 시 바로 방출하던 다른 Subject들과는 다르게 onCompleted가 되었을 때 가장 최신 이벤트를 방출합니다.

let asyncSubject = AsyncSubject<Int>()
let disposeBag = DisposeBag()
        
asyncSubject.onNext(5)
        
asyncSubject.subscribe { event in
    print(event)
}.disposed(by: disposeBag)
        
asyncSubject.onCompleted()

저도 블로그를 작성하면서 직접 테스트해보고 작성중인데, 이걸 어떻게 활용할지는 아직은 잘 모르겠습니다 ㅎㅎ...

직접 이것저것 구현해보면서 체득해보는 수 밖에 없을 것 같아요 흐흐..벌써 신난다 ㅎ

다음 포스팅은 Relay에 대해 정리해보려 합니다!

0개의 댓글