-
RxSwift - BehaviorSubject vs BehaviorRelayRxSwift 2025. 2. 3. 14:25
안녕하세요. 그린입니다 🍏
이번 포스팅은 RxSwift에서 BehaviorSubject와 BehaviorRelay의 차이에 대해 학습해보겠습니다 🙋🏻
RxSwift를 사용하여 상태를 관리할 때 두 개념 모두 자주 사용되는데요.
두가지 모두 Subject입니다.
어떤 차이를 가지고 있고, 어떤 상황에서 어떤것을 쓰는게 좋을지에 대해 알아볼께요.
BehaviorSubject
BehaviorSubject는 현재 값을 유지하며, 새롭게 구독하는 Subscriber에게 항상 최신 값을 방출하는 Subject입니다.
또한 초기 및 기존 값이 존재하기 때문에 UI 상태 관리 시 유용하게 사용될 수 있죠.
특징으로는 다음과 같습니다.
1️⃣ 초기값 지정
2️⃣ 구독 시 최신 값을 즉시 수신
3️⃣ .onNext(value), .onError(error), onCompleted() 호출 가능
4️⃣ .error 혹은 .completed 시 추가적인 이벤트 방출 없음
한번 사용 예시를 간단히 볼까요?
import RxSwift let disposeBag = DisposeBag() let subject = BehaviorSubject(value: "초기값") subject.subscribe { event in print("Subscriber 1: \(event)") }.disposed(by: disposeBag) subject.onNext("새로운 값") subject.subscribe { event in print("Subscriber 2: \(event)") }.disposed(by: disposeBag) // Subscriber 1: next(초기값) // Subscriber 1: next(새로운 값) // Subscriber 2: next(새로운 값)
이렇게 Subscriber가 두개 존재하고 subject를 모두 옵저빙해요.
이때 출력 결과를 보면, Subscriber2가 구독 시 최신 존재하는 2의 값을 받는것이죠.
이와 비슷한 BehaviorRelay를 살펴볼까요?
BehaviorRelay
BehaviorRelay는 BehaviorSubject를 래핑한 형태로 RxCocoa에서 제공합니다.
큰 차이점으로는 .onError()와 .onCompleted()를 지원하지 않는다는 것이죠.
이 차이점은 UI 바인딩에 더 적합하도록 설계된것이에요.
그렇기에 RxCocoa에서 제공하겠죠!?
특징으로는 다음과 같아요.
1️⃣ .onError(), .onCompleted() 없음
2️⃣ 내부적으로 BehaviorSubject를 사용하지만, 구독이 종료되지 않음
3️⃣ .onNext(value) 대신, .accept(value)를 사용해 새 값 방출
4️⃣ UI 상태 관리를 위한 최적의 선택
사용 예시도 볼까요?
import RxSwift import RxCocoa let disposeBag = DisposeBag() let relay = BehaviorRelay(value: "초기값") relay.subscribe { event in print("Subscriber 1: \(event)") }.disposed(by: disposeBag) relay.accept("새로운 값") relay.subscribe { event in print("Subscriber 2: \(event)") }.disposed(by: disposeBag) // Subscriber 1: next(초기값) // Subscriber 1: next(새로운 값) // Subscriber 2: next(새로운 값)
BehaviorSubject와 사용과 흐름이 동일하죠?
그럼 이걸 일목요연하게 정리해서 BehaviorSubject와 BehaviorRelay는 어떤 차이가 있을까요?
BehaviorSubject vs BehaviorRelay
특성 BehaviorSubject BehaviorRelay 초기값 설정 O O 최신값 제공 O O .onNext 호출 여부 O X (.accept 사용) .onError 지원 O X .onCompleted 지원 O X UI 바인딩 적합성 ? (에러 시 종료) O 여기서 살펴볼것이 UI 바인딩 적합성인데요.
BehaviorSubject는 물음표로 두었어요.
MVVM에서 ViewModel의 데이터 바인딩 시 BehaviorRelay는 절대 종료되지 않는 스트림을 제공하기에 UI 바인딩에 매우 적합해요.
(왜냐하면, completed, errorr가 없어서 스트림이 강제 종료되지 않습니다.)
예를들어, 테이블 뷰 사용을 볼 수 있는데요.
BehaviorRelay로 보다 안전하게 값 업데이트가 가능합니다.
만약 BehaviorSubject를 사용해 한 번 에러가 발생하거나 완료되면 데이터를 갱신할 수 없는 상태가 될 위험이 있어요.
그래서, BehaviorSubject를 물음표로 두었어요.
반면, 다시 언급하지만 BehaviorRelay는 종료 개념 자체가 없기에 항상 안전하게 데이터 업데이트가 가능합니다.
// BehaviorSubject let subject = BehaviorSubject(value: ["초기 데이터"]) // onError를 호출하면 이후 데이터 바인딩이 중단됨 subject.onError(NSError(domain: "테스트 오류", code: -1, userInfo: nil)) subject.subscribe(onNext: { data in print("데이터 업데이트: \(data)") }).disposed(by: disposeBag) // 새 데이터 추가 subject.onNext(["새로운 데이터"]) // ❌ 에러로 인해 실행되지 않음 // BehaviorRelay let relay = BehaviorRelay(value: ["초기 데이터"]) // 새로운 데이터 추가 relay.accept(["새로운 데이터"]) // ✅ 정상적으로 UI 업데이트됨
accept도 내부적으로 onNext를 호출하지만 onCompleted나 onError를 막아둔 안전한 방식을 취하고 있어요.
BehaviorRelay를 이용해 테이블 뷰와 아래와 같이 안전히 연결해주죠.
let dataRelay = BehaviorRelay(value: ["초기 데이터"]) // 테이블뷰와 바인딩 dataRelay.bind(to: tableView.rx.items(cellIdentifier: "cell")) { _, item, cell in cell.textLabel?.text = item }.disposed(by: disposeBag) // 데이터 추가 dataRelay.accept(dataRelay.value + ["새로운 데이터"]) // ✅ UI 업데이트 정상 동작
결과적으로 테이블뷰의 데이터는 동적으로 변경되며, 새로운 데이터가 추가될 때 UI가 자동으로 업데이트되어야 하는것이 중요해요.
이때 BehaviorRelay는 스트림이 종료되지 않고, 값이 변경될 때만 UI를 갱신하기에 테이블뷰에서 사용하기가 적합하죠.
이 외에도 BehaviorRelay는 싱글턴 패턴과 결합해 앱 전역적인 상태를 관리할 때도 사용하기 유용할 수 있습니다.
그럼 정리해서 언제 어떤것을 적절히 사용하는게 좋을까요?
When use it?
여러가지 기준이 있을 수 있겠지만 크게 네가지로 잡아봤습니다.
1️⃣ UI 상태 관리 위주 - BehaviorRelay
2️⃣ 상태 변경 감지가 필요하지만, 에러 및 완료 이벤트는 불필요 - BehaviorRelay
3️⃣ 특정 시점에 Subject 종료되어야 함 - BehaviorSubject
4️⃣ Observable이 에러를 방출해야 함 - BehaviorSubject
이런것처럼 대부분의 UI 바인딩에서는 사실상 BehaviorRelay를 사용하면 되지만, 스트림을 종료해야 하거나 에러 및 완료 처리를 구분받아 이에 적절히 UI 처리를 해야한다면 BehaviorSubject를 사용하는것이 적절해보입니다 😃
마무리
두 가지 모두 상태를 관리하는 훌륭한 도구입니다.
이에 프로젝트의 요구사항에 맞춰 적절한 도구를 선택하는것이 개발자의 몫일것 같네요!
레퍼런스
ReactiveX - Subject
만약, Subject를 정의했는데, 이를 Subscriber 인터페이스 없이 다른 에이전트에 전달하고 싶다면 그 Subject를 순수 Observable로 리턴하는 asObservable 메서드를 사용하면 된다. 참고
reactivex.io
RxSwift/RxRelay/BehaviorRelay.swift at main · ReactiveX/RxSwift
Reactive Programming in Swift. Contribute to ReactiveX/RxSwift development by creating an account on GitHub.
github.com
'RxSwift' 카테고리의 다른 글
RxSwift - Driver & BehaviorRelay (0) 2025.02.06 RxSwift로 서버 통신하기 (5) 2022.12.19 RxSwift - Time Based Operator (0) 2021.11.17 RxSwift - Combining Operator (0) 2021.11.12 RxSwift - Transforming Operator (0) 2021.11.10