Moya 서버 통신 - request query를 이용한 get 방식

Juhee Kim·2024년 9월 1일
0

iOS

목록 보기
1/4
post-thumbnail

0. 목표

  • Moya 라이브러리 사용
  • 사용자 ID를 request query로 보내 서버와 통신
  • get 방식 사용
  • 최종: 즐겨찾기 목록(배열) 가져오기

    💡 request query?
    URI 주소 바깥 부분(? 이후)에 변수를 담는 방식
    ex) https🩵://juhee.com/location?memberSeq=1


1. 어떤 통신을 할 것인가: Service API 작성

import Foundation
import Moya

enum LocationAPI {
    case getLocation(memberSeq: Int) // request query에 필요한 memberSeq
}

extension LocationAPI: TargetType {
    var baseURL: URL {
        return URL(string: Config.baseURL)! // 통신할 서버의 기본 주소
    }
    
    var path: String {
        switch self {
        case .getLocation(let memberSeq):
            return "/location" // 상세 주소
        }
    }
    
    var method: Moya.Method {
        switch self {
        case .getLocation:
            return .get // http method
        }
    }
    
    var task: Task {
        switch self {
        case .getLocation(let memberSeq): // ["key": value] 형식으로 파라미터 작성
            return .requestParameters(parameters: ["memberSeq": memberSeq], encoding: URLEncoding.queryString)
        }
    }
    
    var headers: [String: String]? {
        return ["Content-Type": "application/json"]
    }
    
    var sampleData: Data {
        return Data()
    }
}

💥 처음에는 path 리턴값을 return "/location?memberSeq=\(memberSeq)"로 작성했다가 오류가 났다.
🌟 원인은 task 부분에 이미 쿼리 파라미터를 작성해 "/location?memberSeq=1&memberSeq=1"과 같은 형태의 URL이 되었던 것.


2. 받아오는 데이터는 어떤 형식인가: Response Model

JSON 파싱을 위한 모델링이 필요하다.
데이터가 사진과 같이 배열로 들어오기 때문에, 큰 틀과 그 안에 담길 작은 틀(data 배열) 두 개로 모델링했다.

2.1. 큰 틀

import Foundation

struct GenericResponse<T: Codable>: Codable {
    let status: Int
    let message: String
    let data: T
}

2.2. 작은 틀

import Foundation

struct FavLocation: Codable {
    let locationSeq: Int
    let location: String
    let streetName: String
}

typealias GetFavLocationResponse = [FavLocation]

3. 실제 작동부: ViewModel

import SwiftUI
import Moya

final class CreateScheduleViewModel: ObservableObject {
    @Published var favPlaces: [Place] = []
    
    func getFavoriteLocation() {
        let provider = MoyaProvider<LocationAPI>()
        let memberSeq = 1 // 실제 사용 시에는 현재 로그인한 사용자의 memberSeq를 사용해야 합니다.
        
        provider.request(.getLocation(memberSeq: memberSeq)) { result in
            switch result {
            case .success(let response):
                if response.statusCode == 200 {
                    do {
                        let decoder = JSONDecoder()
                        let genericResponse = try decoder.decode(GenericResponse<GetFavLocationResponse>.self, from: response.data)
                        
                        DispatchQueue.main.async {
                            self.favPlaces = genericResponse.data.map { location in
                                Place(location: location.location,
                                      streetName: location.streetName)
                            }
                            print("즐겨찾기 위치 로드 성공: \(self.favPlaces.count)개의 위치를 받았습니다.")
                        }
                    } catch {
                        print("JSON 디코딩 실패: \(error.localizedDescription)")
                    }
                } else {
                    print("서버 오류: \(response.statusCode)")
                    if let json = try? response.mapJSON() as? [String: Any],
                       let detail = json["detail"] as? String {
                        print("상세 메시지: \(detail)")
                    }
                }
            case .failure(let error):
                print("요청 실패: \(error.localizedDescription)")
            }
        }
    }
}

작동부에서 오류가 가장 많이 나기 때문에 오류 확인 메시지는 자세하면 자세할 수록 좋다.

  • case "요청 실패": 서버 접근 불가
  • case "서버 오류": 요청이 서버에 도달했지만 올바르게 처리되지 못함.
    1번으로 돌아가 API 명세서와 내 코드가 올바르게 매칭되는지(http method 등) 확인해보자.
  • case "JSON 디코딩 실패": JSON 파싱 실패.
    2번으로 돌아가 Response 모델링이 적절한지 다시 확인하자. 이것만 해결하면 통신 성공!

💥 실제로 구현하며 URL의 쿼리 파라미터 중복 문제로 서버 오류가, GenericResponse를 만들어두고 사용하지 않고 GetFavLocationResponse로만 JSON 파싱해서 디코딩 실패 오류가 났었다.
🌟 오류 메시지를 통해 어느 단계에서 오류가 발생하는지 깨닫고, API 명세서와 코드를 비교해보니 문제를 해결할 수 있었다.


참고
https://mini-min-dev.tistory.com/110
https://www.youtube.com/watch?v=OKbJLGhUWZI&t=612s

profile
개: 개롭지만 발: 발전하는중

0개의 댓글