본문 바로가기
Swift

[RxSwift] Observable과 Operator 개념 파헤치기 [1]

by Jimmy_iOS 2023. 7. 13.

Observable

Rx 코드의 기반

  • T 형태의 데이터 snapshot을 '전달' 할 수 있는 일련의 이벤트를 비동기적으로 생성하는 기능
  • 하나 이상의 observers가 실시간으로 어떤 이벤트에 반응
  • 세 가지 유형의 이벤트만 방출

RxSwift은 비동기 이벤트 기반 프로그래밍을 지원하여, iOS 앱 개발에서 매우 유용하다.

이러한 비동기 이벤트 기반 프로그래밍에 가장 중요한 요소 중 하나가 Observable이다.

next, error, completed

Observable은 RxSwift에서 가장 기본적인 개념 중 하나이다.

Observable은 이벤트를 발생시키는 객체로, 이벤트는 3가지 종류가 있다.

enum Event<Element> {
case next(Element) // 시퀀스의 다음 요소
case error(Swift.Error) // 시쿼스 실패와 에러
case completed // 완료로 인해 시퀀스 제거
  1. Next: Observable이 방출하는 가장 일반적인 이벤트이다. 이벤트는 next(Element) 형식으로 발생하며, 이벤트가 방출되면 구독자(subscriber)는 해당 값을 받아들일 수 있다.
  2. Error: Observable이 동작하는 도중 예외가 발생하면 이벤트가 발생하며, error(error) 형식으로 구독자에게 전달된다.
  3. Completed: Observable의 동작이 완료되면 이벤트가 발생한다. 이벤트는 completed() 형식으로 발생하며, 이후에는 더 이상 이벤트가 발생하지 않는다.

Observable은 이벤트를 발생시키는 것 외에도, 이벤트를 조작하거나 변환하는 다양한 기능을 제공한다. 이러한 기능을 통해 데이터를 효율적으로 처리하고, 비동기적으로 처리되는 작업을 보다 쉽게 제어할 수 있다.

그림으로 보면 이벤트의 진행 방향이 우측으로 진행되고 있는데

첫 번째 그림에서는 우측에 막히는 게 없으므로 Next 이벤트를 계속해서 방출할 수 있다.

두 번째 그림에서는 작대기로 진행이 막혀있는데 Completed 이벤트를 방출하여 완전 종료될 수 있다.

세 번째 그림에서는 X표시가 있는데 이는 Error를 방출하여 완전 종료될 수 있다.

 

RxSwift에서는 Observable을 생성하고, 구독자(subscriber)를 등록하고,

이벤트를 전달하는 등의 다양한 작업을 수행할 수 있다.

 

따라서 RxSwift의 Observable은 iOS 앱 개발에서 매우 중요한 역할을 수행하며,

RxSwift을 사용하여 비동기 이벤트 기반 프로그래밍을 보다 쉽게 구현할 수 있다.

실제 활용 예시

네트워킹을 통해 데이터를 받게되는 경우 에러를 방출할 수 있고 작업을 완료할 수 있다.

Network.download(file: "https://www...")
    .subscribe(onNext: { data in
        //임시 파일에 데이터 추가
    },
               onError: { error in
        //사용자에게 에러 표현
    },
               onCompleted: {
        //다운로드 된 파일 사용 
        })

이때, 구독을 통해 onNext, onError, onCompleted의 경우에 따라 실행할 코드를 입력할 수 있다.

Operator(새로운 Observable을 만드는 연산자)

새로운 Observable을 만드는 연산자들 중 대표적으로 Just, Of, From, Defer, Create에 대해서 알아보자

Just

Just 함수는 하나의 요소를 가지는 Observable을 생성한다.

이 함수는 주어진 요소를 방출하고 즉시 종료되는 Observable을 만든다.

let just = Observable<[Int]>.just([1, 2, 3])

just.subscribe {print($0)}
// subscribe를 통해 작업을 수행하면 next, error, completed가 각각 실행된다.
// next([1, 2, 3])
// completed

just.subscribe(onNext: {print($0)})
// subscribe(onNext:)를 통해 작업을 수행하면 next만 실행된다.
// [1, 2, 3]

Of

Of 함수는 가변 인자로 전달된 요소들을 가지는 Observable을 생성한다.
이 함수는 전달된 요소들을 순서대로 방출하고, 마지막 요소가 방출된 후 즉시 종료된다.

let of = Observable<[Int]>.of([1, 2, 3])

of.subscribe {print($0)}
// next([1, 2, 3])
// completed

of.subscribe(onNext: {print($0)})
// [1, 2, 3]

여기까지만 봤을 때 궁금증이 하나 생기는데, justof의 차이가 무엇인가?

just는 하나의 이벤트만 생성하고,

of는 여러 개의 이벤트를 생성한다.

From

From 함수는 배열, Dictionary, Set, Sequence 등의 컬렉션을 전달받아 Observable을 생성한다.

From 함수는 전달된 컬렉션의 요소들을 순서대로 방출하고, 마지막 요소가 방출된 후 즉시 종료된다.

let from = Observable<Int>.from([1, 2, 3])
from.subscribe {print($0)}
// next(1)
// next(2)
// next(3)
// completed

from.subscribe(onNext: {print($0)})
// 1
// 2
// 3

여기까지 봤을 때 궁금증이 하나 또 생기는데, offrom의 차이가 무엇인가?

of는 요소를 하나씩 받아서 방출하고,

from은 배열을 받아서 요소를 하나씩 방출한다.

Deferred

Deferred 함수는 Observable을 생성하는 클로저를 전달받아 Observable을 생성한다.

이 함수는 Observable이 구독될 때마다 클로저를 실행하여 Observable을 생성하고,

해당 Observable을 반환한다.

var toggle: Bool = false

let factory = Observable<String>.deferred {
    toggle.toggle()

    if toggle {
        return Observable.just("true")
    } else {
        return Observable.just("fasle")
    }
}

for _ in 0...3 {
    factory.subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)
}

