-Today's Learning Content-

  • Driver와 Signal

1. Driver & Signal

내용정리

RxCocoa를 사용하면서 DriverSignal을 많이 사용했는데, 정작 둘의 차이는 잘 모른 채로 사용했던 것 같다.
오늘은 둘의 역할과 차이를 명확히 공부하여 앞으로 어떤 상황에서 어떤 trait을 사용하는 것이 옳은지 알아보려고 한다.

1) Driver

DriverUI 상태 업데이트를 안전하게 처리할 수 있도록 RxCocoa에서 제공하는 특수한 옵저버블 타입이다.

내부를 살펴보면 아래의 소스 코드를 볼 수 있는데,

여기서 핵심 코드는 schedulershare 부분이다.

먼저 scheduler를 살펴보면 SharingScheduler.make()를 getter로 반환하는데, 이 코드 내부를 살펴 보면

이렇게 MainScheduler를 반환하는 코드임을 알 수 있다.
즉, Driver는 이 코드를 통해 항상 메인스레드에서 동작되는 것을 보장하는 것이다. Driver는 애초에 UI 업데이트를 위해 사용하는 옵저버블이므로, 항상 메인스레드에서 동작하는 것이 보장되어 개발자의 일을 하나 줄여주는 것이라고 볼 수 있다.

다음으로 share를 살펴보면 source.share(replay: 1, scope: .whileConnected)이라는 코드를 볼 수 있는데, 이는 옵저버블의 연산자 중 하나인 share를 사용한 코드이다.
즉, Driver는 항상 가장 최근의 값을 저장하여 새로운 구독자가 최신 상태를 받을 수 있도록 지원한다.

원래는 새로운 옵저버(구독자)가 옵저버블을 구독할 때마다 새로운 스트림이 생겨나지만, Drivershare를 사용하여 구독이 1개 이상 존재하는 상황에서만 유지되는 공유 옵저버블 타입임을 명시하는 것이다.
이렇게 하면 UI 이벤트 소스는 하나 뿐이기 때문에 같은 이벤트가 모든 옵저버에게 보여지게 되게 된다. 이 덕분에 Driver는 Hot Observable로 사용되어 즉시 구독이 가능하기도 하다.

또, Driver를 반환하는 asDrive()의 구현 메소드를 보면 아래와 같은데,

여기서 .catchAndReturn(onErrorJustReturn) 코드 덕분에 RxSwift에서 오류가 발생해도 기본값을 반환하고 스트림을 정상적으로 종료한다.
UI 업데이트는 흐름이 끊기면 안되기 때문에, UI 업데이트 과정에서 오류가 발생하더라도 앱이 멈추지 않도록 도와주는 것이다.
즉, Driver는 Error가 발생하더라도 이를 이벤트로 방출하지 않고 기본값을 방출하기 때문에 UI의 흐름이 깨지지 않고 앱이 종료되지 않음을 보장해준다.

이러한 이유들 덕분에 Driver는 UI의 업데이트를 담당하는 옵저버블로 유용히 사용된다.

2) Signal

Signal은 RxCocoa에서 제공하는 특수한 옵저버블 타입으로, 주로 UI 이벤트를 처리할 때 사용된다. 기본적으로 Driver와 유사한 특징을 가지고 있지만, 특정 상황에서 더 적합한 특성을 가지고 있다.

내부 코드를 살펴보면 아래의 소스 코드를 볼 수 있다.

여기서 schedulerDriver와 동일하기 때문에 Signal도 마찬가지로 MainScheduler에서 사용되어 메인스레드에서 동작하는 것을 보장함을 알 수 있다.

다른 점은 share인데, source.share(scope: .whileConnected)로 되어있다는 것은 옵저버(구독자)는 구독 이후의 이벤트만을 받을 수 있다는 것이다.
이는 여러 구독자가 있어도 새로운 스트림을 만들지 않고 자동으로 공유되는 것을 뜻하며, whileConnected 옵션 덕분에, 구독자가 없으면 리소스를 해제하고 새로운 구독자가 생기면 다시 생성되는 자동 관리가 이루어진다.

또, Signal을 만드는 asSignal()을 살펴보면 Driver와 같이 에러를 반환하지 않고 기본값을 반환하도록 처리가 되어있는 모습을 볼 수 있다.

즉, Signal도 UI 이벤트에서 오류가 발생하더라도 흐름을 끊거나 앱을 종료하지 않는 것을 보장한다는 것을 알 수 있다.

