
Reactive Programming은 개발자들 사이에서 점점 인기를 끌고 있는 프로그래밍 패러다임 중 하나이다.
Rx는 또 'Reactive Extensions', 비동기 데이터 스트림을 처리하는 API의 집합을 말한다.
RxSwift는 Swift언어에 맞게 구현한 라이브러리로, 비동기 코드를 쉽고 간결하게 작성할 수 있게 해주고, UI Event 처리, 네트워크 요청 등 다양한 비동기 작업을 용이하게 한다.
🧐
뭔 말인가?? 배경부터 찬찬히 알아보자.

Rx는 Microsoft에서 만든 라이브러리로, UI 친화적인 extension으로 RxSwift는 RxCocoa를 포함한다.
근데 Combine은 애플이 iOS 13이상에서 지원하는 자체적인 반응형 프로그래밍 라이브러리이다.
둘 다 비동기 데이터 스트림을 처리하고, 반응형 프로그래밍을 구현하기 위해 사용되지만,
RxSwift는 다양한 플랫폼에서 사용할 수 있는 반면,
Combine은 Apple 생태계에 최적화되어 있어 iOS, macOS, watchOS, tvOS 등에서 더 효율적으로 동작한다.
Combine은 Swift의 최신 기능들을 활용하여 더욱 간결하고 강력한 API를 제공하며, 성능 면에서도 최적화되어 있다.
RxSwift의 개념을 이해하면 Combine의 개념을 쉽게 이해할 수 있다!
비동기 프로그래밍은 동기 프로그래밍이랑 반대되는 키워드로,
여러 작업이 동시에 발생할 수 있도록 한다.
예를 들어, 로그인 화면과 기능이 있다고 생각해보자!!

