티스토리 뷰

iOS/RxSwift

RxSwift - Subject

포도 동 2022. 3. 4. 17:10

이번엔 Subject에 대해 알아보겠습니다.

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

즉, 우리는 Subject를 통해서 event를 받을 수도 있고 event를 전달할 수도 있습니다.

 

RxSwift에는 4가지의 Subject 유형이 있습니다.

- PublishSubject : 비어있는 상태로 생성되고 그 후 전달되는 event들을 모두 구독자에게 전달

- BehaviorSubject : 초기 값을 가지고 생성되며, 그 후 전달되는 event가 있으면 그중 마지막 event를 (없으면 초기 값)을 구독자에게 전달

- ReplaySubject : 생성될 때 버퍼의 사이즈를 지정함. 버퍼 사이즈만큼의 최신 event 들을 저장하고 구독자에게 이를 전달

- AsyncSubject : Subject로 Error 또는 Completed 이벤트가 전달되었을 때, 그 직전 event를 구독자에게 전달

 

이렇게 글만 봐서는 이해가 어렵기 때문에 간단한 예제를 통해 학습해 보도록 하겠습니다!

 

PublishSubject

PublishSubject

위 다이어그램에서 맨 윗 줄은 PublishSubject, 그리고 나머지는 구독자입니다.

위를 향하는 화살표는 구독자가 PublishSubject를 구독하는 시점, 아래를 향하는 화살표는 PublishSubject가 구독자에게 이벤트를 방출하는 것을 나타냅니다.

그럼 PublishSubject는 어떤 특징을 가졌을까요? 구독자가 Subject를 구독하는 시점 '이 후'에 발생한 모든 event를 구독자에게 '즉시' 전달합니다.

첫 번째 구독자 (두 번째 줄)는 PublishSubject에 event1이 발생한 이후에 구독을 시작했습니다. 그렇기에 event1은 전달받지 못하고 그 이후에 생성된 event2, event3은 모두 즉시 전달받게 됩니다.

그리고 두번째 구독자 (세 번째 줄)는 PublishSubject에 event1, event2가 발생한 이후에 구독을 시작했기 때문에 그 후 발생한 event3만 전달받게 됩니다.

 

그럼 위 그림을 코드로 작성해 보겠습니다.

import RxSwift

let bag = DisposeBag()
let publishSubject = PublishSubject<Int>()
// PublishSubject는 초기 값 없이 생성됨

publishSubject.onNext(1)
// publishSubject에 event1 발생
// 하지만 해당 Subject의 구독자가 없기때문에 아무도 event를 전달받지 못함

let subscriber1 = publishSubject.subscribe { print("subscriber1 :", $0)}
subscriber1.disposed(by: bag)
// 첫 번째 구독자 생성 및 publishSubject 구독

publishSubject.onNext(2)
// event2 발생

// Result ----------
// subscriber1 : next(2)

// 첫 번째 구독자에게 즉시 전달됨

let subscriber2 = publishSubject.subscribe { print("subscriber2 :", $0)}
subscriber2.disposed(by: bag)
// 두 번째 구독자 생성 및 publishSubject 구독

publishSubject.onNext(3)
// event3 발생

// Result ----------
// subscriber1 : next(3)
// subscriber2 : next(3)

// 첫번째, 두번째 구독자에게 즉시 전달됨

그럼 만약 PublishSubject에 Error 또는 Completed 이벤트가 발생한 후에 구독한 구독자들에겐 어떤 일이 발생할까요?

위 코드에 이어 작성해보겠습니다.

publishSubject.onCompleted()
// completed 이벤트 발생

let subscriber3 = publishSubject.subscribe { print("subscriber3 :", $0)}
subscriber3.disposed(by: bag)
// 세번째 구독자 생성 및 publishSubject 구독

// Result ----------
//subscriber1 : completed
//subscriber2 : completed
//subscriber3 : completed

위처럼 Completed 이벤트가 발생한 PublishSubject를 구독하면, completed 이벤트를 전달받게 됩니다.

Error 이벤트의 경우에도 같은 결과가 발생합니다.

 

BehaviorSubject