deferred문의 클로저 안에서 조건문을 만들어 원하는 Observable을 생성해줄 수 있다.

Create

create 연산자는 Observable을 처음부터 생성할 수 있게 해준다.

이를 사용하여 Observable이 방출할 이벤트들의 시퀀스를 정의할 수 있다.

Observer에는 onNext, onError, onCompleted라는 세 가지 메서드가 있다.

 

onNext를 사용하여 값을 방출하고, onError를 사용하여 오류를 방출하며,

onCompleted를 사용하여 시퀀스가 완료되었음을 알릴 수 있다.

 

create로 생성한 Observable을 구독할 때, 일반적으로 subscribe(onNext:onError:onCompleted:) 메서드를 사용하여 Observable에서 값, 오류 또는 완료 이벤트가 방출될 때 실행할 코드를 지정한다.

 

Observable에서 이벤트가 방출될 때 실행할 코드를 지정하려면 subscribe(_:) 메서드를 사용할 수도 있다.

create는 저수준 연산자이므로 주의해서 사용해야 합니다.

 

일반적으로 just, of, from, deferred와 같이 RxSwift에서 제공하는 고수준 연산자를 사용하는 것이 좋다.

Observable.create {
    $0.onNext(1)
        $0.onError(MyError.error)
    $0.onCompleted()
    return Disposables.create()
}
.subscribe {
    print($0)
}
.disposed(by: disposeBag)
// next(1)
// error(error)
// 이때 completed는 출력되지 않는다. error를 발생시키고 시퀀스가 멈췄기 때문이다.

Observable.create {
    $0.onNext(1)
    $0.onCompleted()
        $0.onError(MyError.error)
    return Disposables.create()
}
.subscribe {
    print($0)
}
.disposed(by: disposeBag)
// next(1)
// completed
// 이때 error는 출력되지 않는다. completed를 발생시키고 시퀀스가 멈췄기 때문이다.

위에서 다른 코드를 보면 궁금증이 하나 생기는데

subscribe(_:)subscribe(onNext:)가 별도로 있는것일까?

이유는 바로 switch문을 사용할 필요를 줄여주기 때문이다.

 

subscribe(_:) 를 사용하게 되면 next, error, completed가 모두 실행되기 때문에

원하는 시퀀스만 내가 골라서 실행해 주기 위해서 switch문을 사용해야하는 번거로움이 있는데

subscribe(onNext:onError:onCompleted:)를 사용하면 원하는것만 선택적으로 구현하면 된다.

Observable.create {
    $0.onNext(1)
    $0.onCompleted()
    return Disposables.create()
}
.subscribe(onNext: {
    print($0)
})
.disposed(by: disposeBag)
// 1
// 이때 completed는 출력되지 않는다. onNext만 실행했기 때문이다.

Observable.create {
    $0.onNext(1)
    $0.onCompleted()
    return Disposables.create()
}
.subscribe {
    switch $0 {
    case .next(let next):
        print(next)
    case .error(let error):
        print(error)
    case .completed:
        print("completed")
    }
}
.disposed(by: disposeBag)
// 1
// completed
// switch문을 통해 onNext(1)과 onCompleted()를 걸러줬다.

참고하면 좋을 사이트: RxMarbles