사용자가 아이디, 비밀번호, 버튼, ... 다양한 순서로 작업을 수행할 수 있다.
이런 UI Event(버튼 클릭, 텍스트 입력 ...)은 사용자의 인터랙션에 의해 순서가 보장되지 않으므로, 비동기적으로 처리된다.
그러니까,
유저가 버튼을 누르던지, 아이디에 텍스트를 입력하던지, 하는 이런 이벤트들은 순서가 없다(==비동기적이다)!
💡 기존 비동기 프로그래밍에는 여러가지 방법이 있었다.
여기에 Rx가 추가된다고 생각하자!
위에서 살펴본 것처럼,
버튼 누른다??
아이디 입력한다??
이런 이벤트들은 순서가 없이, 비동기적으로 동작하기 때문에,
얘네들을 각각 관찰하고 있어야, 이벤트가 일어났음을 알고 바로 이벤트를 처리할 수 있다.
여기에서 관찰 당하는 애가 Observable, 관찰해서 일 처리하는 애가 Observer이다.
RxSwift에서는 이벤트를 전달하는 역할을 하는 것을 Observable,
이벤트를 받아서 처리하는 역할을 하는 것을 Observer 라고 한다.
그러니까, 같은 말이지만,
Observable은 관찰 가능한 형태이고,(관찰 당하는 애)
Observer는 그 이벤트를 관찰하고, 이벤트를 받아서 처리한다.
이 둘의 관계는 생산자와 소비자 관계로 비유할 수 있다.
Observable은 다음과 같은 과정을 거쳐 이벤트를 전달한다:
Observable을 정의한 코드를 살펴보자.
/// A type-erased `ObservableType`.
///
/// It represents a push style sequence.
public class Observable**<Element>** : **ObservableType** {
init() {
#if TRACE_RESOURCES
_ = Resources.incrementTotal()
#endif
}
public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
rxAbstractMethod()
}
public func asObservable() -> Observable<Element> { self }
deinit {
#if TRACE_RESOURCES
_ = Resources.decrementTotal()
#endif
}
}
제네릭 클래스로 이루어져있다.
이게 뭐냐!
우리가 비동기 이벤트를,
Observable로 만든다
== 어떤 관찰 가능한 형태로 만든다
== 제네릭 타입의 Observable이라는 클래스 인스턴스를 만든다!
그리고 구독! Subscribe을 해주면서 이벤트 전달이 시작되고, Dispose(구독 취소)로 종료된다.
이 과정을 스트림(Stream) 또는 시퀀스(Sequence) 라고 한다.
예를 들어,
계산기 앱에서는 숫자나 기호 버튼 클릭 이벤트를 Observable이 전달하고, Observer가 이를 받아 화면에 결과를 표시하거나 다른 처리를 한다.
데이터의 흐름(stream, sequence): 시간에 따라서 달라지는 데이터들!
같은 말인데, 비동기 이벤트는 해당 이벤트에 대한 항목을 순서대로 방출한다.
그 순서는 보장❌안되어있고, 이벤트도 언제 일어날지 모르지만, Observable은 그 이벤트에 대한 1트, 2트, 3트, ..이런식으로 순서대로 방출한다는 뜻이다!
Observable은 비동기 이벤트가 일어났을 때, 이걸 알리기 위해서 이벤트에 대한 항목을 sequence로 방출한다.
이 subscribe 메서드도 정의 코드로 살펴보자.
public func subscribe(_ on: @escaping (Event<Element>) -> Void) -> Disposable {
**let observer = AnonymousObserver** { e in
on(e)
}
**return self.asObservable().subscribe(observer)**
}
public func subscribe<Object: AnyObject>(
with object: Object,
**onNext**: ((Object, Element) -> Void)? = nil,
**onError**: ((Object, Swift.Error) -> Void)? = nil,
**onCompleted**: ((Object) -> Void)? = nil,
**onDisposed**: ((Object) -> Void)? = nil
) -> Disposable {
subscribe(
onNext: { [weak object] in
guard let object = object else { return }
onNext?(object, $0)
},
onError: { [weak object] in
guard let object = object else { return }
onError?(object, $0)
},
onCompleted: { [weak object] in
guard let object = object else { return }
onCompleted?(object)
},
onDisposed: { [weak object] in
guard let object = object else { return }
onDisposed?(object)
}
)
}
각 이벤트에 대한 처리를 정의해두었고, onNext, onError, onCompleted는 모두 클로저로 이루어져있다.
그래서 Observable이 이벤트를 방출할 때, subscribe 메서드와 함께 이벤트 방출 이후, 그 다음 작업을 클로저로 전달한다. 누구한테? Observer한테!
구독(subscribe)한다는 것은
곧 Observer를 생성하고,
그 Observer를 통해 이벤트를 처리하는 것을 의미한다.
내부 코드를 타고타고 가다보면 이벤트 핸들러 클로저까지 볼 수 있다!!!
다시 정리하면,
1. Observer 생성
subscribe 메서드를 호출하면 RxSwift는 내부적으로 Observer를 생성한다. 이 Observer는 이벤트를 처리할 수 있는 클로저를 포함하고 있다.
let observer = AnonymousObserver
->
// AnonymousObserver
final class AnonymousObserver<Element>: ObserverBase<Element> {
typealias EventHandler = (Event<Element>) -> Void
2. Observer와 Observable 연결
생성된 Observer는 Observable에 연결된다. 이 과정에서 Observable은 이벤트가 발생할 때 Observer에게 전달할 준비를 한다.
return self.asObservable().subscribe(observer)
3. 이벤트 발생 및 전달
Observable에서 이벤트가 발생하면, 이 이벤트는 연결된 Observer에게 전달된다.
Observer는 전달받은 이벤트를 eventHandler 클로저를 통해 처리한다.
// AnonymousObserver
override func onCore(_ event: Event<Element>) {
self.eventHandler(event)
}
무한한 Observable Sequence: 계속 이벤트를 전달할 수 있다.
-> UI와 관련된 이벤트(유저가 뭘 입력하든 반응을 줘야된다!)
유한한 Observable Sequence: 계속 이벤트를 전달할 수 있다.
-> 대용량 파일을 다운로드 받는다(다운로드 다 하면 끝)
그래서 사실 Infinite Sequence는 무한한 시퀀스니까, Next로만 이벤트 전달을 하고,
Finite Sequence에서 Next, Error, Complete으로 이벤트 전달함.
📣 Notification: 이벤트를 다시 받을 일이 없으면 노티를 주는 것 -> Dispose 처리
-> Observer가 이벤트 처리를 더이상 안하게 되고 -> 다시 구독하고 -> ...
1) Observable을 생성한다(이벤트 보내는 놈)
2) Subscribe가 되면, Observable이 실행된다.
3) next로 이벤트를 방출한다.(==emit, 전달)
4-1) if 오류 발생: error 이벤트, else: compeleted 이벤트 Notification
-> Sequence 종료(Observable 재사용 불가능)
4-2) ... deinit되는 시점, Sequence가 종료되는 시점에 Dispose
complete, error 이벤트가 전달되면 바로 Dispose !!
=> View 클래스가 deinit되는 시점에 자동으로 dispose, 리소스가 정리된다.
extension Disposable {
/// Adds `self` to `bag`
///
/// - parameter bag: `DisposeBag` to add `self` to.
public func disposed(by bag: DisposeBag) {
bag.insert(self)
}
}
위에서 dispose의 역할이 "메모리 누수를 방지하기 위해!!" 자동으로 구독을 해제하는 것!이라고 다뤘다.
Dispose Bag은 말 그대로, 이런 disposable들을 줍줍해서 담는 가방이다.
구독 취소!하고 싶은 Observable이 많아지면,
disposdBag 가방에 담아두고 한번에 구독 취소 하는거다.

