[RxSwift] Traits

CastleSilver·2023년 5월 12일
0

Reactive X

목록 보기
7/7
post-thumbnail

공식 홈페이지

개요

Swift는 값의 종류와 값의 관계가 명확해야 하는 언어입니다. 즉 언어 내에서 사용하는 변수, 함수, 객체 등의 값의 종류와 그 값들 간의 관계를 명확히 정의하는 시스템입니다. 이렇게 값과 타입을 명확히 함으로써 프로그램의 정확성과 안정성을 향상시킵니다. Swift의 이러한 강력한 타입 시스템은 컴파일러가 코드를 검증하고 오류를 사전에 방지하는 데 큰 역할을 합니다. 이러한 Swift의 특징은 Rx를 더 직관적이고 간단하게 사용할 수 있게 합니다.

Traits는 기존 옵저버블보다 인터페이스 경계를 넘어 옵저버블 시퀀스 속성을 전달하고 보증하는 데 도움이 되며 문맥적 의미와 구문적 설탕을 제공하고 더 구체적인 사용 사례를 대상으로 합니다.

Traits가 인터페이스를 경계를 넘어선다는 것은 옵저버블 시퀀스의 속성을 인터페이스를 통해 다른 개발자나 모듈과 공유할 수 있다는 것을 의미합니다. 즉, Traits는 옵저버블 시퀀스를 더 추상화하고, 옵저버블 시퀀스의 속성을 명확하게 정의합니다.

이런 이유 때문에 Traits는 완전히 선택적입니다.(Driver와 같은 일부 Traits는 RxCocoa 에만 존재하며 일부는 RxSwift에서 존재합니다. 그러나 필요한 경우 다른 Rx에서도 동일한 원칙을 쉽게 구현 가능합니다.)

동작 예시

struct Single<Element> {
    let source: Observable<Element>
}

struct Driver<Element> {
    let source: Observable<Element>
}

위 코드는 RxSwift에서 제공하는 Single과 Driver 구조체를 정의한 것입니다. Single과 Driver는 각각 읽기 전용의 Observable 시퀀스 속성을 가지고 있습니다.
즉, Single과 Driver는 기존의 Observable 시퀀스를 확장하거나 변형한 것입니다. Single과 Driver는 각각 하나의 이벤트만을 처리하며, 메인 스레드에서 실행되거나, 에러 이벤트를 방출하지 않는 등의 특징을 가지고 있습니다.
Single과 Driver를 생성할 때는, Observable 시퀀스를 생성하는 것과 유사한 방식으로 생성할 수 있습니다. 생성된 Single과 Driver는 각각 하나의 Observable 시퀀스 속성을 가지고 있으며, 이를 통해 각각 하나의 이벤트만을 처리합니다. Single과 Driver를 사용하면 Observable 시퀀스의 특성을 명확하게 정의할 수 있으며, 코드의 가독성과 재사용성을 높일 수 있습니다.

Single과 Driver를 사용하면 Observable 시퀀스의 속성을 보증하고, 코드의 가독성과 재사용성을 높일 수 있습니다. 이러한 구조체들은 Observable 시퀀스를 래핑하고 있으므로, .asObservable() 메서드를 사용하여 기존의 Observable 시퀀스로 변환할 수 있습니다.

RxSwift Traits

Single

Single은 Observable과 유사하지만, 하나의 이벤트만을 처리하며, 이 이벤트는 값 또는 에러 이벤트 중 하나만 발생합니다. Single은 HTTP 요청과 같이, 한 번의 요청에 대해 하나의 응답만을 기대할 때 사용될 수 있습니다. Single은 Observable과 달리, side effect를 공유하지 않으며, 무한한 이벤트 스트림이 아닌 단일한 이벤트만을 처리합니다.

Single을 생성하는 방법은 Observable을 생성하는 것과 유사합니다. Single을 생성하려면, create 메서드를 사용하여 Observable 시퀀스를 생성하고, .success 또는 .failure 이벤트를 방출합니다. 그리고 subscribe 메서드를 사용하여 Single을 구독하고, .success 또는 .failure 이벤트를 처리합니다.

Observable 시퀀스를 Single로 변환하는 방법으로 .asSingle() 메서드를 사용할 수 있습니다.

func getRepo(_ repo: String) -> Single<[String: Any]> {
    return Single<[String: Any]>.create { single in
        let task = URLSession.shared.dataTask(with: URL(string: "https://api.github.com/repos/\(repo)")!) { data, _, error in
            if let error = error {
                single(.failure(error))
                return
            }

            guard let data = data,
                  let json = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves),
                  let result = json as? [String: Any] else {
                single(.failure(DataError.cantParseJSON))
                return
            }

            single(.success(result))
        }

        task.resume()

        return Disposables.create { task.cancel() }
    }
}