3) 공통점

둘의 공통점을 정리하자면 아래와 같다.

  • Main Thread에서 동작

    • UI 업데이트는 항상 메인 스레드에서 실행되어야 한다.
    • Driver와 Signal은 기본적으로 메인 스레드에서 실행을 보장한다.
  • Hot Observable (즉시 구독 가능)

    • 둘 다 Hot Observable이며, 구독이 시작되면 기존 이벤트를 공유한다.
  • Error를 방출하지 않음 (never 에러 전략 사용)

    • Driver와 Signal 모두 UI에 영향을 주는 Observable이므로, Error 이벤트가 발생하면 앱이 멈출 위험이 있다.
    • 따라서 asDriver(onErrorJustReturn:) 또는 asSignal(onErrorJustReturn:)을 사용하면 에러가 발생해도 기본값을 반환하고 계속 실행된다.

4) 차이점

DriverSignal의 차이점을 정리하면 아래와 같다.

특징DriverSignal
상태 유지✅ (Replay 1)❌ (Replay 없음)
UI 이벤트✅ 주로 상태 기반 UI 업데이트(UILabel, TableView)✅ 주로 순간적인 UI 이벤트(버튼 탭, 알림)
특성과거 데이터를 1개 유지하여 새로운 구독자에게 최신 값 전달과거 데이터 유지X, 새로운 구독자는 새로운 이벤트부터 수신
사용 예시UI 상태 업데이트(텍스트, 테이블 뷰 데이터)버튼 클릭, 팝업 표시, 알림

Driver와 Signal의 동작 차이

import RxSwift
import RxCocoa

let disposeBag = DisposeBag()

// 1. Driver 예제 (과거 값 유지)
let driver = Observable.of("A", "B", "C")
    .asDriver(onErrorJustReturn: "Error")

driver.drive(onNext: { value in
    print("Driver: \(value)")
}).disposed(by: disposeBag)

// 2. Signal 예제 (과거 값 유지 X)
let signal = Observable.of("1", "2", "3")
    .asSignal(onErrorJustReturn: "Error")

signal.emit(onNext: { value in
    print("Signal: \(value)")
}).disposed(by: disposeBag)
// 출력 결과
Driver: C   // 가장 최근 값인 "C"만 방출됨 (Replay 1)
Signal: 1
Signal: 2
Signal: 3   // 전체 값이 방출됨 (Replay 없음)

5) Driver VS Signal 선택 기준

1. Driver 사용 시기

  • UI 상태 업데이트가 필요한 경우
  • UITableView, UILabel, UITextField와 같은 UI 요소를 지속적으로 업데이트할 때
  • 사용자가 최신 UI 상태를 항상 유지해야 하는 경우

2. Signal 사용 시기

  • UI에서 순간적인 이벤트(버튼 클릭, 알림)을 처리할 때
  • 사용자가 여러 번 클릭할 수 있는 UI 액션을 처리할 때
  • 이전 값과 상관없이 새로운 이벤트를 받을 때마다 반응해야 하는 경우

6) 결론

DriverSignal도 모두 UI의 이벤트를 감지하고 업데이트 해주기 위해 사용하는 trait이지만, 둘의 특성은 비슷하면서도 달라서 적절할 시점에 사용하는 것이 좋은 것임을 알게 되었다.

두 trait은 모두 메인스레드에서 작동을 보장하는데, 사실 observe(on:)이나 subscribe(on:)으로 스케줄러를 지정하면 메인스케줄러가 아닌 다른 스케줄러, 즉, 다른 스레드에서 실행되도록 설정할 수 있다고 한다.
그러나 이는 권장하는 방법은 아니라고 하니 주의하도록 하자.

결론적으로 RxCocoa를 사용해서 UI 요소를 지속적으로 업데이트 해야한다면 Driver를, 순간적인 UI 이벤트를 처리하려면 Signal을 사용하는 것이 적합하다.

-Today's Lesson Review-

RxCocoaUI 업데이트와 관련된 데이터 바인딩을 진행할 때
무척이나 유용해서 잘 사용했는데,
정작 둘의 차이도 잘 모른 채로 Signal을 남용해왔다.
이제는 이런 실수를 하지 않고 상황에 따라 적절하게 DriverSignal을
잘 사용해야겠다고 생각했다.
profile
이유있는 코드를 쓰자!!

0개의 댓글