[Swift ] RxSwift 1탄 - 핵심 기본 개념정리
안녕하세요 조이킴입니다. 🐥
드디어 마주하게된 RxSwift...
러닝커브가 높기로 유명한만큼 개념 정리가 무엇보다도 중요한지라 몇 차례에 걸쳐 나눠 정리해보려 합니다.
RxSwift [Reactive Programming in Swift] 란?
앱은 단순한 그림이 아니라 사용자의 액션, 연결된 데이터의 흐름에 따라 계속해서 변화하는 interaction이 가능한 녀석입니다.
화면에 단순히 뷰객체를 올려놓는 것은 예쁜 그림을 그리는 것에 지나지 않습니다.
우리 앱개발자의 핵심 역할은 어떻게 하면 좀 더 스무스하게 사용자와 앱이 interaction 할 수 있을지를 고민하는 것이라고 볼 수 있습니다.
그렇게 하기 위해서 우리는 '반응형' & '비동기' 프로그래밍을 하게 됩니다. 실시간으로 데이터의 상태 변화 등의 이벤트를 감지하고 변경 사항을 UI 등에 잘 반영시켜 주는 것이죠.
우리가 만드는 앱은 발생되는 이벤트에 따라 실행될 코드의 순서가 정해지기 때문에 '비동기'로 작동하게 됩니다. 이를 위해 closure, addtarget, Notificaton center, GCD, Delegate 패턴 프로토콜 등 다양한 방법으로 구현을 하게 되는데 RxSwift 또한 이런 비동기 프로그래밍을 구성할 수 있게 해주는 API 입니다. 또한, 데이터의 흐름과 변화에 따라 자동으로 코드가 실행될 수 있도록 해주는 반응형 프로그래밍을 지원해줍니다.
전달 가능한 이벤트 3가지
- Next : 정상 이벤트 - emit 방출된다고 표현 // 최신 데이터 방출 등
- Completed: 완료 이벤트 - notification 전달된다고 표현 // 다운로드 완료 등
- Error: 에러 이벤트 - notification 전달된다고 표현 // 디코딩 실패, 네트워크 연결 유실 등
enum Event<Element> {
case next(Element) // next element of a sequence
case error(Swift.Error) // sequence failed with error
case completed // sequence terminated successfully
}
Observable 이벤트 전달자 vs Observer 이벤트 처리자
이벤트가 발생한 것을 감지하고 이를 처리하는 녀석에게 소식을 전달하는 녀석을 Observable이라고 합니다.
이 소식을 받아 이벤트를 처리하는 녀석을 Observer라고 합니다.
이 둘은 상생관계라 subscribe(구독)을 통해 서로 연결이 되고 나서야 이벤트 처리가 시작됩니다. (생각해보면 당연합니다.)
모든 이벤트가 완료되거나 에러가 발생하면 정상종료든 강제종료든 간에 이 연결고리가 끊기는데, 이를 Dispose(해제)라고 합니다.
Infinite Observable Sequence 무한 시퀀스 vs Finite Observable Sequence 유한 시퀀스
이 두가지의 결정적 차이는 이벤트의 계속 방출(emit) 여부입니다. 무한 시퀀스의 경우 종료 이벤트 없이 무한대로 이벤트를 방출합니다. 보통 UI와 관련된 이벤트가 이에 해당됩니다. 유한 시퀀스의 경우 영상 다운로드가 되면 이벤트가 종료되는 등 한정된 횟수로만 이벤트가 방출됩니다.
>> 무한 시퀀스는 complete나 error 이벤트를 전달할 일이 없기에 Next 이벤트만 방출합니다.
subscribe vs bind vs drive
Observable과 Observer를 연결(구독)시키는 방법은 subscribe, bind, drive 로 나눌 수 있겠습니다.
subscribe로 연결할 경우 main thread가 아닌 곳에서 에서 작동할 가능성이 있으므로 UI를 변화시키는 코드는 별도 dispatch를 통해 main thread에서 실행되도록 설정해주어야 하며, 모든 이벤트를 기본으로 받는 구조라 onNext 뿐만 아니라 onComplete와 onError 이벤트까지 전달합니다. 이와 달리 bind나 drive로 연결할 경우 main thread에서 동작하며, onNext만 전달해줄 수 있어 Infinite Observable Sequence인 UI 관련 시퀀스 연결 시 궁합이 아주 좋습니다.
bind와 drive의 차이점은 크게 2가지가 있는데 내용이 길어질 듯하여 별도 페이지에 정리해 두겠습니다.
// .subscribe 사용하여 연결
button
.rx
.tap
.observe(on: MainScheduler.instance) // 이후에 진행하는 코드는 모두 main에서 진행해줘라는 코드.
.subscribe(with: self, onNext: { owner, _ in
owner.label.text = "버튼을 클릭했어요"
}, onDisposed: { owner in
print("disposed")
}).disposed(by: disposeBag)
// .bind 사용하여 연결
button
.rx
.tap
.bind(with: self, onNext: { owner, _ in
owner.label.text = "버튼을 클릭했어요"
}).disposed(by: disposeBag)
Dispose
모든 Observable은 Disposable을 return함으로써 stream을 종료하고, 실행되고 있던 sequence를 종료하게 됩니다.
class Observable<Element> {
func subscribe(_ observer: Observer<Element>) -> Disposable
}
public protocol Disposable {
/// Dispose resource.
func dispose()
}
한정된 횟수의 이벤트를 방출한 후 종료나 에러 이벤트를 전달하여 자체적으로 dispose되는 유한 시퀀스의 경우 dispose() 를 직접 호출하지 않아도 문제는 발생하지 않지만, 계속해서 이벤트를 방출만하는 무한 시퀀스는 별도로 dispose()를 호출해줘야만 리소스를 정리하여 메모리 누수 등을 방지할 수 있습니다. 구독하고 있는 객체가 여럿일 경우 무한시퀀스만 별도로 찾아 일일이 해제 처리를 해주는 것이 번거롭기 때문에 DisposeBag class를 활용하여 한방에 인스턴스를 초기화해서 리소스를 정리할 수 있습니다.
var disposeBag = DisposeBag()
func testJustObservable() {
Observable // 전달
.just(list) // stream . just는 통으로 전달
.subscribe { value in
print("next - \(value)")
} onError: { error in
print("error - \(error)")
} onCompleted: {
print("completed")
} onDisposed: {
print("disposed")
}
.disposed(by: disposeBag)
Observable Stream
데이터의 흐름을 관리하기 위한 핵심 개념인 Observable Stream은 간단히 말해 시간의 흐름에 따라 변경되는 일련의 코드의 집합이라고 할 수 있습니다. 즉, 이벤트를 감지하고 전달하는 Observable, 이벤트를 처리하는 Observer, 앞의 두 녀석들을 연결해주는 구독설정 Subscription, 이벤트를 처리하기 위해 사용되는 연산자 Operators, 이벤트 종료에 따른 구독해제 설정 Dispose까지 일련의 과정을 포함한 흐름 자체를 일컫습니다.
Operators 연산자
RxSwift에는 100여가지가 넘는 연산자가 존재하는만큼 이들은 Observable을 생성하거나, 방출된 이벤트를 처리하는 등 역할이 아주 다양합니다. 다음 포스팅에서 자주 사용되는 연산자들을 정리해보겠습니다.
< 덧 >
RxSwift 라이브러리 구조
자체 제공되는 RxSwift Example Code 활용하기
https://github.com/ReactiveX/RxSwift
GitHub - ReactiveX/RxSwift: Reactive Programming in Swift
Reactive Programming in Swift. Contribute to ReactiveX/RxSwift development by creating an account on GitHub.
github.com
공식 github 페이지에서 제공되는 Example Code를 실행하면 구조 파악에 좀 도움이 됩니다.
테이블뷰 구현하는 법 등 다양한 코드를 제공해주고 있습니다.
RxJS Marbles로 operator 작동 시각적 확인해보기
RxMarbles: Interactive diagrams of Rx Observables
rxmarbles.com
아래처럼 각 Operator 작동이 어떻게 구현되는지 개념 이해에 도움이 되는 시각화된 자료가 업로드 되어 있는 페이지입니다.