티스토리 뷰

아래 예제의 Rest API호출은 Moya를 사용하였습니다.

개발을 하다보면 다음과 같은 문제를 만날 경우가 많다.

요구사항:
"A API를 호출 한 다음 응답으로 받는 Data a를 B API의 파라미터로 넣고 응답이 오면 응답을 화면에 뿌려주세요."


이 경우에 클로저를 사용해 해결 할 수 있다.

func getSome(completion: @escaping (Int) -> Void) {
    getA { [weak self] resultA in
        self?.getB(param: resultA, completion: { resultB in
            completion(resultB)
        })
    }
}
    
func getA(completion: @escaping (String) -> Void) {
    let provider = APIProvider.shared.provider
    provider.request(.aMethod, completion: { result in
        completion(result)
    })
}
    
func getB(param: String, completion: @escaping (Int) -> Void) {
    let provider = APIProvider.shared.provider
    provider.request(.bMethod(param), completion: { result in
        completion(result)
    })
}


또는 RxSwift를 사용하고 있다면 클로저를 Observable로 변환해 다음과 같이 해결 할 수 도 있을것이다.

func getA() -> Observable<String> {
    let provider = APIProvider.shared.provider
    provider.rx.request(.aMethod, callbackQueue: DispatchQueue.global())
        .asObservable()
        .map { result in
            if result.statusCode == 200 {
                return result.toString()
            } else {
                ... //에러처리
            }
        }
        .catch {
            ... // 에러처리
        }
}
    
func getB(param: String) -> Observable<String> {
    let provider = APIProvider.shared.provider
    provider.rx.request(.bMethod(param), callbackQueue: DispatchQueue.global())
        .asObservable()
        .map { result in
            if result.statusCode == 200 {
                return result.toInt()
            } else {
                ... //에러처리
            }
        }
        .catch {
            ... // 에러처리
        }
}
    
func getSome() -> Observable<Int> {
    return Observable.create { [weak self] emitter in
        guard let strongSelf = self else { return Disposables.create() }
        self?.getA.subscribe(onNext: { [weak self] resultA in
            self?.getB(param: resultA).subscribe(onNext: { [weak self] resultB in
                emitter.onNext(resultB)
                emitter.onCompleted()
            })
            .disposed(by: strongSelf.disposeBag)
        })
        .disposed(by: strongSelf.disposeBag)
        return Disposables.create()
    }
}

아래의 예제는 RxSwift를 썻음에도 불구하고 클로저 지옥을 탈출하지 못했다.

이럴 경우엔 Map과 Flat Map을 적절히 사용해주면 클로저 지옥을 탈출할 수 있다.

MapFlatMap은 다음과 같은 역할을 하는 Operator다.

Map

Flat Map

즉 Map은 데이터 스트림 각각의 항목에 특정 변환(?)을 주어 리턴해주는 오퍼레이터이며,
Flat맵은 위와 같이 데이터 스트림 각각의 항목에 변환을 준 후 다시 데이터스트림으로 반환해주는 오퍼레이터이다.

그럼 이녀석들을 이용해 클로저 지옥을 탈출해보자.

func getSome() -> Observable<Int> {
    return getA()
        .map { resultA in
            return resultA
        }
        .flatMap { [weak self] resultA in
            return self?.getB(param: resultA) ?? Observable<Int>.just(-1) // -1이 아닌 Result타입등으로 오류처리를 해주면 더 좋다.
        }
        .map { resultB in
            return resultB
        }
}

이런식으로 코드를 작성해주면 클로저 지옥을 탈출할 수 있다.
간단하게 설명하자면 결과적으로 리턴해주어야하는 타입인 Observable<Int>타입을 만들어가는 과정이며, 데이터 스트림이 방출되면 어떻게 처리할 것인지 map과 flatmap을 이용해 미리 명시해주고 있다.

작성을 하다보면 이런 오류를 만날 때가 있는데

map또는 flatmap에서 한개의 타입이 아닌 여러개의 타입을 리턴하여 다음 클로저에서 받는 타입을 예측하지 못해서 나는 오류이다.
이럴땐

.flatMap { [weak self] resultA -> Result<String, Error> in

와 같이 리턴될 타입을 명시해주면 해결 할 수 있다.

'프로그래밍 > Swift' 카테고리의 다른 글

"Delegate" VS "Parents Object"  (0) 2021.07.26
associatedtype을 이용해 protocol 만들기  (0) 2021.02.23
Optional 과 == 연산자  (0) 2020.11.16
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함