RxSwift는 Observable 패턴과 함수형 연산자를 구현하여 비동기 데이터 스트림을 처리하는 iOS용 리액티브 프로그래밍 라이브러리입니다. ReactiveX(Rx)의 개념을 Swift 언어로 포팅하여 UI, 네트워크 요청, 데이터베이스 및 타이머의 이벤트를 처리하는 통합 API를 제공합니다. ReactiveX, 2025에 따르면 이 라이브러리는 GitHub에서 60,000개 이상의 프로젝트에서 사용되며 네이티브 Combine이 등장하기 전까지 Swift의 리액티브 프로그래밍 표준으로 남아 있습니다. RxSwift는 델리게이트, 클로저 및 알림을 단일 이벤트 처리 체인으로 대체합니다.
핵심 요점
RxSwift는 Microsoft가 2012년 .NET용으로 만든 ReactiveX(Rx) 라이브러리의 Swift 포트입니다. Rx의 핵심 아이디어는 모든 비동기 데이터 소스를 Observable 시퀀스로 표현하고 여기에 함수형 연산자를 적용할 수 있도록 하는 것입니다. iOS 개발에서 RxSwift는 UI 이벤트(버튼 탭, 텍스트 입력, 제스처)를 명시적인 델리게이트와 target-action 없이 애플리케이션 로직에 바인딩하는 데 사용됩니다.
라이브러리는 세 가지 모듈로 구성됩니다: RxSwift(코어 — Observable, Operator, Scheduler), RxCocoa(UIKit 통합 — UIButton, UITextField, UITableView용 rx 확장), RxRelay(종단 이벤트가 없는 Subject). 이러한 분리를 통해 코어를 서버 측 Swift와 테스트에서 사용하고 iOS 애플리케이션에만 UI 래퍼를 연결할 수 있습니다.
Stack Overflow, 2025 설문 조사에 따르면 RxSwift는 iOS용 타사 라이브러리 중 상위 5위 안에 듭니다. 인기의 주된 이유는 비동기 패턴의 통일된 처리입니다: URLSession을 통한 네트워크 요청, UIViewPropertyAnimator를 통한 애니메이션, NotificationCenter를 통한 알림, DelegateProxy를 통한 델리게이트 — 이 모든 것이 예측 가능한 라이프사이클을 가진 Observable로 귀결됩니다.
리액티브 프로그래밍은 프로그램이 데이터를 폴링하는 대신 데이터 변경에 반응하는 패러다임입니다. “데이터 가져오기 → 처리 → UI 업데이트”를 작성하는 대신 개발자는 변환 체인을 설명합니다: “데이터가 변경되면 필터를 적용하고, 그다음 map, 그다음 UI 업데이트”. RxSwift는 지연 실행을 통한 Observable 체인으로 이 패러다임을 구현합니다 — 구독자가 나타날 때까지 아무 일도 일어나지 않습니다.
Observable은 시간에 따른 이벤트 시퀀스를 나타내는 RxSwift의 기본 타입입니다. Observable은 세 가지 유형의 이벤트를 보낼 수 있습니다: next(새 값), error(스트림을 종료하는 오류), completed(성공적 완료). 유한 Observable(모든 값을 보낸 후 완료)과 무한 Observable(예: UI 이벤트 — 절대 완료되지 않음)이 있습니다.
Observable의 라이프사이클은 세 단계로 구성됩니다: 생성(create, just, from), 변환(연산자 map, filter, flatMap), 구독(subscribe). 구독이 없으면 Observable은 아무 작업도 수행하지 않습니다 — 지연 시퀀스입니다. 구독 후 Observable은 완료되거나 구독자가 Disposable을 통해 구독을 취소할 때까지 구독자에게 이벤트를 보내기 시작합니다.
import RxSwift
// 배열에서 Observable 생성
let numbers = Observable.from([1, 2, 3, 4, 5])
// 연산자를 통한 변환
let squared = numbers
.filter { $0 % 2 == 0 }
.map { $0 * $0 }
// 이벤트 처리로 구독
let disposable = squared
.subscribe(onNext: { print($0) },
onError: { print("Error: \($0)") },
onCompleted: { print("Done") })
Subject는 Observable(구독 가능)이면서 Observer(이벤트 전송 가능)인 타입입니다. RxSwift는 네 가지 유형의 Subject를 제공합니다: PublishSubject(새 이벤트만), BehaviorSubject(초기값 포함), ReplaySubject(최근 N개 이벤트 버퍼링), AsyncSubject(완료 전 마지막 값만). Subject는 명령형 코드(클로저, 델리게이트)를 리액티브 체인에 통합하는 데 유용합니다.
Disposable은 구독 취소 토큰입니다. 구독자가 더 이상 이벤트를 수신하지 않으려면 disposable.dispose()를 호출합니다. 실제로는 DisposeBag이 사용됩니다: 소유자(예: UIViewController)의 초기화 해제 시 모든 구독을 자동으로 취소하는 Disposable 컬렉션입니다. 이는 수동 구독 관리에서 불가피한 메모리 누수를 방지합니다.
| Subject 유형 | 초기값 | 구독자에게 재전송 | 사용 사례 |
|---|---|---|---|
| PublishSubject | 없음 | 새 이벤트만 | UI 이벤트, 드문 알림 |
| BehaviorSubject | 있음 | 마지막 + 새 이벤트 | 상태, 데이터 스트림 |
| ReplaySubject | 없음 | 버퍼 N + 새 이벤트 | 기록 캐싱 |
| AsyncSubject | 없음 | 마지막만 | 계산 작업 |
| PublishRelay | 없음 | 새 이벤트만 | 오류/완료 없음 |
RxSwift의 연산자는 하나의 Observable을 받아 데이터 스트림을 변환하는 다른 Observable을 반환하는 함수입니다. 연산자를 조합하여 개발자는 중간 변수와 경합 조건 없이 선언적 처리 체인을 구축합니다. 모든 연산자는 지연됩니다: 체인은 설명 시 구축되고 구독 시 실행됩니다.
변환 연산자는 스트림의 각 이벤트를 변경합니다. map은 각 요소에 함수를 적용하고, flatMap은 각 요소의 Observable을 단일 평면 스트림으로 펼치고, scan은 중간 결과를 누적합니다(reduce와 유사하지만 각 단계를 방출). buffer는 시간 또는 수에 따라 요소를 그룹화하고, window는 스트림을 창별로 중첩된 Observable로 나눕니다.
// 연산자 체인 예시: debounce로 검색
searchTextField.rx.text
.orEmpty
.debounce(.milliseconds(300), scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest { query -> Observable<[String]> in
return apiService.search(query)
}
.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { _, item, cell in
cell.textLabel?.text = item
}
.disposed(by: disposeBag)
필터링 연산자는 조건을 만족하는 이벤트만 통과시킵니다. filter는 조건자로 필터링하고, distinctUntilChanged는 이전 값과 다른 값만 통과시키며, take와 takeWhile은 이벤트 수를 제한합니다. skip과 skipWhile은 처음 N개 이벤트 또는 조건이 충족될 때까지의 이벤트를 건너뜁니다. 오류 처리에는 catchError(가로채서 대체)와 retry(오류 시 시퀀스 재시도)가 사용됩니다.
결합 연산자는 여러 Observable을 하나로 결합합니다. combineLatest는 여러 스트림의 최신 값을 튜플로 병합하고, zip은 동일한 인덱스의 요소를 짝짓고, merge는 여러 스트림을 이벤트 도착 순서대로 하나로 결합합니다. withLatestFrom은 메인 스트림의 이벤트를 다른 스트림의 최신 값과 결합합니다 — 현재 상태가 있는 UI 이벤트에 유용합니다.
| 카테고리 | 연산자 | 설명 |
|---|---|---|
| 변환 | map | 각 값을 함수를 통해 변환 |
| 변환 | flatMap | 중첩된 Observable을 평면 스트림으로 펼침 |
| 필터링 | filter | 조건자에 따라 값 통과 |
| 필터링 | distinctUntilChanged | 변경된 값만 통과 |
| 결합 | combineLatest | 2개 이상 스트림의 최신 값을 결합 |
| 결합 | zip | 인덱스별로 요소 짝짓기 |
| 제어 | debounce | 스트림의 멈춤까지 이벤트 지연 |
| 제어 | take | 처음 N개 이벤트를 가져와 스트림 완료 |
RxSwift의 Scheduler는 실행 스레드(또는 큐)에 대한 추상화입니다. Scheduler는 이벤트 생성, 변환 및 구독이 실행될 스레드를 결정합니다. 개발자가 명시적으로 DispatchQueue를 지정하는 GCD와 달리 RxSwift는 두 가지 매개변수를 사용합니다: subscribeOn(Observable이 실행될 스레드)과 observeOn(구독자가 호출될 스레드)입니다.
MainScheduler는 메인 스레드에서 코드를 실행합니다 — UIKit 업데이트에 필수입니다(모든 UI 변경은 메인 스레드에서 발생해야 함). SerialDispatchQueueScheduler는 직렬 GCD 큐를 래핑하고 ConcurrentDispatchQueueScheduler는 동시 큐를 래핑합니다. 백그라운드 작업에는 qos: .background 또는 .utility와 함께 ConcurrentDispatchQueueScheduler가 사용됩니다.
// 일반적인 패턴: 백그라운드 작업 + main에서 UI
apiService.fetchData()
.subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
.observeOn(MainScheduler.instance)
.subscribe(onNext: { data in
self.tableView.reloadData()
self.loadingIndicator.stopAnimating()
})
.disposed(by: disposeBag)
Scheduler의 올바른 사용은 네트워크 요청 및 무거운 계산 중 UI 차단을 방지합니다. subscribeOn은 전체 업스트림에 영향을 미칩니다(observeOn 이전의 모든 연산자는 지정된 스케줄러에서 실행됨). observeOn은 다운스트림을 전환합니다 — observeOn 이후의 모든 연산자는 지정된 스케줄러에서 실행됩니다. 체인 내에는 다양한 변환 단계에서 스레드 간 전환을 위해 여러 개의 observeOn이 있을 수 있습니다.
SerialDispatchQueueScheduler는 이벤트의 순차적 처리를 보장하며 이는 공유 리소스의 스레드 안전성에 중요합니다. 고부하 작업(이미지 처리, JSON 파싱)의 경우 ConcurrentDispatchQueueScheduler를 사용하세요 — RxSwift는 하나의 Observable 내에서 이벤트 순서를 유지하지만 다른 Observable은 병렬로 처리될 수 있습니다.
RxSwift는 iOS 프로젝트에서 리액티브 체인을 통해 UI를 데이터에 바인딩하는 데 가장 자주 사용됩니다. RxCocoa는 모든 표준 UIKit 구성 요소에 대한 rx 확장을 제공합니다: UIButton용 rx.tap, UITextField용 rx.text, UIPickerView용 rx.selectedRow. 이를 통해 @IBAction과 델리게이트를 제거하고 선언적 구독으로 대체할 수 있습니다.
MVVM(Model-View-ViewModel)은 ViewModel이 상태와 비즈니스 로직을 관리하고 View가 ViewModel의 리액티브 속성을 구독하는 아키텍처 패턴입니다. RxSwift는 MVVM에 완벽하게 맞습니다: ViewModel은 데이터용 Observable 또는 Driver를 게시하고 View는 bind를 통해 이를 구독합니다. ViewModel은 View에 대한 참조가 없습니다 — View가 소비하는 Observable을 반환합니다.
class LoginViewModel {
let email = BehaviorRelay<String>(value: "")
let password = BehaviorRelay<String>(value: "")
var isFormValid: Observable<Bool> {
return Observable
.combineLatest(email, password) {
!$0.isEmpty && $0.contains("@") && $1.count >= 6
}
}
}
// ViewController에서:
viewModel.isFormValid
.bind(to: loginButton.rx.isEnabled)
.disposed(by: disposeBag)
네트워크 요청은 RxSwift의 두 번째로 일반적인 사용 사례입니다. URLSession.rx.response는 HTTP 요청을 Observable로 래핑하여 성공 시 (response, data)를, 실패 시 error를 반환합니다. Codable 및 map 연산자와 결합하면 콜백과 각 메서드의 오류 처리 없이 Observable<MyModel>이라는 간결한 리액티브 API 계층을 얻을 수 있습니다. 네트워크 오류 시 retry(3) 연산자가 지수 백오프와 함께 자동으로 요청을 세 번 재시도합니다.
Combine은 iOS 13에서 도입된 Apple의 네이티브 프레임워크입니다. Publisher, Subscriber 및 연산자를 통해 비동기 이벤트 작업이라는 동일한 작업을 해결합니다. 그러나 Combine에는 주요 차이점이 있습니다: Failure 타입을 통한 엄격한 오류 타입 지정, Swift Concurrency(async/await)에 대한 내장 지원, @Published 및 ObservableObject를 통한 SwiftUI와의 통합입니다.
RxSwift는 하위 호환성에서 우위를 점합니다 — iOS 8+를 지원하는 반면 Combine은 iOS 13+가 필요합니다. RxSwift는 더 풍부한 타사 확장 생태계(RxDataSources, RxGesture, RxAnimated)와 상세한 연산자 문서를 보유하고 있습니다. 이전 iOS 버전을 지원하는 프로젝트의 경우 RxSwift가 유일한 선택지로 남아 있습니다.
iOS 13+의 새 프로젝트에서는 Apple이 Combine을 권장합니다. SwiftUI와 긴밀하게 통합되어 있고 바이너리 크기가 작으며 공식 지원을 받습니다. 그러나 기존 RxSwift 코드를 Combine으로 마이그레이션하려면 모든 Observable을 Publisher로 다시 작성해야 하며 RxCocoa 생태계에는 완전한 Combine 대응물이 없습니다 — UIKit에는 모든 UI 구성 요소에 대한 네이티브 Publisher가 없습니다.
| 특징 | RxSwift | Combine |
|---|---|---|
| 최소 iOS | iOS 8+ | iOS 13+ |
| 오류 타입 | Error(모든 유형) | Generic Failure |
| UI 확장 | RxCocoa(UIKit, AppKit) | @Published(SwiftUI) |
| 연산자 | 400+ 연산자 | 약 100개 연산자 |
| Swift Concurrency | 브리지 통해 | 네이티브 지원 |
| 상태 | 타사 | Apple 공식 |
자주 묻는 질문
Observable은 지연 실행이 있는 이벤트 소스입니다. Subject는 Observable이면서 Observer입니다 — 구독하고 수동으로 새 이벤트를 보낼 수 있습니다. Subject는 명령형 코드를 리액티브 체인에 통합하는 데 유용합니다.
RxSwift는 iOS 11-12 지원이 필요하거나, 기존 RxSwift 코드베이스가 있거나, 풍부한 연산자 생태계(Combine의 약 100개 대비 400+)가 필요할 때 선택됩니다. iOS 13+의 새 프로젝트에서는 Combine이 선호됩니다.
DisposeBag은 소유자 초기화 해제 시 모든 구독을 자동으로 취소하는 Disposable 토큰 컬렉션입니다. DisposeBag이 없으면 구독이 클로저에 강한 참조를 생성하여 UIViewController가 소멸될 때 메모리 누수가 발생합니다.
오류를 기본값으로 대체하려면 catchError, Observable 실행을 재시도하려면 retry, 오류를 이벤트로 변환하려면 materialize를 사용하세요. UI 계층에서 Driver와 Signal은 오류를 전달하지 않습니다 — 내부적으로 처리됩니다.
Driver는 MainScheduler에서의 실행, 오류 없음 및 리소스 공유를 보장하는 특수 Observable 타입입니다. Driver는 UI 업데이트가 메인 스레드에서 발생하도록 보장합니다. Observable은 그러한 보장을 제공하지 않습니다 — observeOn이 필요합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.