-
RxSwift - SubjectRxSwift 2021. 11. 3. 22:09
안녕하세요. 그린입니다🟢
이번 포스팅에서는 저번 RxSwift의 첫번째 타자였던 Observable에 이어 Subject를 알아보겠습니다💁🏻
이전 포스팅에서 Observable을 학습하면서 이벤트를 만들고 구독 및 dispose하는 방법에 대해 알아봤습니다.
다시 말하지만 아래 링크에서 학습을 하고 있습니다🧐
https://github.com/fimuxd/RxSwift/blob/master/Lectures/03_Subjects/Ch3.%20Subjects.md
그런데 많은 코드에서는 실시간으로 이벤트를 추가하고 방출하는것을 할 수 있어야합니다.
이를 위해 Subject라는 개념이 나왔습니다.
그럼 자세히 알아볼께요🙌Subject?
Observable이자 Observer인 것을 Subject라 합니다.
Observable은 unicast로 하나의 Observer만 subscribe할 수 있지만
Subject는 multicast로 여러개의 Observer를 subscribe할 수 있습니다.
조금 더 자세히 들어가본 Observable과 Subject의 차이
Observable
단일 함수로 상태를 가지지 않고 구독하는 Observer에 매번 생성하는 create를 사용해 실행해줍니다.
이렇게 각 Observer에 대해 생성되고 실행되서 버그가 발생할 수 있습니다.
let randomObservable = Observable<Int>.create{ observer in observer.onNext(Int.random(in: 0 ..< 10)) return Disposables.create() } randomObservable.subscribe(onNext: { (num) in print("\(num)") }) randomObservable.subscribe(onNext: { (element) in print("\(num)") }) // 3 // 7
위 코드같이 매번 구독 시 생성하여 만들어주니 다른 값을 방출하게 됩니다.
Subject
Observer 정보를 저장해 한번의 실행으로 여러 Observer에게 결과를 보냅니다.
let randomObservable = BehaviorSubject(value: 0) randomObservable.onNext(Int.random(in: 0..<10)) randomObservable.subscribe(onNext: { (num) in print("\(num)") }) randomObservable.subscribe(onNext: { (element) in print("\(num)") }) // 5 // 5
위 코드는 Observer의 정보를 저장함으로 동일한 결과를 방출합니다.
둘의 차이를 다시 정리해보면 상태를 가지고 저장하는 유무라 볼 수 있습니다.
그로인해 여러 옵저버에 대해 같은 코드를 실행하냐 안하냐의 차이를 볼 수 있으며
그렇기에 Subject는 Observable하면서 그 값을 관찰할 수 있는 Observer가 되는것입니다.
여러 옵저버가 하나의 데이터를 바라보며 수시로 저장 및 변경될때 Subject를 쓸 수 있어야합니다.
Subject 종류
4가지의 Subject 타입을 가지고 있습니다.
1. PublishSubject
초기값이 없으며 새로운 값만 subscribe에 방출
2. BehaviorSubject
초기값을 가지며 subscriber에게 초기값 혹은 새로운 값을 방출
3. ReplaySubject
초기화 시 버퍼를 가지며 버퍼 사이즈만큼 값을 가지면서 subscriber에 방출
4. Variable
BehaviorSubject를 래핑해 현재 값으로 상태 보존하며 초기 혹은 새로운 값을 subscriber에 방출
그러면 이제 4가지의 Subejct에 대해 자세히 알아보면서 어떻게 쓰이는지 보겠습니다💁🏻
PublishSubject
구독 후 Observable이 방출하는 이벤트를 전달 받음
이렇게 마블다이어그램으로 설명될 수 있습니다.
다른 Observer가 각기 다른 시점에 구독한다면 구독되는 시점에 따라 이전것은 받지 못합니다.
음.. 코드로 보시죠!
let subject = PublishSubject<Int>() let disposeBag = DisposeBag() subject.subscribeNext { print("First Observer is \($0)") } .addDisposableTo(disposeBag) subject.on(.Next(1)) subject.on(.Next(2)) subject.subscribeNext { print("Second Observer is \($0)") } .addDisposableTo(disposeBag) subject.on(.Next(3)) subject.onCompleted() // First Observer is 1 // First Observer is 2 // Second Observer is 3 // First Observer is 3
위와 같이 코드로 보면 좀 더 이해가 되실거에요.
처음 구독 후 1과 2를 받았고 그다음 구독 후 3을 받았어요.
이렇게 되면 1과 2는 처음 구독 프린트문에만 적용되고 두번째 구독한 부분에서는 1과 2다음에 이뤄졌기에 프린트가 되지 않죠.
completed 혹은 error로 Subject를 끝내지 않는 이상 계속 살아있게 됩니다.
PublishSubject 용도
어떨때 쓰일 수 있을까요?
실시간에 필요한 데이터 프로젝트에 쓰일 수 있을겁니다.
예를들어 마감임박 같은 경우를 보면 1시 기준으로 마감되는 사항에 대해 이전에 들어온 유저에겐 알려줘야하지만
마감 후 들어오는 유저에게는 이벤트를 방출할 필요가 없을겁니다.
그럴때 유용하게 쓰일 수 있겠습니다.
BehaviorSubject
초기값을 가지고 이후 구독되는것에 대해서 최근 값을 방출합니다.
위의 그림처럼 분홍 초기값을 가지고 구독 시 최근값이 없으니 분홍값을 전달해요.
그리고 쭉 이어지다 새로운 구독이 생기면 제일 최근값인 초록을 방출합니다.
이것또한 코드로 보시죠!
let subject = BehaviorSubject(value: 1) let disposeBag = DisposeBag() subject.subscribeNext { print("First Observer is \($0)") } .addDisposableTo(disposeBag) subject.on(.Next(2)) subject.on(.Next(3)) subject.subscribeNext { print("Second Observer is \($0)") } .addDisposableTo(disposeBag) subject.on(.Next(4)) subject.onCompleted() // First Observer is 1 // First Observer is 2 // First Observer is 3 // Second Observer is 3 // First Observer is 4 // Second Observer is 4
처음에 초기값 1을 가지고 구독 시 방출해요 그리고 publishSubject와 동일하게 진행하다 두번째 구독 시 최근값인 3을 방출해주고 또 진행되는 사이클입니다.
BehaviorSubject 용도
초기값 혹은 가장 최근값을 가지고 있으니 미리 데이터를 패치할때 유리합니다.
화면을 미리 구성하거나 할때 로딩 시 해당 작업을 해준다면 최근값들로 미리 채워넣을 수 있습니다.
RelaySubject
구독전에 버퍼 사이즈 만큼 값을 저장했다 구독 시 해당 사이즈만큼 값을 방출해주고 시작됩니다.
버퍼 크기가 0이면 publishSubject와 다를게 없습니다.
이건 코드로 더 잘 이해가 될테니 코드 보시죠!
let subject = ReplaySubject<Int>.create(bufferSize: 2) let disposeBag = DisposeBag() subject.subscribeNext { print("First Observer is \($0)") } .addDisposableTo(disposeBag) subject.on(.Next(1)) subject.on(.Next(2)) subject.subscribeNext { print("Second Observer is \($0)") } .addDisposableTo(disposeBag) subject.on(.Next(3)) subject.onCompleted() // First Observer is 1 // First Observer is 2 // Second Observer is 1 // Second Observer is 2 // First Observer is 3 // Second Observer is 3
요런 느낌~!
버퍼 사이즈만큼 지정해줄 수 있습니다🙌
ReplaySubject 용도
이것은 최근검색어에 비교를 해보면 좋을것 같습니다.
원하는 크기만큼 최근 검색한것에 대해 즉 10개까지 보여준다라고 치면 사이즈를 10으로 주고 설정해줄 수 있습니다.
이런 이벤트들 구성에 특화되어 있습니다.
Variable
BehaviorSubject를 래핑했기에 초기값을 가질 수 있으며 동일한 동작을 해주지만 completed나 error 이벤트가 발생하지 않는 차이가 있습니다. 다 이벤트가 방출되고 Variable이 해제되면 자동 completed 이벤트를 방출합니다.
코드로 보면 이렇습니다.
let variable = Variable(1) let disposeBag = DisposeBag() variable.asObservable().subscribe { print("First Observer is \($0)") } .addDisposableTo(disposeBag) variable.value = 2 variable.value = 3 variable.asObservable().subscribet { print("Second Observer is \($0)") } .addDisposableTo(disposeBag) variable.value = 4 // First Observer is Next(1) // First Observer is Next(2) // First Observer is Next(3) // Second Observer is Next(3) // First Observer is Next(4) // Second Observer is Next(4) // First Observer is Completed // Second Observer is Completed
BehaviorSubject와 거의 동일함으로 이해가 좀 더 잘되었습니다!
마무리하며
이번에는 이렇게 Subject에 대해 알아보았어요!
정말 정말 아직까지도 헷갈리는 부분 투성이였다고 느껴지는데 그래도 접근하고 감을 잡은것 같아 만족합니다!
가장 중요한건 다시 기초로 돌아가 Subject가 Observable하고 어떤 차이인지 그걸 캐치하는것을 위해 달려왔어요.
그것만 캐치한다면 Subject 이해는 다한거라고 생각합니다👍[참고자료]
https://sujinnaljin.medium.com/rxswift-subject-99b401e5d2e5
https://github.com/fimuxd/RxSwift/blob/master/Lectures/03_Subjects/Ch3.%20Subjects.md
http://minsone.github.io/programming/reactive-swift-subject
'RxSwift' 카테고리의 다른 글
RxSwift - Transforming Operator (0) 2021.11.10 RxSwift - Filtering Operators (0) 2021.11.08 RxSwift - Observable (0) 2021.10.30 RxSwift - Zip (0) 2021.10.13 RxSwift - Buffer & Window (0) 2021.10.06