iOS/RxSwift

RxSwift) Observable, Observer, Subscribe 흐름 이해하기

소들이 2023. 1. 10. 20:39

 

 

 

안녕하세요, 소들입니다 🐮

12월 한 달.. 망나니처럼 논 업보를 청산하러 왔습니다.. OTL..

 

저번 포스팅에선 RxSwift가 무엇인지,

반응형 프로그래밍이 무엇인지에 대해 개념적인 이해만 했잖아요??

이번 포스팅에선

 

Observable

Observer

Subscribe

 

의 흐름에 대해 좀 이해를 해보려고 합니당!

당장 Observable를 생성하는 연산자가 뭐고 어떻게 생성하고를 배우고 하는 게 아니라,

이들이 어떤 상호작용을 통해 반응형 프로그래밍을 이루는지에 대해 다룰 포스팅이에요!

해당 기능에 대해 더 자세하게 다룰 내용은 다음 포스팅부터 진행 하겠습니다!

 

이전 포스팅 내용을 숙지 하고 있다는 가정 하에 쓰기 때문에,

Reactive Programming이 뭔지 모르신다면 먼저 보고오시길 바라겠음

모든 포스팅은 편의 말투로 합니다~!!

 

+

문서는 그저 기본 개념만 있어서..

문서 + RxSwift 코드 뒤적이며 제 방식대로 이해한 거라

혹여나 잘못된 내용은 꼭꼭 피드백 주세요!

내용이 꽤 어렵습니다

 

 

 

 

1. RxSwift가 뭐라구!?

 

RxSwift는 비동기 프로그래밍을

관찰 가능한 순차적 형태와 함수 형태의 연산자를 통해 처리하게끔 도와줍니다

 

우린 앞서 RxSwift에 대해 위와 같이 정의 하면서,

 RxSwift는 반응형 프로그래밍이기 때문에

어떤 비동기 이벤트에 대해 관찰 가능한 형태로 만들고

이를 관찰하는 놈이 있을 경우, 이 비동기 이벤트의 변화에 따른 전파를 받을 수 있구나

를 이해 했잖음??

 

여기서..!!!

관찰 가능한 순차적인 형태가 바로 Observable

해당 이벤트의 변화를 관찰하여 전파 받는 놈이 바로 Observer

 

끝^^!!....

이라 하고 싶지만,,,^^,,,

좀 더 자세히 알아보러 갑시당!!

 

 

 

 

2. Observable과 Observer

 

2-1. Observable

 

자, 여러분 

RxSwift의 정의 중 머릿부분만 짤라서 봐보셈

어떤 비동기 이벤트에 대해 관찰 가능한 형태로 만든다 였잖음!??!

여기서 이 관찰 가능한 형태라는 것이 바로!! 앞으로 Observable이라고 부르는 것임!! ㅎㅎ

따라서 Observable의 정의를 보자면,

 

Observable은 관찰이 가능한 흐름으로,

비동기 이벤트의 시퀀스를 생성할 수 있는 대상

 

을 말함!!

실제, RxSwift의 Observable이란 것에 대한 정의를 보면

 

 

 

 

제네릭 클래스로 구성이 되어 있음!!!!

우리가 비동기 이벤트를 어떤 관찰 가능한 형태로 만든다는 것은,

비동기 이벤트를 제네릭 타입의 Observable이란 클래스 인스턴스를 만든단 것과 같은 것임!!

당장 Observable을 어떻게 만드는지,

어떤 연산자를 쓰는지는 다음 Observable 포스팅에서 다룰 것이니 넘어가셈

 

아.. 음.. 오키..

근데 Observable이 비동기 이벤트의 시퀀스...?...를 생성한다는 게

도대체 무슨 말잉겨,, 싶잖음??

 

조금 더 쉽게 이해를 해봅시당

우리가 가장 많이 사용하면서, 가장 대표적인 비동기 이벤트가 뭐가 있음?

