본문 바로가기
iOS/RxSwift

[RxSwift] Single + Extension을 통해 로깅 시스템 구현

by 최지철 2024. 10. 20.
반응형

 

네트워크 응답의 Raw JSON 데이터를 로깅을 할때, 함수를 통해서 네트워크 요청 클래스에서 진행을 하는데 문뜩 Single로 네트워크 통신을 주로(사실은 다) 처리하니까. Single에 Extension으로 추가해서 조금 더 보기 좋게 할 수 있지 않을까? 해서 해보았습니다.

Single Extension 구현

import RxSwift

extension PrimitiveSequence where Trait == SingleTrait, Element: DataResponse {
    /// Raw JSON을 로깅하는 확장 메서드
    /// - Parameter tag: 로그 태그
    /// - Returns: 로깅이 적용된 Single
    func logRawJSON(tag: String = "Raw JSON Response") -> Single<Element> {
        return self.do(onSuccess: { response in
            #if DEBUG
            if let jsonString = String(data: response.data, encoding: .utf8) {
                log.debug("\(tag): \(jsonString)")
            } else {
                log.error("응답 데이터를 문자열로 변환하는 데 실패했습니다.")
            }
            #endif
        }, onError: { error in
            #if DEBUG
            log.error("로깅 중 에러 발생: \(error.localizedDescription)")
            #endif
        })
    }
}

 

위 코드에서는 RxSwift의 PrimitiveSequence를 확장하여 Single 타입에 로깅 기능을 추가하고 있습니다.

주요 부분을 하나씩 살펴보겠습니다.

1. 확장 조건

extension PrimitiveSequence where Trait == SingleTrait, Element: DataResponse
  • Trait == SingleTrait: Single 타입에만 확장을 적용합니다.
  • Element: DataResponse: 응답 데이터가 DataResponse 타입이어야 합니다.

2. logRawJSON 메서드

func logRawJSON(tag: String = "Raw JSON Response") -> Single<Element>
  • tag: 로그에 사용할 태그를 지정할 수 있으며, 기본값은 "Raw JSON Response"입니다.
  • 반환 타입은 로깅이 적용된 Single<Element>입니다.

3. 로깅 구현

return self.do(onSuccess: { response in
    #if DEBUG
    if let jsonString = String(data: response.data, encoding: .utf8) {
        log.debug("\(tag): \(jsonString)")
    } else {
        log.error("응답 데이터를 문자열로 변환하는 데 실패했습니다.")
    }
    #endif
}, onError: { error in
    #if DEBUG
    log.error("로깅 중 에러 발생: \(error.localizedDescription)")
    #endif
})
  • do(onSuccess:onError:): Single의 성공 및 에러 이벤트를 가로채어 추가 작업을 수행합니다.
  • #if DEBUG: 디버그 모드에서만 로깅이 수행되도록 조건부 컴파일을 사용합니다.
  • 성공 시 응답 데이터를 문자열로 변환하여 디버그 로그로 출력합니다. 변환에 실패하면 에러 로그를 출력합니다.
  • 에러 시 에러 메시지를 로그로 남깁니다.

사용 예시

이해를 위한 예시 

import RxSwift
import Alamofire

struct APIService {
    static func fetchData() -> Single<DataResponse> {
        return Single<DataResponse>.create { single in
            let request = AF.request("https://api.example.com/data")
                .response { response in
                    switch response.result {
                    case .success(let data):
                        single(.success(response))
                    case .failure(let error):
                        single(.failure(error))
                    }
                }
            return Disposables.create {
                request.cancel()
            }
        }
    }
}

// 사용 예시
APIService.fetchData()
    .logRawJSON(tag: "API Fetch Data")
    .subscribe(onSuccess: { response in
        // 성공 처리
    }, onFailure: { error in
        // 에러 처리
    })
    .disposed(by: disposeBag)

 

제 실제 프로젝트 코드입니다. 

   func fetchSafeAreaList(start: Coordinate, goal: Coordinate, route: Route) -> Single<SafeAreaListInfo> {
        let startCoord = "\(start.lat),\(start.lon)"
        let goalCoord = "\(goal.lat),\(goal.lon)"
        return network
            .request(.fetchSafeAreaList(start: startCoord, goal: goalCoord, highWayInfo: route.trafast[0].highWayInfos))
            .logRawJSON()// <---- 이친구 입니다.
            .map { response -> SafeAreaDTO in
                do {
                    let decoder = JSONDecoder()
                    let decodedResponse = try decoder.decode([SafeAreaDTO].self, from: response.data) // 배열로 디코딩
                    if let firstSafeArea = decodedResponse.first {
                        return firstSafeArea
                    } else {
                        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: [], debugDescription: "No SafeAreaDTO found"))
                    }
                } catch {
                    log.error("Decoding error: \(error)")
                    if let jsonString = String(data: response.data, encoding: .utf8) {
                        log.error("Received JSON: \(jsonString)")
                    }
                    throw error
                }
            }
            .map { $0.toDomain() }
            .do(onSuccess: { (route) in
                log.debug("response fetchSafeAreaList \(route)")
            }, onError: { error in
                log.error("Error occurred during fetchSafeAreaList request: \(error.localizedDescription)")
            })
    }

 

결과값

 

이렇게 로그가 잘 찍히는걸 확인 할 수 있습니다

반응형