💡 request query?
URI 주소 바깥 부분(? 이후)에 변수를 담는 방식
ex) https🩵://juhee.com/location?memberSeq=1
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이 되었던 것.
JSON 파싱을 위한 모델링이 필요하다.
데이터가 사진과 같이 배열로 들어오기 때문에, 큰 틀과 그 안에 담길 작은 틀(data
배열) 두 개로 모델링했다.
import Foundation
struct GenericResponse<T: Codable>: Codable {
let status: Int
let message: String
let data: T
}
import Foundation
struct FavLocation: Codable {
let locationSeq: Int
let location: String
let streetName: String
}
typealias GetFavLocationResponse = [FavLocation]
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 "서버 오류"
: 요청이 서버에 도달했지만 올바르게 처리되지 못함.case "JSON 디코딩 실패"
: JSON 파싱 실패.💥 실제로 구현하며 URL의 쿼리 파라미터 중복 문제로 서버 오류가, GenericResponse
를 만들어두고 사용하지 않고 GetFavLocationResponse
로만 JSON 파싱해서 디코딩 실패 오류가 났었다.
🌟 오류 메시지를 통해 어느 단계에서 오류가 발생하는지 깨닫고, API 명세서와 코드를 비교해보니 문제를 해결할 수 있었다.
참고
https://mini-min-dev.tistory.com/110
https://www.youtube.com/watch?v=OKbJLGhUWZI&t=612s