Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
Today
Total
관리 메뉴

J

[RxSwift] Subscribe vs. Bind vs. Drive 본문

RxSwift

[RxSwift] Subscribe vs. Bind vs. Drive

yujaehui 2024. 5. 31. 18:49

1. subscribe, bind, drive 특징 비교

  subscribe bind drive
사용 대상 Observable Observable, Driver Driver
에러 처리 에러를 처리 가능 에러 처리하지 않음 에러 처리하지 않음
Thread 관리 별도의 Thread 관리가 필요 (Main Thread에서 동작하지 않음) Main Thread에서 자동으로 동작 Main Thread에서 자동으로 동작
주요 사용 사례 모든 Observable에서 값을 직접 처리할 때 주로 UI 바인딩에 사용, UI 요소와 데이터를 연결 UI 바인딩에 최적화된 Driver 사용 시
주요 이벤트 next, completed, error next (에러 이벤트는 전달되지 않음) next (에러 이벤트는 전달되지 않음)
Hot/Cold
Observable
Cold/Hot Observable 모두 처리 가능 Cold/Hot Observable 모두 처리 가능 Hot Observable인 Driver만 사용 가능
메모리 관리 disposed(by:) 또는 직접 해제 필요 disposed(by:)를 통해 메모리 관리 disposed(by:)를 통해 메모리 관리

2. 사용 예시

subscribe 예시

import RxSwift

let observable = Observable.of(1, 2, 3, 4, 5)
let disposeBag = DisposeBag()

observable.subscribe(
    onNext: { value in
        print("Next:", value)
    },
    onError: { error in
        print("Error:", error)
    },
    onCompleted: {
        print("Completed")
    }
).disposed(by: disposeBag)
  • subscribe는 next, error, completed 이벤트를 모두 처리할 수 있음.

bind 예시

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {
    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        let textField = UITextField()
        let label = UILabel()

        // 텍스트 필드의 입력값을 레이블에 바인딩
        textField.rx.text.orEmpty.bind(to: label.rx.text).disposed(by: disposeBag)
    }
}
  • bind는 Observable을 UI 요소에 직접 연결하여 UI 업데이트를 안전하게 수행.
  • 에러 처리가 필요 없는 UI 작업에서 유용.

drive 예시

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {
    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        let textField = UITextField()
        let label = UILabel()

        // 텍스트 필드의 입력값을 Driver로 변환하여 레이블에 바인딩
        textField.rx.text.orEmpty.asDriver().drive(label.rx.text).disposed(by: disposeBag)
    }
}
  • drive는 Driver를 사용하여 UI 요소와의 바인딩을 안전하게 수행.
  • 에러를 전파하지 않고, Main Thread에서 동작하기 때문에 UI 업데이트에 최적화.

3. 선택 가이드

  • subscribe
    • 에러 이벤트 처리가 필요한 경우 사용.
    • 예를 들어, 네트워크 요청이나 파일 처리 등의 작업에서 발생하는 에러를 직접 처리해야 할 때 적합.
    • Observable 시퀀스의 모든 이벤트(next, error, completed)를 처리할 수 있음.
  • bind
    • UI 요소와 Observable을 간단하게 바인딩하고자 할 때 사용.
    • 에러 처리가 필요 없고, Main Thread에서 동작하는 UI 바인딩에 적합.
    • 에러가 발생해도 작업을 계속 진행해야 할 때 유용.
  • drive
    • UI 바인딩에 최적화된 Driver를 사용할 때 적합.
    • Driver는 Hot Observable이므로, 여러 구독자가 동일한 데이터를 공유할 때 유리 (스트림 공유).
    • 에러 전파 없이 Main Thread에서 안전하게 동작하는 UI 작업에 적합.

4. 정리

subscribe 모든 Observable에서 사용 가능, next, error, completed 이벤트를 처리할 수 있음. 에러 처리가 필요할 때 사용
bind Observable 및 Driver와 UI 요소를 간단히 바인딩, 에러를 처리하지 않으며 Main Thread에서 동작
drive Driver에서만 사용 가능, Main Thread에서 안전하게 동작, 에러 처리 없이 UI 작업에 최적화

(+) bind와 dirve는 도대체 어떤 차이?

bind는 Main Thread에서 동작하지만, 스트림을 공유하지는 않음.
만약 bind를 사용하면서 스트림도 공유하고 싶다면 share 연산자를 사용해야 함.
func bindTest() {
    // tap 이벤트 정의
    let tap = signInButton.rx.tap
        .map { "test \\(Int.random(in: 1...100))" }

    // tap을 각각의 TextField에 바인딩
    tap.bind(to: emailTextField.rx.text)
        .disposed(by: disposeBag)
    tap.bind(to: passwordTextField.rx.text)
        .disposed(by: disposeBag)
    tap.bind(to: nicknameTextField.rx.text)
        .disposed(by: disposeBag)

    // 문제: 구독이 3번 일어나면서 각기 다른 값이 출력됨 (이벤트가 3번 발생)
    // 해결: stream을 공유해서 동일한 이벤트를 전달하고 싶다면 `share()` 사용

    let sharedTap = signInButton.rx.tap
        .map { "test \\(Int.random(in: 1...100))" }
        .share()

    // 이제 tap 이벤트는 동일한 값이 각 필드에 전달됨
    sharedTap.bind(to: emailTextField.rx.text)
        .disposed(by: disposeBag)
    sharedTap.bind(to: passwordTextField.rx.text)
        .disposed(by: disposeBag)
    sharedTap.bind(to: nicknameTextField.rx.text)
        .disposed(by: disposeBag)
}

 

하지만, drive는 Main Thread에서 동작하면서, 스트림을 공유.
만약 bind를 사용하면서 스트림도 공유하고 싶다면 share 연산자를 사용해야 함.
func driveTest() {
    // tap 이벤트를 Driver로 변환
    let tap = signInButton.rx.tap
        .map { "test \\(Int.random(in: 1...100))" }
        .asDriver(onErrorJustReturn: "")

    // drive를 사용하여 모든 필드에 동일한 값 전달
    tap.drive(emailTextField.rx.text)
        .disposed(by: disposeBag)
    tap.drive(passwordTextField.rx.text)
        .disposed(by: disposeBag)
    tap.drive(nicknameTextField.rx.text)
        .disposed(by: disposeBag)
}

'RxSwift' 카테고리의 다른 글

[RxSwift] Single  (0) 2024.06.03
[RxSwift] Driver  (0) 2024.05.31
[RxSwift] Subject, Relay  (0) 2024.05.29
[RxSwift] Observable, Observer, Subscribe, Dispose  (0) 2024.05.23