바로 UIButton(버튼)의 클릭 이벤트임!!!!!

 

 

 

 

 

 

위와 같이 버튼을 만들었을 때, 버튼은 왜 비동기 이벤트일까??

내가 버튼을 만들긴 했지만,

이 버튼의 클릭 이벤트가 유저에 의해 언제 발생할지 선언할 당시에는 모름!!

따라서 우린 이벤트가 들어왔을 때를 가정하고 프로그래밍을 해왔잖음?

(만약 버튼이 sync 동작이었다면, 버튼이 눌릴때까지 아무 작업도 할 수 없을 것임)

따라서 버튼은 비동기 이벤트에 해당됨

 

근데 Reactive Programming이란,

데이터의 흐름이 변경되었을 때 전파하는 데 중점을 둔 프로그래밍이잖음

따라서 여기서 데이터는 버튼이 될 거고,

데이터의 흐름이 변경된다는 것은 버튼이 클릭 된다는 것이 될 것임!

 

그렇다면 이 버튼의 흐름 변경을 전파하기 위해선

버튼의 클릭 이벤트가 "관찰이 가능한 형태"

즉, Observable이 되어야 하겠구나!

 

정리하자면,

버튼의 클릭을 나타내는 UIButton의 tap 이벤트는 비동기 이벤트로,

데이터의 변화에 대해 관찰이 가능한 형태,

즉, RxSwift에서 이를 Observable 형태로 만들 수 있음

 

그러면 이 버튼이 눌렸을 때 어떤 일이 일어나냐면,

버튼이 눌렸다는 것은 데이터의 흐름이 변경 되었다는 거잖음?

따라서 이 버튼의 tap event에 대한 Observable은

버튼이 눌렸을 때(비동기 이벤트가 발생했을 때),

해당 이벤트가 발생했다는 것을 알려주기 위해 "항목(item)"이란 것을 "방출(emit)"함!!

(좀 더 쉬운 단어를 찾았으나.. 음 ReactiveX 홈페이지에서 item이라 표현하넹..)

 

 

 

 

이런 식으로!!! 

그럼 다시 이해할 수 없는 단어 조합으로 돌아가서,

Observable을 비동기 이벤트의 "시퀀스"를 생성할 수 있는 대상 이라 했잖음???

시퀀스라고 한 이유가,

 

 

 

 

이런 식으로 버튼이 여러번 눌리면,

그만큼 Observable은 해당 이벤트에 대한 항목을 순서대로 방출하기 때문임!!

(화살표 방향처럼, 오른쪽으로 갈 수록 더 최근에 눌린 항목이 됨!)

 

정리하자면,

비동기 이벤트를 관찰 가능한 형태로 만든 것이 바로 Observable이고,

이 Observable은 실제 그 비동기 이벤트가 일어났을 때, 이를 알리기 위해

그 이벤트에 대한 항목(item)을 시퀀스로 방출하는구나!

 

그럼 이 비동기 이벤트가 방출하는 항목을 누가 받나염?

바로 Observer가 받아염!!

 

 

 

2-2. Observer

 

Observer는 Observable을 구독합니다

 

좋아요.. 구독... 알람 설정까지..ㅎ

우리가 유튜브를 볼 때, 어떤 유튜버에 대한 영상을 내 피드에서 보고 싶을 때

그때 해당 유튜버를 구독하잖음??

 

이와 마찬가지로,

Observer는 내가 원하는 비동기 이벤트를 방출하는 Observable이 있을 때,

해당 "Observable을 구독"을 할 경우,

Observable이 비동기 이벤트가 실행 되어 항목(item)을 방출 했을 때,

항목을 받을 수 있음!!

 

유튜브는 구독 버튼이라도 있는데..

Observer는 도대체 Observable 구독을 어떠케 하져,,?

 

이는 ReactiveX 공식문서를 보면,

 

 

 

 