private var disposables = [Disposable]()
요렇게 배열 가방에 줍줍한 것들을 담아서 한번에 구독 취소하는 것!!
-> 정확히는 DisposeBag이 deinit될 때 배열에 있는 모든 Disposable 객체들을 dispose 한다!
좀 자세히 추가해두었지만,
https://velog.io/@maddie/iOS-RxSwift1-1-Dispose%EA%B0%80-%EB%AD%98%EA%B9%8C
다음 글에서 이어집니다~~
자주 쓰던 반복문이나 조건문 말고 새로운 Rx만의 관점에서 바라보자!
-> 연산자 관련 내용은 다음 포스트에 자세히 있습니다~!~!!~
Rx프로토콜은 RxSwift로 우리가 코드를 구성할 수 있겠는데,
이 프로토콜을 살펴보면 마지막에

import Foundation
/// Extend NSObject with `rx` proxy.
extension NSObject: ReactiveCompatible { }
결국 ViewController, UIView, ...의 제일 최상위 부모뷰인 NSObject에서 이 프로토콜을 쓴다. 그래서 모든 UI 요소에 rx를 붙여서 접근할 수 있다.
UIKit에 RxSwift를 사용한 간단한 예시이다.
UIButton의 탭 이벤트를 구독하고 UILabel에 표시해보자.
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
var button: UIButton!
var label: UILabel!
var disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
// Observable 생성
button.rx.tap
.subscribe(onNext: { [weak self] _ in
self?.label.text = "Button tapped!"
})
.disposed(by: disposeBag)
}
}
여기서 button.rx.tap은 Observable!
사용자가 버튼을 탭할 때마다 next 이벤트를 방출한다.
subscribe(onNext: { ... })는 Observer!
Observable이 방출하는 이벤트를 받아서 처리한다. 여기서는 레이블의 텍스트를 변경한다.
마지막으로, disposed(by: disposeBag)을 사용하여 구독을 관리하고, 뷰 컨트롤러가 사라질 때 자동으로 구독을 해제한다.
모든 Rx의 끝은 저 가방으로 끝날 것..
UI Event는 위에서 배운대로, Infinite Sequence여서 onError, onComplete는 쓰지 않아도 되겠다.

요 on만 있는 애가 UIEvent(Infinite Sequence 어쩌고) 이다!
저도 공부를 비동기적으로 하고 있는데 말이죠ㅎㅎ
제 블로그를 관찰하고 계시다면 당근을 흔들어주세요. 🥕
위에서 당당하게
여기서 button.rx.tap은 Observable!
라고 써놨길래 업데이트 해보겠습니다.
엄밀히 따져보면,
button.rx.tap.subscribe 어쩌고
가 있다면,
button.rx.tap <- 은 ControlEvent 타입입니다.
ControlEvent는 ControlEventType 프로토콜을 채택하고 있는 구조체인데,
ControlEvent 안에는
events라는 Observable도 있고,
이벤트가 방출되면 실행되는 init 구문으로, 그 이벤트에 대한 Observable을 생성해서 events 프로퍼티에 넣어주는 코드도 들어있답니다.

그래서 button.rx.tap은 Observable인가?
아주 그렇지는 않고, ControlEvent를 반환하는데, 그 ControlEvent의 events 프로퍼티가 Observable이다!라고 볼 수 있겠습니다.
그러면, button.rx.tap.subscribe 어쩌고는 뭔가?
subscribe는 아까 Observable 클래스에 있던 메서드 아닌가??
그러면 controlevent 뒤에 observable 클래스 메서드를 쓰는건가??
짬뽕인가??
싶지만,
Observable: ObservableType
ControlEvent: ControlEventType: ObservableType
결국 공통적으로 ObservableType 프로토콜을 채택하고 있다.
subscribe는 Observable의 메서드가 아니라, ObservableType 프로토콜에 구현되어 있기 때문에,
button.rx.tap.subscribe가 가능하다고 보는 게 좋겠다.
그리고 투비 컨티뉴
샤라웃 투 소들이님
https://babbab2.tistory.com/185