본문 바로가기

RxSwift

[RxSwift] Subject & Relay

 

지난 포스팅에서 Observable은 이벤트를 observer에게 전달하고, observer는 Observable을 구독하여 전달되는 이벤트를 처리한다고 했다.

 Observable은 observer와 달리 다른 Observable을 구독하지 못하며(observer만이 구독 가능)

 observer는 다른 observer로 이벤트를 전달하지 못한다.(Observable만이 이벤트 전달 가능)

 

 여기서 Observable과 observer의 역할을 동시에 수행할수 있는 Subject라는 것이 나왔다.

 Subject는 다른 Observable로부터 이벤트를 받을수도 있으며 Subject를 구독하고 있는 구독자에게 이벤트를 전달할수도 있다.

 그러니 Subject는 Observable인 동시에 observer인 샘이다.

 

 

Subject는 4가지로 나눌 수 있다.

먼저 PublishSubject에 대해 알아보겠다.

 

PublishSubject

 

PublishSubject는 Subject로 전달되는 새로운 이벤트를 observer에게 전달하는 가장 기본적인 형태의 Subject다.

Subject가 생성되는 시점에는 내부에 아무런 이벤트가 저장되어 있지 않은 비어있는 상태로 생성된다.

( 생성직후 observer가 구독을 시작하게 되면 observer는 아무런 이벤트를 받을 수 없다.

즉 Subject가 생성된 후에 구독을 시작하게 되고 어떠한 이벤트가 발생했을때 비로소 이벤트를 받을수 있다. )

 

let subject = PublishSubject<String>() // 문자열이 포함된 Next 이벤트를 받아서 다른 observer에게 전달 할 수 있는 Subject

subject.onNext("이건 보이지 않을거에요") // onNext 메소드 호출 가능 ( Observable.create 메소드 내부에서 onNext를 호출했던것처럼 subject에서 바로 onNext 가능 )

 

하지만 해당 Subject를 구독하고 있는 observer가 현재 없기 때문에 이벤트가 처리되지 않을것이다.

 

그리하여 아래와 같이

해당 PublishSubject를 구독하게 된다면 그때서야 이벤트를 받을 준비를 하게 되는 것이고

구독 시점 이후에 발생한 이벤트를 받아볼 수 있게 되는것이다.

subject.subscribe {
    print($0)
}.disposed(by: disposeBag)

subject.onNext("구독한 후에 발생한 이벤트라서 보일거에요")

 

 

Observable에서 complete 이벤트가 발생하면 해당 Observable은 종료되어 다른 Next 이벤트를 더이상 전달할 수 없게 된다고 했다.

Subject도 complete 이벤트가 발생하게 되면 전달할 Next 이벤트가 더이상 없기 때문에

새로운 구독자가 생긴다 해도 바로 Complete 이벤트를 전달하게 되어 종료하게 된다. (Error 이벤트도 동일)

 

Complete 이벤트나 Error 이벤트를 발생시키고 새로운 구독자를 생성한 후 로그를 찍어본다면

새로운 구독자에게는 Next 이벤트가 아닌 Complete 이벤트만이 받아볼 수 있는 것을 확인할 수 있을것이다.

subject.onCompleted()
//subject.onError(MyError.error)

subject.subscribe {
    print("Complete 이벤트가 발생한 후에 구독을 해서 Next 이벤트는 전달받지 못할것이고 바로 Complete 이벤트를 전달 받는다.",$0)
}

 

 

BehaviorSubject

 

BehaviorSubject는 PublishSubject와 유사한 방법으로 동작한다.

Subject로 전달된 이벤트를 구독자에게 전달하는 것은 동일하지만 Subject를 생성하는 방식에 차이가 있다.

BehaviorSubject는 PublishSubject와 달리 생성 시점에 기본 시작 이벤트를 지정할 수 있다.

그리고 Subject로 전달되는 이벤트중에서 가장 마지막에 전달된 최신 이벤트를 저장해 놓았다가 새로운 구독자에게 최신 이벤트를 전달한다.