Observer가 Observable을 구독할 수 있게 해주는 것이 바로   Subscribe  라는메서드임!!

또한 Observer가 이 Subscribe를 통해 Observable을 구독 했을 때,

총 세가지 클로저를 파라미터로 넘겨줘서 이벤트를 처리 시킬 수 있는데

Subscribe라는 메서드의 원형을 보면,

 

 

public func subscribe(
    onNext: ((Element) -> Void)? = nil,
    onError: ((Swift.Error) -> Void)? = nil,
    onCompleted: (() -> Void)? = nil,
    onDisposed: (() -> Void)? = nil
) -> Disposable

 

 

위와 같이 onNext, onError, onCompleted라는 파라미터가 있고(onDisposed는 설명 생략)

모두 타입이 클로저 타입인 것을 알 수 있음

자, 우리가 기존에 어떤 비동기 이벤트를 넘기고 싶을 때 클로저로 넘겼잖음??

같은 맥락임

 

Observer는

내가 구독하려는 Observable이 항목(item)을 방출 했을 때 어떤 작업을 할 것인지,

만약 해당 Observable이 오류가 발생 했을 때는 어떤 작업을 할 것인지,

더이상 이벤트가 발생하지 않고 종료됐을 때는 어떤 작업을 할 것인지

subscribe의 메서드 파라미터를 통해 클로저로 같이 넘겨주는 것임!!

 

 onNext

Observable이 새로운 항목(item)을 방출할 때마다 이 클로저가 호출 됩니다

이때 Observable이 방출하는 항목을 클로저의 파라미터로 전달받습니다

 

 onError

Observable은 기대하는 데이터가 생성되지 않았거나 다른 이유로 오류가 발생했을 때,

이 오류를 구독자인 Observer에게 알리기 위해 이 메서드를 호출합니다

 

 onCompleted

Observable은 이벤트가 종료되어 더이상 호출되지 않을 때,

이벤트가 완료 되었음을 구독자인 Observer에게 알리기 위해 이 메서드를 호출합니다

 

으음...

이해가 좀 가세여..? 똑똑똑..

 

정리하자면,

비동기 이벤트를 관찰 가능한 형태로 만든 것이 Observable,

Observable은 해당 비동기 이벤트가 발생했을 때 항목(item)을 방출함

 

관찰 가능한 형태인 Observable을 관찰하는 것이 Observer,

Subscribe(onNext:...) 메서드를 통해 Observable를 구독할 수 있는데, 

Observable에서 항목이 방출됐을 때, 에러가 났을 때, 이벤트가 종료되었을 때에 대한 처리를

클로저로 작성하여 파라미터로 넘겨줌

 

아 앞으로

subscribe(onNext: onError: onCompleted: onDisposed:)는 넘나.. 기니까

subscribe(onNext:...)로 생략해서 말하겠음

 

일단 이론은 이게 끝인데 ....

내가 처음 공부할 때.. 먼 생각이 들었냐면..

 

그럼 Observer는 또 뭔 객체로 생성해줘야 하는 건가..?

그래서 Observable을 구독 하는 건가..?

 

였음

이건 이제

실제 버튼을 클릭했을 때의 코드를 예제로 봐보자

 

 

 

 

3. UIButton 클릭 이벤트로 보는 Observable과 Observer, Subscribe

 

위에서 지겹도록 얘기 했던 것처럼

UIButton의 클릭 이벤트는 비동기 이벤트라

RxSwift에서 Observable이 될 수 있다고 했잖음??

이를 통해 실제 어떻게 사용하는지 흐름을 봐볼 것임!!

 

참고로 RxSwift에는 UIKit에 대한 정보가 없기 때문에,

UI와 관련된 Reactive Programming을 하려면 RxCocoa를 사용해야 함

(그렇다고 RxCocoa만 import 하면 안 되고, RxSwift로 같이 import 해주어야 함)

 

