API 네트워크 통신 과정을 통해 RxSwift 이해하기

TMDB에 있는 TV, MOVIE 정보를 가져와 보여주는 과정을 RxSwift와 RxAlamofire를 사용해서 한번 구현해 보려고 한다.

 

모델링 구현

먼저 데이터 모델링 구성을 살펴보면 TV와 MOVIE는 거의 동일하므로 하나만 살펴보도록 하자

import Foundation

struct TVListModel: Decodable {
    let page: Int
    let results: [TV]
}

struct TV: Decodable {
    let name: String
    let overview: String
    let posterURL: String
    let vote: String
    let firstAirDate: String
    
    private enum CodingKeys: String, CodingKey {
        case name
        case overview
        case posterPath = "poster_path"
        case voteAverage = "vote_average"
        case voteCount = "vote_count"
        case firstAirDate = "first_air_date"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        overview = try container.decode(String.self, forKey: .overview)
        let path = try container.decode(String.self, forKey: .posterPath)
        posterURL = "<https://image.tmdb.org/t/p/w500\\(path)>"
        let voteAverage = try container.decode(Float.self, forKey: .voteAverage)
        let voteCount = try container.decode(Int.self, forKey: .voteCount)
        vote = "\\(voteAverage)(\\(voteCount))"
        firstAirDate = try container.decode(String.self, forKey: .firstAirDate)
    }
}

JSON 코드를 바인딩하기 위해 Decodable로 선언한 뒤 API 규칙에 맞게 모델링을 진행하였다.

init을 사용해서 JSON 데이터에서 각 속성을 디코딩해 TV구조체 속성에 할당 할 수 있도록 설정했다.

 

각각 try를 사용해서 오류를 던질 수 있도록 설계해 오류가 발생하여도 이를 쉽게 알아차리고 처리할 수 있도록 구현했다.

 

네트워킹 구현

모델링 구현이 완료되었으니 이제 네트워크 데이터를 받을 수 있도록 구현하자

import Foundation
import RxSwift
import RxAlamofire

class Network<T:Decodable> {
    
    private let endpoint: String
    private let queue: ConcurrentDispatchQueueScheduler
    
    init(_ endpoint: String) {
        self.endpoint = endpoint
        self.queue = ConcurrentDispatchQueueScheduler(qos: .background)
    }
    
    func getItemList(path: String) -> Observable<T> {
        let fullPath = "\\(endpoint)\\(path)?api_key=\\(APIKEY)&language=ko"
        return RxAlamofire.data(.get, fullPath)
            .observe(on: queue)
            .debug()
            .map { data -> T in
                return try JSONDecoder().decode(T.self, from: data)
            }
    }
}

전체 코드는 이렇게 구현하였다 자세하게 세부적으로 살펴보면

private let endpoint: String
private let queue: ConcurrentDispatchQueueScheduler

init(_ endpoint: String) {
  self.endpoint = endpoint
  self.queue = ConcurrentDispatchQueueScheduler(qos: .background)
}

Network<T: Decodable> 제네릭 타입인 T를 사용해 Decodable 를 준수하도록 구현하였고

endpoint에는 요청의 기본 url이 들어가 관리하기 용이하도록 따로 관리하기 위해 지정했다.

 

그 다음 ConcurrentDispatchQueueScheduler 를 활용해 네트워크 작업을 수행할 스케줄러를 설정하였고 백그라운드 스레드에서 작업을 수행하도록 설정했다.

func getItemList(path: String) -> Observable<T> {
    let fullPath = "\\(endpoint)\\(path)?api_key=\\(APIKEY)&language=ko"
    return RxAlamofire.data(.get, fullPath)
        .observe(on: queue)
        .debug()
        .map { data -> T in
            return try JSONDecoder().decode(T.self, from: data)
        }
}

이 메서드를 통해 네트워크 요청을 수행하고 결과를 Observable<T>로 반환하 도록 구현하여 비동기적으로 데이터가 방출될 수 있도록 구현하였고

 

fullPath를 지정해 각 각 원하는 데이터를 별도로 구현해 가져올 수 있도록 매개변수를 추가해 구현하였다.

.map { data -> T in
    return try JSONDecoder().decode(T.self, from: data)
}

그 다음 응답 데이터를 제네릭 타입으로 디코딩 하는 과정을 거쳐야 한다.

 

이 과정을 통해 네트워크 요청을 비동기적으로 처리하고 응답 데이터를 필요한 형식으로 변환하여 사용할 수 있도록 구현하였다.

 

RxSwift, RxAlamofire를 활용해 간결하고 효율적으로 사용할 수 있다고 하지만 아직 그 정확한 내용을 이해하기는 어려운 것 같다..

감이 올랑 말랑..한 상태라고나 할까..

 

일단 오늘은 네트워크 API 구현까지만 기록으로 남겨두려고 한다. 그 다음 VM을 구현하여 데이터를 받고 VC에 해당 내용을 뿌려주는 것 까지 완료했지만 정확하게 완전히 이해가 됐다고 보기 어려우니 차근차근 기록으로 쌓아 담아두자

 

오늘은 여기까지!!!!!!!!!!!

 

Observable을 완벽하게 이해하고 적용하는 그날까지..