[RxSwift] Subscribe의 동작 파헤치기


Subscribe의 일반적인 사용

RxSwift를 사용한다면 예외 없이 Subscribe를 사용하고 계실 것입니다.

보통 다음같이 사용하죠.

[Subscribe의 사용]

let numbersObservable = Observable.of(1, 2, 3, 4).debug()

numbersObservable
.subscribe()
.disposed(by: disposeBag)

[코드 실행결과]

-> subscribed**
-> Event next(1)**
-> Event next(2)**
-> Event next(3)**
-> Event next(4)**
-> Event completed**
-> isDisposed**

익숙하다구요? 당연하다구요?

그렇다면 한가지 질문을 드리겠습니다.

Observable은 보이는데  Observer는 어딨을까요?

보통 Subject를 Observable이면서 Observer라고 얘기하는데, 여기서 말하는 Observer를 한번이라도 본적 있으신가요? 전 없는데…

Observer 는 어딨는 걸까요? 🤔

Subscribe의 정의

공식 문서를 한번 살펴보겠습니다1. 설명의 처음 한 문단만 보겠습니다.

Subscribe
operate upon the emissions and notifications from an Observable

The Subscribe operator is the glue that connects an observer to an Observable. In order for an observer to see the items being emitted by an Observable, or to receive error or completed notifications from the Observable, it must first subscribe to that Observable with this operator.

Subscribe는 Observable의 알림과 방출에 따라 동작한다.
Subscribe 연산자는 Observable에 Observer를 연결하는 접착제다. Observable에서 아이템이 방출되거나 에러 혹은 완료 알림을 받기 위해서는 반드시 처음에 이 연산자를 Observable에 사용해 구독해야만 가능하다.

Wha~~t???

역시 공식문서느님은 말해주고 계셨네요. 문서를 읽으며 알 수 있는 사실은 두가지 입니다.

하나는 Subscribe가 Observable에 Observer를 연결해준다는 것! Subscribe가 Observer같이 느껴졌는데 Subscribe가 Observer는 아니였습니다.

다른 하나는 Subscribe가 Operator였다는 사실입니다. 😮 헐…

Observable을 합치거나 발행된 아이템을 변형시키거나 걸러내는 행위를 하는 것만 Operator인줄 알았는데 Subscribe도 연산자였던 것입니다. 하는 일은 정의와 같이 Observable에 Observer를 붙여주는 일이죠.

오호! 지식이 늘었네요! 😙 (지식이 +1 늘었다.)

Subscribe가 Observable에 Observer를 붙여준다는 사실은 확인했는데, 아직까지 Observer가 어디있는지는 발견하지 못했습니다.

이제 뭘해야할까요? Subscribe 안의 코드를 한번 살펴보죠.

Subscribe의 내부

[ObservableType+Extensions.swift]