UIButton은 UIKit에 속해있기 때문에

RxCocoa를 import해서 UIButton의 tap에 대한 비동기 이벤트 처리를 다음과 같이 작성할 수 있음

(참고로 여기서 tap 이벤트란, .touchUpInside에 대한 동작임)

 

 

sodeulButton.rx
            .tap
            .subscribe(onNext: {
                print("Observable이 항목을 방출 했다!")
            },
            onError: { error in
                print("에러가 발생 했다!")
            },
            onCompleted: {
                print("해당 이벤트가 끝났다!")
            })
            .disposed(by: disposedBag)

 

 

위처럼 작성하면 되는데,

당장은 disposed에 대한 것은 무시해주세요~~!!!!!!!!

 

위 코드는 지금껏 우리가 이론으로 공부한 내용 그대로인데,

sodeulButton에 대해 tap event가 발생했을 경우,

subscribe(onNext:...)를 통해 "구독"을 해서

해당 Observable이 방출하는 항목에 대해 받을 수 있고,

이때 이 메서드의 파라미터로 

onNext(항목이 방출 됐을 때, 즉 버튼이 눌렸을 때 실행시킬 클로저)

onError(에러가 발생 했을 때 실행시킬 클로저)

onCompleted(이벤트가 종료됐을 때 실행시킬 클로저)

를 각각 넘겨줄 수 있단 것임!!!

 

그러면 다음과 같이,

실제로 버튼이 눌렸을 때(Observable의 항목이 방출 됐을 때)

 

 

 

gif 짤입니다만?

 

 

 

Observer가 등록해놓은 클로저가 실행이 됨!

 

아니.. 근데.. 저는 Observer를 만든 적이.. 없는데...

그냥 Observable이 항목 방출 했을 때 실행할 클로저만 넘겨줬을 뿐인데..

어떻게 Observable이 구독이 되어 항목을 받을 수 있는 것임.??????

 

이거는 말이져

subscribe(onNext:...)로 클로저를 넘겨줄 경우, 해당 메서드 안에서

 

 

 

 

자체적으로 AnonymousObserver란 것을 생성해서,

해당 Observable에 subscribe를 해줌!!

 

결론적으로,

subscribe(onNext:...) 메서드를 사용할 경우,

메서드 내부에서 옵저버를 자체적으로 생성해서

우리가 따로 Observer를 생성하지 않고 파라미터로 클로저만 넘겨줘도

Observable을 구독하여 방출하는 항목을 받을 수 있게 된 것!

 

+ 참고로, Observer를 직접 생성할 수도 있는데

대표적으로 Observable이자 Observer 역할을 하는 Subject가 있음

이건 다음 포스팅에서 Observable에 대해 다룰 때 같이 다루겠음

 

 

 

3-1. 조금..? 많이..? 심화 내용

 

