Fezravien / re-ios-open-market

REST API 연동을 통한 상품 목록 조회, 상세 조회, 등록, 수정, 삭제 기능을 가진 앱
3 stars 0 forks source link

Model Refactor and Improvement #26

Closed Fezravien closed 3 years ago

Fezravien commented 3 years ago

모델 리펙토링 및 개선하기

모델


리펙토링 및 개선할 부분 및 생각해볼 부분


Response

Request

NetworkManager

Network

Fezravien commented 3 years ago

Response && Request

스크린샷 2021-10-04 오후 1 37 06

Ressponse

// itemList
struct ItemList: Decodable {
    let page: UInt16
    let items: [Item]
}

// Item
struct Item: Decodable {
    let id: UInt
    let title: String
    let descriptions: String?
    let price: UInt
    let currency: String
    let stock: UInt
    let discountPrice: UInt?
    let thumbnails: [String]
    let images: [String]?
    let registrationDate: Double

    enum CodingKeys: String, CodingKey {
        case id, title, descriptions, price, currency, stock, thumbnails, images
        case discountPrice = "discounted_price"
        case registrationDate = "registration_date"
    }
}

Request

ItemModification 스펠링이 틀린거 수정

multipart/form-data로 서버에 요청하기 위해 구조체 내에 연산 프로퍼티를 통해 키/값 형식을 반환하는 프로퍼티 구성 MultiPartForm 프로토콜을 채택하여 상품 등록 / 수정을 같은 타입으로 상품 삭제는 JSONEncoder 이므로 Encodable 프로토콜 채택

// 상품등록
struct ItemRegistration: MultiPartForm {
    let title: String
    let descriptions: String
    let price: UInt
    let currency: String
    let stock: UInt
    let discountedPrice: UInt?
    let images: [Data]
    let password: String

    var asDictionary: [String : Any?] {
        [
            "title": self.title,
            "descriptions": self.descriptions,
            "price": self.price,
            "currency": self.currency,
            "stock": self.stock,
            "discounted_price": self.discountedPrice,
            "images": self.images,
            "password": self.password
        ]
    }
}

// 상품 수정
struct ItemModification: MultiPartForm {
    let title: String?
    let descriptions: String?
    let price: UInt?
    let currency: String?
    let stock: UInt?
    let discountedPrice: UInt?
    let images: [Data]?
    let password: String

    var asDictionary: [String : Any?] {
        [
            "title": self.title,
            "descriptions": self.descriptions,
            "price": self.price,
            "currency": self.currency,
            "stock": self.stock,
            "discounted_price": self.discountedPrice,
            "images": self.images,
            "password": self.password
        ]
    }
}

// 상품 삭제
struct ItemDelete: Encodable {
    let password: String
}
Fezravien commented 3 years ago

Request 생성하기

GET, POST, PATCH, DELETE 오버로딩을 통해 동일한 메소드 명으로 파라미터만 다르게 하여 일관성을 유지했다

    /// GET - 목록 조회
    func createRequest(page: UInt) -> URLRequest? {
        guard let fetchURL = NetworkConstant.itemList(page: page).url else { return nil }
        let request = URLRequest(url: fetchURL)

        return request
    }

    /// GET - 상품 조회
    func createRequest(id: UInt) -> URLRequest? {
        guard let fetchURL = NetworkConstant.item(id: id).url else { return nil }
        let request = URLRequest(url: fetchURL)

        return request
    }

    /// DELETE - 상품 삭제 (JSONEncoder)
    func createRequest<T: Encodable>(data: T, itemID: UInt) throws -> URLRequest? {
        let encodeData: Data

        do {
            encodeData = try encoder.encode(data)
        } catch {
            throw MarketModelError.encoding(error)
        }

        guard let deleteURL = NetworkConstant.delete(id: itemID).url else { return nil }
        var request = URLRequest(url: deleteURL)
        request.httpMethod = "DELETE"
        request.httpBody = encodeData
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        return request
    }

    /// POST, PATCH - 상품 등록/수정 (mulitpart/form-data)
    func createRequest<T: MultiPartForm>(url: URL?, encodeType: T, method: NetworkConstant.Method) throws -> URLRequest {
        guard let url = url else { throw MarketModelError.url }

        switch method {
        case .post:
            return createMultipartFormRequest(url: url, type: encodeType, method: .post)
        case .patch:
            return createMultipartFormRequest(url: url, type: encodeType, method: .patch)
        default :
            throw MarketModelError.createRequest
        }
    }
Fezravien commented 3 years ago

Network URLSession

URLSession 외부에서 의존성을 주입받는다. 핸들러는 Result 타입으로 하여 성공 : 데이터 실패 : 특정 오류

오케이

final class Network: MarketNetwork {
    private let session: MarketSession

    init(session: MarketSession) {
        self.session = session
    }

    func excuteNetwork(request: URLRequest, completion: @escaping (Result<Data?, Error>) -> Void) {
        session.dataTask(with: request) { data, response, error in
            if let _ = error {
                completion(.failure(MarketModelError.network))
                return
            }

            guard let response = response as? HTTPURLResponse else {
                completion(.failure(MarketModelError.casting("HTTPURLResponse")))
                return
            }

            guard (200...299) ~= response.statusCode else {
                completion(.failure(MarketModelError.response(response.statusCode)))
                return
            }

            completion(.success(data))
        }.resume()
    }
}