위 코드에서는, getRepo 함수를 사용하여 GitHub API를 호출하고, Single을 생성합니다. 이후, subscribe 메서드를 사용하여 Single을 구독하고, .success 또는 .failure 이벤트를 처리하는 코드를 작성합니다. 이를 통해, Single을 사용하여 HTTP 요청 결과를 처리하는 방법을 보여줍니다.

Completable

Completable은 Observable과 유사하지만, 이벤트를 발생시키지 않으며, 완료 이벤트 또는 에러 이벤트만을 방출합니다.
Completable은 어떤 작업이 완료되었는지만을 알고 싶을 때 사용될 수 있습니다. 예를 들어, 로컬 캐시에 데이터를 저장하는 작업이 완료되었는지만을 알고 싶을 때 사용할 수 있습니다. Completable은 Observable과 달리, side effect를 공유하지 않으며, 어떤 요소도 방출하지 않습니다.

Completable을 생성하는 방법은 Observable을 생성하는 것과 유사합니다. create 메서드를 사용하여 Completable 시퀀스를 생성하고, .completed 또는 .error 이벤트를 방출합니다. 그리고 subscribe 메서드를 사용하여 Completable을 구독하고, .completed 또는 .error 이벤트를 처리합니다.

Maybe

Maybe는 Observable과 유사하지만, 단일한 이벤트를 처리하며, 이 이벤트는 값 또는 에러 이벤트 중 하나만 발생합니다. Maybe는 Single과 Completable의 중간 형태로, 하나의 이벤트를 처리할 때 사용됩니다.

Maybe는 세 가지 이벤트 중 하나를 방출할 수 있습니다. 즉, 완료 이벤트, 값 이벤트, 또는 에러 이벤트 중 하나를 방출할 수 있습니다. 이 중 하나의 이벤트가 발생하면, 더 이상의 이벤트는 방출되지 않습니다. 예를 들어, 값 이벤트가 발생하면 완료 이벤트는 방출되지 않습니다.
Maybe는 값 이벤트를 방출할 수 있으므로, Single보다는 더 일반적인 사용 사례를 가집니다. 예를 들어, 네트워크 요청을 수행하고, 결과를 반환하는 작업에서 Maybe를 사용할 수 있습니다.

Maybe를 생성하는 방법은 Observable을 생성하는 것과 유사합니다. create 메서드를 사용하여 Maybe 시퀀스를 생성하고, .success, .completed, 또는 .error 이벤트를 방출합니다. 그리고 subscribe 메서드를 사용하여 Maybe을 구독하고, .success, .completed, 또는 .error 이벤트를 처리합니다.

Observable 시퀀스를 Maybe로 변환하는 방법으로 .asMaybe() 메서드를 사용할 수 있습니다.

RxCocoa Traits

Driver

Driver는 UI 레이어에서 리액티브 코드를 작성하기 위한 가장 복잡한 특징입니다. Driver는 애플리케이션을 구동하는 데이터 스트림을 모델링하는 데 사용됩니다.
Driver는 세 가지 특징을 가지고 있습니다. 즉, 에러가 발생하지 않으며, 메인 스케줄러에서 관찰이 발생하며, side effect를 공유합니다.
Driver는 이름 그대로 애플리케이션을 구동하는 데이터 스트림을 모델링하기 위해 고안되었습니다. 예를 들어, CoreData 모델에서 UI를 구동하거나, 다른 UI 요소의 값을 사용하여 UI를 구동하는 경우에 사용할 수 있습니다.
Driver는 UI 요소와 애플리케이션 로직이 일반적으로 스레드 안전하지 않기 때문에, 메인 스레드에서 관찰되어야 합니다. 또한, Driver는 side effect를 공유하기 때문에, HTTP 요청과 같은 side effect가 있는 작업을 여러 번 수행하지 않고, 결과를 모든 UI 요소에게 재생할 수 있습니다.

let results = query.rx.text
    .throttle(.milliseconds(300), scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
    }

results
    .map { "\($0.count)" }
    .bind(to: resultCount.rx.text)
    .disposed(by: disposeBag)

results
    .bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .disposed(by: disposeBag)

위 코드에서는, query의 값을 입력받아, 서버에서 검색 결과를 가져오는 작업을 수행하고, 결과를 두 개의 UI 요소에 바인딩하는 코드를 작성합니다. 이 코드에서는 flatMapLatest 연산자를 사용하여, 검색어가 변경될 때마다 서버에서 새로운 검색 결과를 가져오도록 합니다. 이를 통해, Driver를 사용하여 UI를 구동하는 방법을 보여줍니다.