sodeulButton.rx
            .tap
            .subscribe(onNext: {
                print("Observable이 항목을 방출 했다!")
            }
            .disposed(by: disposedBag)

 

 

내가 위 코드를 예를 들었기 때문에

button.rx.tap 까지만 했을 때 Observable이 나오고,

따라서 그 Observable에 대고 구독을 한 것이구나! 라고 생각할 수 있는데

 

실제 button.rx.tap 까지만 했을 경우,

 

 

 

 

리턴 타입은 Observable 타입이 아니라, ControlEvent<Void> 타입임!!

따라서 ControlEvent란 구조체 타입을 살펴보면,

 

 

 

 

ControlEvent 구조체 안에 events란 프로퍼티로 Observable이 존재 하고,

UIButton의 경우 rx.tap 이란 연산 프로퍼티를 통해 ControlEvent의 init 함수가 불려질 때 

.touchUpInside 이벤트에 대한 Observable이 생성되어 이 events 프로퍼티에 들어가게 된 것임!!

 

정리정리 정리!!!!!!! 하자면!!!!!!!!

button.rx.tap을 했을 땐 해당 tap event에 대한 Observable이 리턴 되는 게 아니라,

ControlEvent<Void>라는 타입이 리턴 되고,

그 ControlEvent<Void> 인스턴스 안에 events란 프로퍼티가 있는데,

이 eventes란 프로퍼티가 바로 우리가 버튼 클릭 이벤트를 받고 싶을때 구독해야할 Observable이란 것

 

어.. 근데 그러면

button.rx.tap까지 하면.. Observable 타입이 아닌데..!

어떻게 subscribe를 통해 구독을 한 것임..?

Observer는 Observable을 구독하는 거잖아!

그러면 button.rx.tap.events.subscribe를 해야하는 거 아닌가..!?!? 싶잖음??

응 events 프로퍼티 어차피 접근 제어자 internal이라 접근 못해~

 

이 의문을 풀어봐봅시당

위에서 Observable 타입이 아닌 ControlEvent 타입에 대고

subscribe를 해도 문제가 되지 않았던 이유는!!

ControlEvent 구조체가 어떤 프로토콜을 따르는지 먼저 봐야 됨

 

 

 

 

ControlEvent 구조체는 ControlEventType이란 프로토콜을 따르는데,

ControlEventType이란 프로토콜은 ObservableType이란 프로토콜을 따르고 있음!

 

 

 

이렇게!

근데 이 ObservableType이란 프로토콜은 ControlEvent 구조체만 따르는 게 아니라,

Observable 클래스 또한 이 프로토콜을 따르고 있음

 

뭘 말하고 싶은 거냐면,

 

 

extension ObservableType {
    public func subscribe(
        onNext: ((Element) -> Void)? = nil,
        onError: ((Swift.Error) -> Void)? = nil,
        onCompleted: (() -> Void)? = nil,
        onDisposed: (() -> Void)? = nil
    ) -> Disposable {
        ...
    }
}
 

 

 

우리가 구독할 때 Observable에 대고 사용하던 subscribe(onNext: ...) 메서드는

사실 Observable 클래스의 고유한 메서드가 아니라

 ObservableType이란 프로토콜의 extension에 구현되어 있어서,

 ControlEvent 타입 또한 이 프로토콜을 채택하고 있기에

subscribe(onNext: ...) 메서드를 사용할 수 있던 것임!

 

다시, 정리하자면

button.rx.tap 까지 했을 땐 해당 tap event에 대한 Observable 타입이 반환되는 것이 아니라,

이때 반환되는 것은 ControlEvent<Void> 타입임

다만, ControlEvent<Void> 타입이 내부적으로 ObservableType을 따르기 때문

ObservableType 프로토콜 extension에 정의되어 있는

subscribe(onNext:...) 메서드를 통해 구독을 할 수 있었던 것

이해가 됐으려나..!?

 

근데 그러면 한 가지 의문이 또 들어야 됨

Observable에 대고 subscribe(onNext: ...)를 할 경우..

어떻게 이 Observable 자체에 알아서 구독이 되는 것이고,

ControlEvent에 대고 subscribe(onNext: ...)를 할 경우..

ControlEvent에서 Observable은 events란 프로퍼티로 갖고 있는데..

어떻게 이 events 프로퍼티에 알아서 구독이 되는 것일까..!?

 

를 알아보자!

이 의문을 풀기 위해선.. 

다시 ObservableType 프로토콜에 구현되어 있는 subscribe(onNext:...) 메서드에서

 Observer를 subscribe 하는 부분을 살펴보면 됨

 

 

 

 

마지막에 코드 부분을 보면 disposable을 생성해서 리턴 할 때

이렇게 asObservable이란 메서드를 호출 한 후

observer 자체를 파라미터로 넘기는 subscribe(_:) 메서드를 호출하는 것을 볼 수 있는데,

위 의문이 가능했던 것은 바로 이 asObservable이란 메서드 때문에 가능한 것임!

 

자, 이 asObservable이란 메서드는 어디서 튀어나왔냐면,

Observable도 ControlEvent도 따르는 ObservableType이란 프로토콜을 다시 보면

 

 

 

 

ObservableConvertibleType이라는 프로토콜을 따르는데,

이 ObservableConvertibleType 프로토콜 안에

 

 

 

 

선언되어 있는 메서드임!!!!

따라서 당연히 ControlEvent 구조체도, Observable 클래스도

asObservable이란 메서드를 구현해줘야 하는데,

 

기존 Observable 클래스에선 asObservable 메서드에

 

 

 

 

자기 자신인 self를 리턴하고,

ControlEvent 구조체에선 asObservable 메서드를

 

 

 

 

자신이 갖고 있는 이벤트 Observable인 self.events라는 프로퍼티를 리턴하기 때문에,

Observable에 대고 구독을 하든,

Observable 타입이 아닌 ControlEvent 타입에 대고 구독을 하든

문제 없이 Observable을 구독을할 수 있었던 것이랍니다..!!!!!!

 

+ 아 근데

ObservableType 프로토콜의 extension에

 

 

 

 

asObservable이 기본 메서드로 제공되긴 하는데,,

이 메서드는 언제 쓰는지는 머르겠음..;;

 

 

 

마지막으로 한 가지 더 짚어보자면,

subscribe(_:)subscribe(onNext:...) 메서드는 오버로딩으로 서로 완전 다른 메서드인데,

클로저를 통해 AnonymousObserver를 생성하는 subscribe(onNext:...) 메서드

ObservableType 프로토콜에 extension으로 정의되어 있지만 (프로토콜에는 선언 x)

 

바로 다른 옵저버를 구독시켜버리는 subscribe(_ observer: Observer) 메서드

 

 

 

 

아예 ObservableType 프로토콜 자체에 선언되어 있고,

extension으로 제공도 해주지 않기 때문에(내기준 아무리 찾아봐도 없..음....)

이 메서드의 경우

 

 

 

 

Observable 클래스는 위처럼

rxAbstractMethod라는 추상 메서드를 호출하게 구현 되어 있고,

 

 

 

 

ControlEvent 구조체는 내부 이벤트 Observable의 subscribe(_:)를 사용할 수 있도록

해당 메서드가 각각 구현되어 있음!

따라서 최종적으론, Observable의 subscribe(_:)가 불린다..!

 

아.. Observable subscribe(_:)에서 부르는 추상 메서드 rxAbstractMethod는

더 공부해봐야 되고.. 내용이 좀 깊어질까봐.. 사실 잘도 모르겠음ㅋ

흐름 다루는 건 여기서 그만........하겠음

 

 

왜 ControlEvent에 대해 이렇게 깊게 다루세염..?ㅠ

 하고 생각하실 수 있는데,

앞으로 공부할 내용 중 하나인 Traits에 이 ControlEvent이 속해서

말 나온 김에 좀 자세히 다루고 가자!! 하다가 길어진 것 같음,,ㅠㅠㅠ

 

Observable과 ControlEvent 프로토콜의 내용이 좀 복잡해서 정리를 해봤는데,

 

 

당신이 독수리가 아니라면 확대해서 보시길..

 

 

위에서 말한 주요 프로토콜에 한해 이런 그림이라 보면 될듯......!?

(참고로 위에 정리해둔 메서드는 자주 사용하는 메서드와

의문점을 풀기 위한 메서드 정리일 뿐, 저게 전부는 아닙니당)

 

 

 

.

.

.

 

길고 .. 길다..

흠.. 이번 내용은 개인적으로 조금..? 많이..? 어렵다고 생각해요ㅠㅠ

저도 글 쓰면서 많이 찾아보고 공부하며 썼지만

잘못된 내용이 있을 것 같은 이 기분..?은 뭘까? 왠지 잠을 이룰 쑤가 업서