public func subscribe(onNext: ((Element) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
        -> Disposable {
            let disposable: Disposable
            
            if let disposed = onDisposed {
                disposable = Disposables.create(with: disposed)
            }
            else {
                disposable = Disposables.create()
            }
            
            #if DEBUG
                let synchronizationTracker = SynchronizationTracker()
            #endif
            
            let callStack = Hooks.recordCallStackOnError ? Hooks.customCaptureSubscriptionCallstack() : []
            
            let observer = AnonymousObserver<Element> { event in
                
                #if DEBUG
                    synchronizationTracker.register(synchronizationErrorMessage: .default)
                    defer { synchronizationTracker.unregister() }
                #endif
                
                switch event {
                case .next(let value):
                    onNext?(value)
                case .error(let error):
                    if let onError = onError {
                        onError(error)
                    }
                    else {
                        Hooks.defaultErrorHandler(callStack, error)
                    }
                    disposable.dispose()
                case .completed:
                    onCompleted?()
                    disposable.dispose()
                }
            }
            return Disposables.create(
                self.asObservable().subscribe(observer),
                disposable
            )
    }

저희가 위 코드에서 살펴볼 부분은 두 부분입니다. 하나는 여기

let observer = AnonymousObserver<Element> { event in
                
                #if DEBUG
                    synchronizationTracker.register(synchronizationErrorMessage: .default)
                    defer { synchronizationTracker.unregister() }
                #endif
                
                switch event {
                case .next(let value):
                    onNext?(value)
                case .error(let error):
                    if let onError = onError {
                        onError(error)
                    }
                    else {
                        Hooks.defaultErrorHandler(callStack, error)
                    }
                    disposable.dispose()
                case .completed:
                    onCompleted?()
                    disposable.dispose()
                }
            }

보시다시피 Subscribe안에서 Observer를 생성하고 있네요! Observer는 event를 종류별로 방출하는 역할을 하고 있네요. 말그대로 이벤트를 관찰하고 거기에 맞는 행위를 하는 Observer역할을 하는 진짜 Observer입니다.

그다음에 살펴볼 코드는 다음 코드입니다.

return Disposables.create(
                self.asObservable().subscribe(observer),
                disposable
            )

Subscribe를 하면 반환되는 것이 Disposable인데 Subscribe안에서 self.asObservable().subscribe(observer) 요런 인자를 만들어서 Disposable을 생성하고 있네요.

self.asObservable()을 좀 더 뜯어보면

extension ObservableType {
    
    /// Default implementation of converting `ObservableType` to `Observable`.
    public func asObservable() -> Observable<Element> {
        // temporary workaround
        //return Observable.create(subscribe: self.subscribe)
        return Observable.create { o in
            return self.subscribe(o)
        }
    }
}

Observable을 생성하고 subscribe를 한 구독체를 반환하고 그 Observable에 .subscribe(observer) 로 Observer를 붙인 구독체를 최종적으로 반환하는 것을 확인할 수 있습니다.

내부에서 subscribe를 두번하는 것에 대한 얘기는 여기서는 생략하고(확실히 잘 몰라서 😓) Subscribe를 했을때 발생하는 일을 정리해 보겠습니다.

Subscribe가 하는 일

Observable을 Subscribe하면 Subscribe내부에서

Observer를 생성하고 생성한 그  Observer를내부에서 생성한  Observable에 붙이고붙인 그 구독체를 반환한다.

이렇게 정리할 수 있습니다.

Subscribe가 하는 일을 기억해두세요.

사실 이번 포스트는 다음 포스트인 Observable의 Share와 Share(replay)를 설명하기 위한 사전 포스트거든요. 😊

다음 포스트에서 또 만나요~

Read more

카카오모빌리티 5년, 그 경험과 생각

2018년부터 2024년까지 5년 동안 카카오모빌리티에서 개발자로 일하며 얻은 경험과 느낀 점을 정리해 보았습니다. 개인적인 관점에서 작성된 내용이며, 제 경험이 회사 전체를 대표하지는 않습니다. 일 * 리더의 변화가 회사 분위기를 바꾼다. * 재직 중 CEO가 한 번 교체되었고, 그 후 CTO를 비롯한 여러 리더들이 함께 교체되었습니다. * 리더가 바뀌니 마치 다른 회사에 다니는 것처럼

By 토미
복잡한 마음이 든다면 적어보세요.

복잡한 마음이 든다면 적어보세요.

살다보면 분노, 불안, 우울 등 부정적인 감정이 찾아옵니다. 이런 감정은 시간이 지나면 금새 사라지기도 하지만, 때로는 계속 불쑥 불쑥 올라와서 마음을 혼란에 빠뜨립니다. 이럴 땐 한번 적어보세요. 내 마음은  왜 화가 났는지, 왜 불안한지, 왜 우울한지, 적다보면 부정정인 감정의 근본 원인이 무엇인지 그 감정을 어떻게 정리해야 할지 실마리를 찾을 수

By 토미
인생이란 책

인생이란 책

사람은 누구나 인생이란 책의 저자입니다.  모든 사람은 단 한 권의 책을 쓰고, 모든 책의 주인공은 저자 자신입니다.  1년의 삶을 한 페이지에 적는다고 했을 때, 책은 보통 80페이지 정도 되고, 많아도 120페이지를 넘기진 않습니다.  책에 한 번 써진 내용은 수정하거나 삭제할 수 없습니다. 마음에 들지 않는 페이지가 있더라도 찢어버릴 수 없습니다.

By 토미
박경리 - 산다는 것

박경리 - 산다는 것

체하면 바늘로 손톱 밑 찔러서 피 내고 감기들면 바쁜듯이 뜰 안을 왔다갔다 상처나면 소독하고 밴드하나 붙이고 정말 병원에는 가기 싫었다 약도 죽어라 안 먹었다 인명재천 나를 달래는 데 그보다 더 생광스런 말이 또 있을까 팔십이 가까워지고 어느 날부터 아침마다 나는 혈압약을 꼬박꼬박 먹게 되었다 어쩐지 민망하고 부끄러웠다 허리를 다쳐서 입원했을

By 토미


[책] 토미의 Git with 소스트리

Git을 제대로 알고 싶으신 분들께 추천드립니다.



[온라인 강의] 토미의 Git & Github

Git을 제대로 알고 싶으신 분들께 추천드립니다.