본문 바로가기

TIL

[TIL] Single에 대한 간단 내용 정리

RxSwift로 네트워크 코드 구성

Single에 대한 설명을 하기 앞서 우선 RxSwift스럽게 구성한 네트워크 통신 코드에 대해 이야기 하고자 합니다.

왜냐하면 이 코드를 기준으로 Single에 대해 설명할 예정이기 때문이죠.

 

다음 코드 스니핏은 RxSwift 스럽게 구성한 네트워크 코드입니다.

static func fetchJoke() -> Observable<Joke> {
        return Observable.create { observer -> Disposable in
        	// 네트워크 통신에 관한 코드 작성..
            return Disposables.create()
        }
    }

 

위 코드는 네트워크 통신을 위해 새로운 Observable을 생성하고, 또 네트워크 통신 결과로 Joke 타입의 인스턴스를 외부로 반환해 주고 있습니다.

 

이러한 코드는 기본적으로 completionHandler 클로저를 통한 반환과는 다르게 콜백지옥(클로저가 겹겹치 중첩되어 가독성이  저하되는 현상) 문제에서 벗어나 가독성을 현저히 높힐 수 있다는 장점이 있습니다.

 

그렇다면 왜 저는 위 코드를 통해 Single을 설명하려고 하는 걸까요?

조금 더 설명드려보겠습니다.

 

자 이제 본격적인 설명을 위해 Alamofire 라이브러리를 사용하여 실질적인 네트워크 통신 코드를 구성해 보겠습니다.

static func fetchJoke() -> Observable<Joke> {
        return Observable.create { observer -> Disposable in
            AF.request(URL(string:  baseUrl)!)
                .validate(statusCode: 200..<300)
                .responseDecodable(of: Joke.self) { response in
                    switch response.result {
                    case .success(let success):
                        observer.onNext(success)
                        observer.onCompleted()
                    case .failure(let failure):
                        observer.onError(failure)
                    }
                }
            return Disposables.create()
        }
    }

 

위 코드를 보시면 네트워크 통신이 성공했을 때, onNext()로 네트워크 통신 결과를 이벤트로 방출해주고 나서 onCompleted() 이벤트로 스트림을 Dispose하여 해제시키고 있습니다.

이와 마찬가지로 네트워크 통신에 실패한 경우 onError()를 통해 스트림을 해제하고 있습니다. 

 

이는 위 코드에 Single을 도입하면 조금 더 코드가 구성화되고, 반응형스럽게 변할 수 있다는 가능성을 내포하고 있다는 것을 의미합니다.

 

자 그럼 Single에 대해 간단히 설명해 보겠습니다.

Single이란?

드!디!어! 고대하던 Single에 대한 설명입니다.

 

우선 ReactiveX에서 제공하는 공식문서를 살펴보겠습니다.

 

위 공식 내용을 보시면 Single은 Observable과 비슷하지만 방출하는 이벤트는 onSuccess와 onError로 2가지만 방출된다고 나와 있습니다.

 

이와 더불어 아래 공식문서를 살펴 보시면 Single은 한번 이벤트를 방출하고 나선 구독을 종료한다고 나와 있습니다.

 

자 그럼  이제 개념도 정리되었으니 위 처음에 구성했었던 네트워트 코드를 재구성해보겠습니다.

Single 적용 후

아래 코드는 Single 적용 후 코드입니다.

다른 부분들은 거의 동일하고 오직 바뀐부분은 이벤트로 방출하는 부분만 개선되었습니다.

 

참 쉽죠?

static func fetchJoke() -> Single<Joke> {
        return Single.create { single -> Disposable in
            AF.request(URL(string:  baseUrl)!)
                .validate(statusCode: 200..<300)
                .responseDecodable(of: Joke.self) { response in
                    switch response.result {
                    case .success(let success):
                        single(.success(success)) // 수정된 부분
                    case .failure(let failure):
                        single(.failure(failure)) // 수정된 부분
                    }
                }
            return Disposables.create()
        }
    }

 

한번 더 리팩토링?(Result Type)

여기서 오류 발생에 대한 처리를 조금 더 첨가하여 위 코드를 한번 더 리팩토링 해보겠습니다.

 

아래 코드를 보시면 반환 타입이 Single로 바뀌었고, 그 안에 방출하는 타입을 Result로 바꾸었습니다.

이 Result 타입 도입의 이점은 기존에  단순히 onError만으로 해제했었던 스트림을 끊지 않고 에러 사항도 외부로 반환해줄 수 있다는 점이 있습니다.

 static func fetchJokeWithSingleResultType() -> Single<Result<Joke, APIError>> { // 개선된 부분
        return Single.create { single -> Disposable in
            
            AF.request(URL(string:  baseUrl)!)
                .validate(statusCode: 200..<300)
                .responseDecodable(of: Joke.self) { response in
                    switch response.result {
                    case .success(let success):
                        single(.success(.success(success))) // 개선된 부분
                    case .failure(let failure):
                        single(.success(.failure(APIError.invalidURL))) // 개선된 부분
                    }
                }
            return Disposables.create()
        }

 

글 읽어 주셔서 감사합니다~!

참조 사이트

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