BehaviorSubject

위 다이어그램에서 맨 윗 줄은 BehaviorSubject를 나타냅니다. 그리고 두 번째, 세 번째 줄은 구독자, 위로 향하는 화살표는 Subject를 구독하는 시점, 아래를 향하는 화살표는 event가 구독자에게 전달되는 것을 나타냅니다.

그럼 BehaviorSubject는 어떤 특징을 가지고 있을까요? BehaviorSubject는 초기 값이 저장된 채로 생성되고, 그 후 전달되는 event 중 가장 최신 이벤트로 저장된 값을 갱신합니다. 그리고 구독자가 Subject를 구독하는 시점에 가장 최신 이벤트를 전달하고, 그 후에 전달되는 모든 event는 즉시 구독자에게 전달 및 저장값을 경신합니다.

즉, BehaviorSubject는 구독자가 Subject를 구독한 이후에 event가 발생하지 않더라도 최소 1개의 이벤트를 전달받게 됩니다.

코드로 작성해보겠습니다.

let bag = DisposeBag()
let behaviorSubject = BehaviorSubject<Int>(value: 1)
// BehaviorSubject 생성 및 초기값으로 1 저장

let subscriber1 = behaviorSubject.subscribe { print("subscriber1 :", $0)}
subscriber1.disposed(by: bag)
// 첫 번째 구독자 생성 및 behaviorSubject 구독

// Result ----------
// subscriber1 : next(1)

// 초기값 1이 첫번째 구독자에게 전달됨

behaviorSubject.onNext(2)
// event2 발생

// Result ----------
// subscriber1 : next(2)
// 첫 번째 구독자에게 즉시 전달 및 behaviorSubject에 저장된 값을 2로 갱신함

let subscriber2 = behaviorSubject.subscribe { print("subscriber2 :", $0)}
subscriber2.disposed(by: bag)
// 두 번째 구독자 생성 및 behaviorSubject 구독

// Result ----------
// subscriber2 : next(2)

// 저장된 2가 두번째 구독자에게 전달됨


behaviorSubject.onNext(3)
// event3 발생

// Result ----------
// subscriber1 : next(3)
// subscriber2 : next(3)
// 첫번째, 두번째 구독자에게 즉시 전달됨 및 저장값 3으로 갱신

PublishSubject와 마찬가지로 Subject가 completed 이벤트가 발생한 후에 구독한 구독자에게는 completed 이벤트를 전달합니다.

behaviorSubject.onCompleted()

let subscriber3 = behaviorSubject.subscribe { print("subscriber3 :", $0)}
subscriber3.disposed(by: bag)

// Result ----------
//subscriber1 : completed
//subscriber2 : completed
//subscriber3 : completed

Error의 경우에도 마찬가지입니다.

 

ReplaySubject

ReplaySubject

위 다이어그램의 맨 윗줄은 버퍼의 크기가 2인 ReplaySubject입니다.

첫 번째 구독자 (두 번째 줄)는 이미 ReplaySubject를 구독하고 있는 상태이며, 그 후 발생한 event1, event2를 모두 즉시 전달받습니다. 그리고 두번째 구독자 (세번째 줄)는 구독한 시점에서 버퍼의 사이즈 만큼 저장된 event1, event2를 구독 즉시 전달받게 됩니다. 그 후 발생한 event3은 첫번째, 두번째 구독자 모두에게 즉시 전달되고 버퍼에 저장된 이벤트는 event2, event3으로 변경됩니다.

즉, ReplaySubject는 버퍼의 사이즈만큼의 최신 이벤트를 저장하고 저장된 event를 구독자에게 즉시 전달합니다. 또한, 새 이벤트를 받으면 버퍼에 담긴 이벤트 중 가장 오래된 이벤트를 갱신합니다.

코드를 작성해 보겠습니다.

let bag = DisposeBag()
let replaySubject = ReplaySubject<Int>.create(bufferSize: 2)

let subscriber1 = replaySubject.subscribe { print("subscriber1 :", $0)}
// 첫번째 구독자가 replaySubject 구독, 버퍼에 담긴 event가 없기에 아무것도 전달받지 못함