PublishSubject는 구독한 시점 이후에 발생한 이벤트를 받아볼 수 있는 반면에 BehaviorSubject는 구독하기 전에 초깃값으로 저장되어있는 이벤트를 바로 받아볼 수 있다는 차이점이 있다.

 

 

let pSubject = PublishSubject<Int>() // 내부에 이벤트가 저장되지 않은 상태로 Subject 생성

pSubject.subscribe {
    print($0, "pSubject") // 구독을 했지만 바로 Next 이벤트를 받아볼 수 없다.
}.disposed(by: disposeBag)

.
.
.

let bSubject = BehaviorSubject<Int>(value: 0) // 명시된 타입에 맞는 초기 이벤트를 초깃값으로 지정하여 Subject 생성

bSubject.subscribe {
    print($0, "bSubject") // 구독을 하여 Subject에 가장 마지막에 전달된 최신 Next 이벤트를 받아볼 수 있다.
}

 

 

그리하여 로그창에는 초기 이벤트로 저장되었던 0이라는 이벤트를 구독함과 동시에 받아볼 수 있는 걸 확인할 수 있다.

 

 

후에 아래와 같이 Subjcet로 Next 이벤트를 방출했을때

BehaviorSubject에 저장되어있는 Next 이벤트는 0이 아닌 1로 변경이 되어있는걸 확인할 수 있을 것이다.

bSubject.onNext(1)

 

 

Complete 이벤트나 Error 이벤트를 발생시켰을때

새로운 구독자에게는 Next 이벤트가 아닌 Complete 이벤트만이 받아볼 수 있는 것은 PublishSubject와 동일하다.

 

bSubject.onCompleted()
//bSubject.onError(MyError.error)

bSubject.subscribe {
    print("Complete 이벤트가 발생한 후에 구독을 해서 Next 이벤트는 전달받지 못할것이고 바로 Complete 이벤트를 전달 받는다.",$0)
}

 

 

 

ReplaySubject

ReplaySubject는 하나 이상의 새로운 이벤트를 버퍼에 저장한다.

observer가 구독을 시작하면 버퍼에 있는 모든 이벤트를 전달한다.

PublishSubject나 BehaviorSubject에서 이벤트가 들어왔을때 최신 이벤트를 제외한 나머지 과거의 모든 이벤트는 사라지게 되는데

이런 점에 있어 두개 이상의 이벤트를 저장해두고 새로운 구독자로 전달하고자 할때 ReplaySubject를 사용한다.

ReplaySubject를 생성할때는 PublishSubject, BehaviorSubject와 달리 create 메소드로 Subject를 생성한다.

 

let rSubject = ReplaySubject<Int>.create(bufferSize: 3)
// 3개의 이벤트를 저장하는 버퍼를 가진 ReplaySubject를 생성
// 버퍼는 메모리에 저장되기 때문에 필요이상으로 큰 버퍼를 사용하는것은 피해야한다.



(1...10).forEach {
    rSubject.onNext($0)
}
// 1부터 10까지의 10개의 이벤트를 Subject로 전달 (Subject 생성시 버퍼 사이즈를 3으로 지정해주었기 때문에 마지막에 저장된 3개의 이벤트가 저장 되어진다. 8, 9, 10)
rSubject.subscribe {
    print($0)
}

 

 

 

 

후에 Subject로 Next 이벤트가 전달되었을때 가장 오래된 Next 이벤트가 삭제되며

가장 마지막에 저장된 3개의 이벤트로 변경된다. (9, 10, 11)

rSubject.onNext(11)

 

 

 

Complete 이벤트나 Error 이벤트를 발생시켰을때

PublishSubject와 BehaviorSubject와는 달리

새로운 구독자에게는 버퍼 사이즈 만큼의 저장 되어있는 Next 이벤트가 전달된 후에 Complete 혹은 Error 이벤트가 전달된다.

