Swift - URLSession Request: GET, POST, multipart-form/data
지금 프로젝트에서는 Moya(Alamofire 추상화한 라이브러리)를 사용하고 있는데, 외주 작업하는 데서는 URLSession으로 직접 하더라
multipart-form/data도 별로 다뤄본 적 없고 그래서 이번 기회에 좀 곤란했던 부분들 정리해 봄
기본적인 리퀘스트
그냥 간단하게 추가해 봄니다
GET request
static func getGETRequest(url: String, data: Dictionary<String, Any>) -> URLRequest? {
var urlComponents = URLComponents(string: url)
data.forEach { (key: String, value: Any) in
urlComponents?.queryItems?.append(URLQueryItem(name: key, value: "\(value)"))
}
guard let url = urlComponents?.url else { return nil }
var request = URLRequest(url: url)
request.httpMethod = "GET"
return request
}
GET은 쿼리로 보낸다. 제일 간단
POST request
static func getPOSTRequest(url: String, data: Dictionary<String, Any>) -> URLRequest? {
guard let url = URL(string: url) else { return nil }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let jsonData = try! JSONSerialization.data(withJSONObject: data, options: [])
request.httpBody = jsonData
return request
}
POST는 보통 바디에 json 형식으로 담아 보낸다
이외에도 바디를 json 형식으로 보낼 거면 PATCH면 걍 httpMethod
를 PATCH로 바꾸고 하면 됨
multipart-form/data
파일 데이터 등을 보낼 때 바디를 구성하는 방법이다.
얘는 데이터 전송 방식이 다른 거기 때문에 당연히 POST, PATCH 등 다양하게 결합 가능
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
일단 리퀘스트 생성할 때 httpMethod
알아서 정하고
헤더의 Content-Type
을 multipart/form-data; boundary=\(boundary)
로 지정해 줘야 함
static func getMultipartFormData(boundary: String, params: Dictionary<String, Any>, images: [UIImage]) -> Data {
var data = Data()
let boundaryPrefix = "--\(boundary)\r\n"
let boundarySuffix = "--\(boundary)--\r\n"
data.append(convertParams(params, boundaryPrefix))
data.append(convertImagesData(images, boundaryPrefix))
data.append(boundarySuffix.data(using: .utf8)!)
return data
}
그 다음 데이터(바디)를 얻어줍니다
저거 주의해야 함… 바운더리를 각 파라미터 마다 앞에다 붙이고 마지막에도 붙여주는데 마지막에는 "--"
한 번 더 붙음ㅋㅋ
아무튼 그냥 있는 애들 다 변환해서 붙여주면 된다
그냥 파라미터들 보내기
static func convertFormField(key: String, value: String, _ boundaryPrefix: String) -> Data {
var data = Data()
data.append(boundaryPrefix.data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
data.append("\(value)\r\n".data(using: .utf8)!)
return data
}
static func convertParams(_ params: Dictionary<String, Any>, _ boundaryPrefix: String) -> Data {
var data = Data()
params.forEach { (key, value) in
data.append(convertFormField(key: key, value: "\(value)", boundaryPrefix))
}
return data
}
일단 파일이 아닌 파라미터들의 경우 이런 식으로 하나하나씩 붙여서 보내면 된다.
파일 데이터 보내기
static func convertImagesData(_ images: [UIImage], _ boundaryPrefix: String) -> Data {
var data = Data()
for image in images {
guard let imageData = image.jpegData(compressionQuality: 0.5) else { return data }
let fileName = "\(UUID().uuidString).jpg"
let fieldName = "image"
let mimeType = "image/jpeg"
data.append(boundaryPrefix.data(using: .utf8)!)
data.append("Content-Disposition: form-data; name=\"\(fieldName)\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!)
data.append("Content-Type: \(mimeType)\r\n\r\n".data(using: .utf8)!)
data.append(imageData)
data.append("\r\n".data(using: .utf8)!)
}
return data
}
fileName
: 보낼 파일 이름fieldName
: 파라미터 이름mimeType
: 파일 타입 위 예시는 이미지이고 파일 보낼 때는mimeType
을 바꿔 주면 됨
근데!! 이렇게만 하면 잘 될까요
파라미터가 배열이면
대충 글 쓰기 API가 있어서, title
, tags
, images
등을 보낼 필요가 있었다.
뭐 대충 보내니까 잘 됨… 근데 해당 태그 게시글들 불러오니까 방금 쓴 애가 없네?? 아니, 글 쓰고 나서 리스폰스도 정상적으로 됐다고 오는데 왜 리스트 불러오면 얘가 없지?? 했더니
tags
파라미터의 값이 "["Tag1", "Tag2"]"
같은 형식으로 가고 있었음ㅋㅋ
태그가 각각 따로 "Tag1"
, "Tag2"
로 저장이 안 되고 말그대로 "["Tag1", "Tag2"]"
라는 태그로 저장이 되었다. 어레이가 어레이가 아니엇네용ㅎ
static func convertParams(_ params: Dictionary<String, Any>, _ boundaryPrefix: String) -> Data {
var data = Data()
params.forEach { (key, value) in
// Array check
if let arr = value as? Array<Any> {
arr.forEach { elem in
data.append(convertFormField(key: key, value: "\(elem)", boundaryPrefix))
}
} else {
data.append(convertFormField(key: key, value: "\(value)", boundaryPrefix))
}
}
return data
}
여기서 Array인 경우 각각 중복되게 "tags"
: "Tag1"
, "tags"
: "Tag2"
과 같이 다시 넣어주니까 정상 동작 함니다…
생각해보니까 파일 여러 개 보낼 때 저런 식으로 똑같이 필드네임 중복해서 각각 다 넣어 주는데 왜 몰랐을까
굿
댓글남기기