replaySubject.onNext(1)
// event1 발생 및 버퍼에 저장

// Result ----------
//subscriber1 : next(1)
// 구독중인 첫번째 구독자에게 event1 즉시 전달됨

replaySubject.onNext(2)
// event2 발생 및 버퍼에 저장

// Result ----------
//subscriber1 : next(2)
// 구독중인 첫번째 구독자에게 event2 즉시 전달됨

let subscriber2 = replaySubject.subscribe { print("subscriber2 :", $0)}
// 두 번째 구독자가 Subject 구독 시작

// Result ----------
//subscriber2 : next(1)
//subscriber2 : next(2)
// 두번째 구독자가 Subject를 구독하는 즉시 버퍼에 저장된 event1, event2 전달됨

replaySubject.onNext(3)
// event3 발생 및 버퍼에 있던 event1 (더 오래된 것)을 event3으로 교체

// Result ----------
//subscriber1 : next(3)
//subscriber2 : next(3)
// 구독중인 모든 구독자가 event3 발생 즉시 전달받음

그럼 Completed 또는 Error 가 발생한 후에 구독을 시작하면 어떻게 될까요?

replaySubject.onCompleted()

let subscriber3 = replaySubject.subscribe { print("subscriber3 :", $0)}

// Result ----------
//subscriber1 : completed
//subscriber2 : completed
//subscriber3 : next(2)
//subscriber3 : next(3)
//subscriber3 : completed

//이미 Subject를 구독중이던 구독자1, 구독자2는 즉시 completed 이벤트를 전달받고
//Subject가 Completed 된 이후에 구독을 시작한 구독자3은
//버퍼에 저장된 event2, event3을 전달 받은 후 completed 이벤트를 전달받음

BehaviorSubject는 저장된 값이 있어도 Completed 된 이후 구독자에게 Completed 이벤트만 전달하는 반면

ReplaySubject는 Completed 된 이후에 구독을 시작해도 버퍼에 저장된 이벤트들을 전달하고 Completed 이벤트를 전달합니다.

이는 Error의 경우에도 동일합니다.

 

AsyncSubject

AsyncSubject는 위의 Subject들과 이벤트를 전달하는 시점에 차이가 있습니다.

위의 Subject들은 구독 중에 이벤트가 발생하면 즉시 구독자에게 전달하는 공통점을 가지고 있습니다.

AsyncSubject는 Completd 이벤트가 발생했을 때만 직전에 전달받은 Next이벤트를 구독자에게 전달합니다.

만약 Completed 이벤트를 받지 못하고 계속 Next 이벤트만 받는 경우나 Error 이벤트로 Subject가 종료되는 경우에 구독자들은 어떤 이벤트도 전달받지 못합니다.

코드를 작성해보겠습니다.

let bag = DisposeBag()
let asyncSubject = AsyncSubject<Int>()

let subscriber1 = asyncSubject.subscribe { print("subscriber1 :", $0)}
// 첫번째 구독자가 asyncSubject 구독

asyncSubject.onNext(1)
asyncSubject.onNext(2)
// Next 이벤트가 발생해도 구독자는 전달받지 못함

asyncSubject.onCompleted()

// Result ----------
//subscriber1 : next(2)
//subscriber1 : completed

// Completed 이벤트가 발생하면 가장 최근에 발생한 event2를 구독자에게 전달하고 Completed도 전달

let subscriber2 = asyncSubject.subscribe { print("subscriber2 :", $0)}

// Result ----------
//subscriber2 : next(2)
//subscriber2 : completed

// Completed 이벤트가 발생한 후에 구독을 시작해도 Completed 전 가장 최신 이벤트를 전달받음

 

참고 : Reactive Programming with Swift By the raywenderlich.com Tutorial Team

'iOS > RxSwift' 카테고리의 다른 글

RxSwift - Filtering Operators  (0) 2022.05.15
RxSwift - Relay  (0) 2022.03.05
RxSwift - Disposable, DisposeBag  (0) 2022.03.01
RxSwift - Infallible  (0) 2022.02.28
RxSwift - Observables, Observers  (0) 2022.02.27
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함