본문 바로가기
Swift

[Swift] Observable(UniCast) vs Subject(MultiCast) { Bind vs Drive }

by Jimmy_iOS 2023. 11. 7.

RxSwift를 공부하다 보면 Observable과 Subject가 나오는데 둘의 차이점이 무엇일까요?

같은 Observable을 여러 Observer에서 구독했는데 어떤 경우에는 같은 값이 오고 어떤 경우에는 다른 값이 오는 이유는 무엇일까요?

Subscribe와 Bind와 Drive의 차이는 무엇일까요?

오늘은 이 궁금증에 대해서 알아보도록 하겠습니다.

Observable(UniCast)

Observable은 RxSwift에서 사용되는 개념으로, 단일 값을 방출하는 일련의 이벤트 스트림입니다.

Observable은 이벤트를 생성하고, 이벤트를 구독하는 Observer들에게 이벤트를 전달합니다.

Observable은 하나의 Observer에게만 이벤트를 전달하는 UniCast 방식입니다.

예를들어 아래와 같은 Random Int를 방출하는 Observable을 생성하고

3개의 다른 Observer가 구독하여 값을 출력하면, 각각 다른 값을 수신하는 것을 확인할 수 있습니다.

이렇게 Observable은 각각 독립적인 스트림을 가지고 있으며, 여러 번의 구독이 발생할 때마다 각각의 스트림에 이벤트가 전달됩니다.

Observable은 subscribe 메서드를 사용하여 옵저버를 구독하고, 옵저버에게 이벤트를 전달합니다.

이벤트는 next, error, completed와 같은 형태를 가지며, next 이벤트는 새로운 값을 방출하고, error 이벤트는 오류를 발생시키고, completed 이벤트는 완료를 나타냅니다.

MultiCast

반면, Subject는 Observable과 유사하지만, 하나의 스트림을 공유하는 MultiCast 방식입니다.

Subject는 Observable과 Observer 역할을 동시에 수행할 수 있으며, 이벤트를 생성하여 구독자들에게 전달할 수 있습니다.

Subject는 subscribe 메서드로 구독자를 추가하고, 이벤트를 구독자들에게 전달합니다.

이렇게 Subject는 하나의 스트림을 공유하기 때문에 여러 구독자에게 동일한 이벤트를 전달할 수 있습니다.

참고로 Subject는 총 4가지가 존재합니다.

  • PublishSubject: 위에서 봤던 subject입니다. Element 없이 빈 상태로 생성되고, subscriber는 subscribe한 시점 이후에 발생되는 이벤트만 전달받습니다.
  • BehaviorSubject: PublishSubject와 유사합니다. 차이점은 반드시 초기 값을 가지고 생성됩니다. subscribe가 발생하면, 발생한 시점 이전에 발생한 이벤트 중 가장 최신의 이벤트를 전달받습니다.
  • ReplaySubject: BufferSize와 함께 생성됩니다. BehaviorSubject와 유사하지만, BufferSize만큼의 최신 이벤트를 전달받습니다.
  • Variable: BehaviorSubject의 Wrapper로 볼 수 있습니다. BehaviorSubject처럼 작동하며, 더 쉽게 사용하기 위해 만들어졌습니다.

그럼에도 불구하고

Observable을 MultiCast로 변환할수는없을까요?

물론 가능합니다.

Observable의 share()로 멀티캐스트로 변환

Observable을 MultiCast로 변환하려면 share() 메서드를 사용할 수 있습니다.

이를 통해 동일한 Observable에 대한 여러 구독이 하나의 스트림을 공유하게 됩니다.

Observable 예시 코드를 살펴보겠습니다.

BasicAPIManager.fetchData()는 URLSession을 기반으로 비동기 작업을 수행하고

“URLSession Succeed”를 출력합니다.

함수를 호출하고 요청을 다른 3개의 옵저버에서 구독하면, 독립적인 스트림에서 메서드가 3번 호출되고 이벤트가 각각 발생하는 것을 확인할 수 있습니다.

UniCast에서 MultiCast로 변환은 간단하게 Observable에 share()메서드만 추가로 붙여주면 되는데요

Share()를 사용해 MultiCast로 변환 예시 코드를 살펴보겠습니다.

위 코드를 실행하면 "URLSession Succeed"가 한 번만 출력되고, 동일한 스트림에서 이벤트 전달이 3번 발생한 것을 확인할 수 있습니다.

이제 UniCast와 MultiCast의 차이점을 알겠습니다.

그런데 중요한건 왜 UniCast와 MultiCast로 구분되어있냐? 인데요

UniCast와 MultiCast로 구분되어 있는지에 대해서는 명확한 이유가 있습니다.

예를 들어, 네트워킹을 통해 받아온 값을 UI 객체에 전달할 때 MultiCast를 사용하면 여러 구독자에게 동일한 이벤트를 전달할 수 있습니다. 이는 UniCast로는 구현하기 어렵거나 복잡한 경우에 유용합니다.

즉, UniCast와 MultiCast는 사용하는 상황과 용도에 따라 다른 동작 방식을 가지고 있으며, 필요에 따라 적절하게 선택하여 사용할 수 있습니다.

그러면 다음으로는 네트워킹을 통해 받아온 값을 UI객체에 전달해주는 예시코드를 살펴보겠습니다.

네트워킹 → UI객체

위 코드와 같이 네트워킹을 통해 받아온 데이터를 Navigation Title에 적용하는 코드를 실행했더니

보라색 경고창과 함께 앱이 크래쉬 되었습니다.

그 이유는 바로,

네트워킹은 MainThread에서 동작하지 않기 때문에 Observer를 MainThread에서 동작하는 코드를 삽입해줘야 합니다.

위와 같이 .observe(on: MainScheduler.instance) 코드를 삽입하면 정상적으로 동작하는 것을 확인할 수 있습니다.

하지만 이렇게 share() 도 쓰고 .observe(on: MainScheduler.instance) 도 쓰는게 정말 귀찮았나봅니다.

그래서 RxSwift에서는 binddrive 메서드를 사용하여 Observable을 UI 요소와 바인딩할 수 있습니다.

Bind

bind 메서드는 스트림에서 발생하는 이벤트를 UI 요소에 직접 바인딩할 수 있는데요

위 코드처럼 subscribe 대신 bind와 RxCocoa에서 제공하는 기능인 rx 키워드를 사용하여 UI 요소와 바인딩할 수 있습니다.

MultiCast를 지원하면서 메인스레드에서 안전하게 에러 핸들링을 통해 앱 크래쉬를 방지하고 싶다면

RxSwift에서 제공하는 Drive 를 사용할 수 있습니다.

Drive

drive 메서드는 Observable의 share() 기능을 포함해 MultiCast를 지원하면서 메인 스레드에서만 작동하도록 보장하여 UI 업데이트에 사용됩니다.

drive 메서드는 UI 요소에 대한 스트림을 안전하게 처리하고, 메인 스레드에서 실행되도록 보장합니다. 또한, drive 메서드를 사용하면 UI 요소의 속성을 바인딩하는 동안 에러가 발생하더라도 앱이 크래시되지 않습니다.


Uploaded by N2T