rSubject.onCompleted()
//rSubject.onError(MyError.error)

rSubject.subscribe {
    print("Complete 이벤트가 발생한 후에 구독을 해도 저장되어 있는 Next 이벤트를 전달한 후에 Complete 이벤트를 전달 한다.",$0)
}

 

AsyncSubject

 

AsyncSubjcet는 다른 Subjcet와 이벤트를 전달하는 시점에 차이가 있다.

PublishSubject, BehaviorSubject, ReplaySubject는 Subjcet로 이벤트가 전달되면 즉시 구독자에게 전달한다.

반면 AsyncSubjcet는 Complete 이벤트가 전달 되기전까지 어떤 이벤트도 구독자에게 전달하지 않는 특징을 가지고 있다.

Complete 이벤트가 전달되면 그 시점에 가장 최근에 전달된 Next 이벤트 하나를 구독자에게 전달한다.

 

let ASubject = AsyncSubject<Int>()

ASubject
    .subscribe{ print($0) }
    .disposed(by: bag)

ASubject.onNext(1) // 아직 subject에 Complete 이벤트를 전달하지 않았기 때문에 아무런 이벤트를 구독자에게 전달하지 않을것이다.
ASubject.onNext(2) // 상동
ASubject.onNext(3) // 상동

 

Complete 이벤트 전달.

ASubject.onCompleted() // 가장 최근에 전달된 Next 이벤트 하나를 구독자에게 전달 (만약 전달된 Next 이벤트가 하나도 없다면 그냥 Copmlete 이벤트만 전달)

 

Error 이벤트 전달.

ASubject.onError(MyError.error) // Error 이벤트 발생시 Next 이벤트는 구독자에게 전달되지 않으며 바로 Error 이벤트만 전달되어진다.

 

Relay

 

Subject를 래핑하고 있는 두가지 Relay가 있다.

PublishRelay는 PublishSubject를 래핑한것이고, BehaviorRelay는 BehaviorSubject를 래핑한것이다.

Relay는 일반적인 Subject와 달리 Next 이벤트만 받고 Complete 이벤트와 Error 이벤트는 전달 하지 않으며 전달 받지도 않는다.

즉 구독자가 dispose 되기 전까지 계속 이벤트를 처리한다.

주로 종료없이 계속 전달되는 이벤트 시퀀스를 처리할때 활용한다. (ex. UI 이벤트 처리)

 

 

PublishRelay

let pRelay = PublishRelay<Int>() // PublishSubject와 같이 초기 이벤트 없이 생성한다.

pRelay.accept(0)
// PublishSubject와 같이 구독하기 전에 발생한 이벤트는 무시된다.
// relay에서는 이벤트를 전달하고자 할때 onNext가 아닌 accept 메소드를 사용해야한다.

pRelay.subscribe {
    print($0)
}
.disposed(by: bag)

pRelay.accept(1)
// PublishSubject와 같이 구독한 후에 전달된 이벤트이기 때문에 즉시 이벤트를 받아볼수 있다.

 

 

BehaviorRelay

let bRelay = BehaviorRelay(value: 1) // BehaviorSubject와 같이 초기 이벤트를 설정할 수 있다.

bRelay.accept(2)
// BehaviorSubject와 같이 저장되어 있는 이벤트가 2로 변경된다.
// relay에서는 이벤트를 전달하고자 할때 onNext가 아닌 accept 메소드를 사용해야한다.

bRelay.subscribe {
    print($0)
}
.disposed(by: bag)

bRelay.accept(3)
// BehaviorSubject와 같이 구독한 후에 전달된 이벤트이기 때문에 즉시 이벤트를 받아볼수 있다.

 

'RxSwift' 카테고리의 다른 글

[RxSwift] Operators (2)  (0) 2021.02.14
[RxSwift] Operators (1)  (0) 2021.02.12
[RxSwift] Observables & Observer  (0) 2021.02.10