본문 바로가기

MOBILE/ios

[iOS] RxSwift와 Observable, operator

도대체 RxSwift는 무엇인가?

RxSwift가 무엇인지 알려면 Reactive X가 무엇인지부터 알아야 한다.

Reactive X의 공식 홈페이지를 보면 대문짝만하게 다음 설명이 나와있다.

 

observable steams를 이용한 비동기 프로그래밍을 위한 API

라고 소개하고 있다. 이 문장으로 Reactive X는 비동기 프로그래밍을 위한 API라는 것까지는 알게 되었다.

하지만? observable? stream? 뭔소린지 도대체 알 수가 없다. 이 무수한 갈고리핑을 해결하기 위해 일단 웹 사이트에서 제공해주는 공식 문서들을 살펴보자.

Reactive X 웹 사이트에 있는 문서들을 번역하도록 하겠다! 하지만 번역기를 거치지 않고 직접 하고 있다는 점에 유의 바란다.


Introduction

https://reactivex.io/intro.html

 

Reactive X는 observable sequence를 이용하여 비동기와 event 기반의 프로그래밍을 다루기 위한 라이브러리이다.

 

Reactive X는 또한 data와 event의 흐름을 지원하기 위해 observer pattern을 사용하며, operator를 추가로 제공하고 있다.

이는 저수준의 threading, 동기화 처리, thread-safety, concurrent 자료 구조, non-blocking I/O 와 같은 작업들을 추상화하면서 선언적으로 sequence를 구성할 수 있도록 한다.

 

Observable은 여러 항목의 sequence에 접근하는 가장 좋은 방법으로 격차를 해결한다.

  single Items multiple Items
synchronous T getData() Iterable<T> getData()
asynchronous Future<T> getData() Observable<T> getData()

 

Reactive X는 "functional reactive programming" 으로도 불리지만 이는 오류이다. ReactiveX는 함수형일 수 있고 반응형일 수도 있지만, "functional reactive programming"은 다른 의미이다. 한 가지 주요한 차이점은 바로 functional reactive programming은 시간이 지남에 따라 연속적으로 변화하는 값(continuously)에서 동작하는 반면에, ReactiveX는 시간이 지나도 이산적인 값으로 출력되는 값에서 동작한다.(discrete)

 

한마디로 RxSwift는 이러한 Reactive X를 swift에서 사용할 수 있도록 한 라이브러리인 것이다. 즉 비동기 처리를 사용하기 위한 라이브러리 중 하나이다. 그러면 이러한 RxSwift를 이루는 요소 중 가장 핵심인 Observable이 무엇이며 어떻게 사용하는지 알아보자.


Observable

 

ReactiveX는 observer pattern을 사용한다고 했다. 즉 데이터가 변화하는 것을 관찰하는 observer를 설정하고, 이를 subscribe한 후 다른 일을 처리하고 있다가 observer에게 알림을 받으면 되기 때문이다.

 

공식 문서에 있는 다음 예시를 살펴보자.

https://reactivex.io/documentation/observable.html

1. 비동기 호출 결과를 return 받고 동작을 처리하는 메소드를 정의한다. 이는 Observer의 일부가 된다.

2. observable로 비동기 호출을 정의한다.

3. subscribe를 통해 이를 observable과 연결한다.

 

그럼 일단 observable이 있어야 한다. 이를 생성하는 메소드들을 알아보자.


creating observable operator

create

직접적인 코드 구현을 통해 옵저버 메서드를 호출하여 Observable을 생성한다

 

 

return type

observable 

 

parameter

subscribe: AnyObserver를 Disposable로 반환하는 escaping 클로저이다.

 

설명에 나와있듯이, observer의 onNext, onError, onComplete 메소드를 구현함으로써 observable로 동작하게 된다.

 

다음과 같이 사용한다.

Observable<String>.create { observer in 
    observer.onNext("Hello")
    observer.onCompleted()
    
    return Disposables.create()
}.subscribe(
    onNext: { print($0) },
    onError: { print($0) }, 
    onCompleted: { print("Completed") }, 
    onDisposed: { print("Disposed") }
).disposed(by: disposeBag)

 

 

결과는 다음과 같다.

Hello
Completed

 

이때 .subscribe 메소드는 다음과 같다.

Subscribe 연산자는 Observable와 관찰자로서 연결된다. Observable이 아이템을 배출하는 것을 감시하기 위한 관찰자이며, 또한 Observable로부터 에러나 완료에 대한 알림을 받기 위한 관찰자이다. 이 연산자를 가지고 Observable에 subscribe를 해야 한다.

 

일반적인 구현은 3가지 메소드를 가진다.

onNext