Driver를 생성하는 방법은, asDriver 메서드를 사용하여 기존의 Observable 시퀀스를 변환하는 것입니다. asDriver 메서드는, 기존의 Observable 시퀀스를 Driver 시퀀스로 변환합니다. Driver는 Observable과 유사하지만, UI 요소와 애플리케이션 로직이 스레드 안전하게 관찰되고, side effect를 공유하기 때문에, UI 레이어에서 리액티브 코드를 작성하기에 적합합니다.

Driver는 ControlProperty 특징을 상속하고 있으며, asDriver 메서드를 사용하여 기존의 ControlProperty 시퀀스를 Driver 시퀀스로 변환할 수 있습니다. Driver는 에러가 발생하지 않으며, 메인 스케줄러에서 관찰이 발생하며, side effect를 공유하기 때문에, UI 요소와 애플리케이션 로직이 스레드 안전하게 관찰됩니다.

Driver를 사용하여 UI를 구동하는 방법은, flatMapLatest 연산자를 사용하여 검색어가 변경될 때마다 서버에서 새로운 검색 결과를 가져오는 작업을 수행하고, 결과를 두 개의 UI 요소에 바인딩하는 것입니다.

Driver의 장점은, 특히 대규모 시스템에서, UI 요소와 애플리케이션 로직이 스레드 안전하게 관찰되고, side effect를 공유하기 때문에, 안정적인 UI 구현을 보장할 수 있다는 것입니다. 또한, Driver를 사용하면, drive 메서드를 사용하여 UI 요소에 결과를 바인딩할 수 있으며, 이를 통해 컴파일러가 모든 속성이 충족되었는지 검증할 수 있습니다. 이를 통해, 안정적인 UI 구현을 보장할 수 있습니다.

Signal

Signal은 Driver와 유사하지만, 구독 시 최신 이벤트를 재생하지 않는다는 차이점이 있습니다. 그러나 여전히 구독자는 시퀀스의 계산 리소스를 공유합니다.
Signal은 명령형 이벤트를 리액티브 방식으로 모델링하기 위한 빌더 패턴으로 간주할 수 있습니다.

Signal은 다음과 같은 특징을 가지고 있습니다.
• 에러가 발생하지 않습니다.
• 이벤트는 메인 스케줄러에서 전달됩니다.
• 계산 리소스를 공유합니다. (share(scope: .whileConnected))
• 구독 시 최신 이벤트를 재생하지 않습니다.

Signal은 에러가 발생하지 않으며, 이벤트는 메인 스케줄러에서 전달됩니다. 또한, 계산 리소스를 공유하기 때문에, 여러 구독자가 동시에 시퀀스를 구독해도 성능 문제가 발생하지 않습니다. 그러나, 최신 이벤트를 재생하지 않기 때문에, 구독 시점 이전의 이벤트는 구독자에게 전달되지 않습니다.

ControlProperty

ControlProperty는 UI 요소의 속성을 나타내는 Observable/ObservableType의 특징입니다.
ControlProperty는 초기 컨트롤 값과 사용자가 시작한 값 변경을 나타내는 값의 시퀀스만 포함합니다. 프로그래밍 방식으로 값이 변경되는 경우에는 보고되지 않습니다.
ControlProperty의 속성은 다음과 같습니다.
• 에러가 발생하지 않습니다.
• share(replay: 1) 동작을 합니다.
• 상태를 유지하며, 구독 시 (subscribe 호출) 마지막 요소가 생성되었으면 즉시 재생됩니다.
• 컨트롤이 해제되면 시퀀스가 완료됩니다.
• 에러가 발생하지 않습니다.
• 이벤트는 MainScheduler.instance에서 전달됩니다.
ControlProperty의 구현은 이벤트 시퀀스가 메인 스케줄러에서 구독되도록 보장합니다 (subscribeOn(ConcurrentMainScheduler.instance) 동작).

ControlEvent

ControlEvent는 UI 요소의 이벤트를 나타내는 Observable/ObservableType의 특징입니다.
ControlEvent의 속성은 다음과 같습니다.
• 에러가 발생하지 않습니다.
• 구독 시 초기 값을 보내지 않습니다.
• 컨트롤이 해제되면 시퀀스가 완료됩니다.
• 에러가 발생하지 않습니다.
• 이벤트는 MainScheduler.instance에서 전달됩니다.
ControlEvent의 구현은 이벤트 시퀀스가 메인 스케줄러에서 구독되도록 보장합니다 (subscribeOn(ConcurrentMainScheduler.instance) 동작).

profile
우당탕탕 비전공자 개발자

0개의 댓글