본문 바로가기

Swift

[Swift] URLSession으로 multipart/form-data request하기

안녕하세요 wody입니다.

 

얼마 전에 URLSession에 대해 알아보는 글을 작성했었는데, 오늘은 활용하는 글을 작성하고자 합니다.

 

이번 공부의 핵심 주제는 다음과 같습니다

 

HTTP 통신 

 

HTTP 개요 - HTTP | MDN

HTTP는 HTML 문서와 같은 리소스들을 가져올 수 있도록 해주는 프로토콜입니다. HTTP는 웹에서 이루어지는 모든 데이터 교환의 기초이며, 클라이언트-서버 프로토콜이기도 합니다. 클라이언트-서버

developer.mozilla.org

 

Apple Developer - URLRequest 

 

Apple Developer Documentation

 

developer.apple.com

 

개요

URLSession을 통해 GET 방식의 통신은 body가 없으므로 API에 맞는 url주소로 요청하면 되지만, 그 외에 header, body의 설정이 필요한 경우가 있습니다. 그 예로 multipart/form-data를 통해 이미지나 데이터를 업로드 할 때엔 URLRequest를 통해 어떻게 요청할지 설정해줘야 합니다.

 

URLRequest

URLRequest는 요청에 대한 정보를 나타내는 구조체입니다. 헤더 및 바디의 정보를 포함하고 있으며 사용자 정의에 따라 동작 메소드도 달라집니다

 

요청 요소

  • httpMethod: String? // 동작하는 메소드 ex. "GET", "POST"
  • url: URL?  // 요청 보낼 URL
  • httpBody: Data? // 구성 요소

헤더필드 메소드

  • setValue(String?, forHTTPHeaderField: String) // 헤더 필드 값을 설정

 

코드

request 구성 (header, body)

let boundary = "Boundary-\(UUID().uuidString)"

var request = URLRequest(url: url)
request.httpMethod = "POST"

// Header
request.setValue("multipart/form-data; boundary\(boundary)",
		forHTTPHeaderField: "Content-Type")

// Body
request.httpBody = createBody(...)

 

body 구성

func createBody(paramaeters: [String: Any], boundary: String) -> Data {
  var body = Data()
  let boundaryPrefix = "--\(boundary)\r\n"

  for (key, value) in paramaeters {
    body.append(boundaryPrefix.data(using: .utf-8)!)
    body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf-8)!)
    body.append("\(value)\r\n".data(using: .uft-8)!)
  }

  body.append(boundaryPrefix.data(using: .utf-8)!)

  return body
}

 

body 안에 이미지 혹은 데이터를 넣는다면

ex. png Image

// ImageFile.swift
struct ImageFile {
    let filename: String
    let data: Data
    let type: String
}

// image 파일을 Asset에서 불러올 경우
import UIKit

let imageData = UIImage(named: "dummyImage")!
let dummyImaage = ImageFile(filename: "dummy", data: imageData.pngData()!, type: "png")

func createBody(paramaeters: [String: Any], boundary: String, images: [ImageFile]?) -> Data {
    var body = Data()
    let boundaryPrefix = "--\(boundary)\r\n"

    for (key, value) in paramaeters {
      body.append(boundaryPrefix.data(using: .utf-8)!)
      body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf-8)!)
      body.append("\(value)\r\n".data(using: .uft-8)!)
    }

    // image를 첨부하지 않아도 작동할 수 있도록 if let을 통해 images 여부 확인
    // requst의 key값의 이름에 따라 name의 값을 변경
    if let images = images {
    	for image in images {
          body.append(boundaryPrefix.data(using: .utf-8)!)
          body.append("Content-Disposition: form-data; name=\"images[]\"; filename=\"\(image.filename)\"\r\n".data(using: .utf8)!)
          body.append("Content-Type: image/\(image.type)\r\n\r\n".data(using: .utf8)!)
          body.append(image.data)
          body.append("\r\n".data(using: .utf8)!)
      }
    }

    body.append(boundaryPrefix.data(using: .utf-8)!)

    return body
}