Observable이 아이템을 방출할 때마다 이 메소드가 호출된다. 방출되는 아이템은 이 메소드의 파라미터로 전달된다.

 

onError

데이터 생성에 실패했을 때나 다른 오류를 마주했을 때 Observable이 이 메소드를 호출하여 오류를 알려준다. 이 메소드는 Observable을 중지시키고 onNext 또는 onCompleted와 같은 다른 메소드를 더 이상 호출하지 않는다. 이 메소드는 어떤 에러가 발생했는지 그 종류를 파라미터로 전달받는다.(어떤 때는 Exception 또는 Throwable, 어떤 때는 simple string 이다. 이는 구현에 따라 달라진다.)

 

onCompleted

어떤 에러를 마주하지도 않았을 때, 마지막으로 onNext 호출이 끝나면 Observable은 이 메소드를 호출한다.

 

즉 observable에 대한 동작을 정의하고 싶다면 onNext, 다 끝났을 때의 동작은 onCompleted, 에러 처리는 onError에서 하면 된다는 것이다.


just

객체 하나 또는 객채집합을 Observable로 변환한다. 변환된 Observable은 원본 객체들을 발행한다

 

 

return type

observable 

 

parameter

observable로 전달될 데이터

Just 연산자는 아이템을 아이템을 방출하는 Observable로 변환시킨다.

 

Just는 From과 비슷하지만, From은 배열이나 iteration이 가능한 어떤 것을 하나씩 방출하는 반면 Just는 그냥 배열이나 iteration이 가능한 어떤 것들을 변환하지 않고 하나의 아이템으로써 방출한다.

 

Just에 null을 넣어서 전달한다면, null을 방출하는 Observable을 반환한다. 즉 빈 observable이 생성되는 것이 아니기 때문에 주의해야 하며, 빈 Observable을 생성하기 위해서는 Empty 연산자를 사용해야 한다.

 

즉 데이터 하나를 그대로 전달하고 싶은 경우에는 Just를 사용하는 것이다. 사용법은 다음과 같다.

 

Observable.just(["Hello", "World"])
    .subscribe(onNext: { arr in
        print(arr)
     })
    .disposed(by: disposeBag)

 

Observable.just로 배열 데이터를 가지는 Observable을 바로 생성한다.

그 다음 Observable에 .subscribe 연산자를 사용하여, 어떤 동작을 할지 구현한다. 여기서는 onNext 즉 데이터가 방출될 때의 동작만 구현하였다.

.subscribe 연산자의 결과는 disposable이다. 따라서 이를 관리하기 쉽게 dispose bag에 담는것이 바로 .disposed 메소드이다.

disposable 결과는 onComplete, onError가 실행되면 자동으로 삭제된다. 즉 stream이 끝나면 자동으로 삭제되지만, 중간에 취소를 할때에는 이 disposable 객체를 삭제함으로써 쉽게 작업을 중단할 수 있기 때문에 사용한다.

 

실행 결과는 다음과 같다.

["Hello", "World"]

From

다른 객체나 자료 구조를 Observable로 변환한다

 

 

return type

observable 

 

Observable으로 작업할 때, 다루고자 하는 모든 데이터가 Observable 또는 다른 타입으로 섞여있는 것 보다는 각각이 Observable로 표현되는 것이 더 편하다. 이 From 메소드는 data stream의 전체 생애를 관리하는 연산자이다.

 

예를 들어 Iterable은 synchronous Observable의 한 종류로 생각될 수 있다. 이러한 object들을 Observable로 명확하게 변환함으로써 다른 Observable들과 상호작용할 수 있다.

 

이러한 이유로 많은 ReactiveX는 이 메소드를 구현하고 있으며, 이는 특정 언어에만 구현된 객체나 자료구조를 Observable로 변환하는 것을 가능하게 해준다.

 

즉 just처럼 데이터를 가지고 직접 Observable을 생성하지만, 배열같이 iteration이 가능한 경우는 하나 하나를 전부 Observable로 순서대로 만들어준다는 것이다. 사용법은 다음과 같다.

 

Observable.from(["Hello", "this", "is", "RwSwift"])
    .subscribe(onNext: { str in
        print(str)
    })
    .disposed(by: disposeBag)

 

이 때의 결과는 다음과 같다.

 

Hello
this
is
RxSwift

 

만약에 이를 just로 구현하였다면 결과는 ["Hello", "this", "is", "RwSwift"] 이었을 것이다. 

하지만 From은 배열과 같은 아이템들은 모두 하나씩 Observable로 만들기 때문에 각각 나오게 되는 것이다.

 

이렇게 기본적인 Observable의 사용법을 알아보았다.

다음에는 연산자를 어떻게 활용할 수 있는지 알아보도록 하겠다!