๐ŸŽ Apple/iOS

[iOS] Alamofire๋กœ Multipart ์ด๋ฏธ์ง€ ํ†ต์‹ ํ•˜๊ธฐ | post | Swift

JINiOS 2024. 2. 13. 21:57
728x90

์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ์ค‘ ์ด๋ฏธ์ง€๋ฅผ ์„œ๋ฒ„๋กœ ์—…๋กœ๋“œ ํ•ด์•ผํ•˜๋Š” ํƒœ์Šคํฌ๊ฐ€ ์žˆ์—ˆ๋‹ค!

๊ธฐ์กด์— ๋ชจ๋“  API๋ฅผ Alamofire๋กœ ํ†ต์‹ ํ•˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฏธ์ง€ ํ†ต์‹  ์—ญ์‹œ Alamofire๋กœ ์ง„ํ–‰ํ–ˆ๋‹ค.

 

๊ตฌํ˜„์€ ๋‹ค ํ•ด๋‘์—ˆ๋Š”๋ฐ, ๋” ์ฐพ์•„๋ณด๋ฉด์„œ ์ ์šฉํ•  ๋‚ด์šฉ์ด ์žˆ๋‚˜ ์•Œ์•„๋ณด๊ธฐ ์œ„ํ•ด, ๋‚˜์ค‘์— ๋‹ค์‹œ ๋ณด๊ธฐ ์œ„ํ•ด ๊ธฐ๋กํ•˜๋ ค ํ•œ๋‹ค. ๐ŸŒ

 

 


 

๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋จผ์ € ํ™•์ธ ๋ณด์ž!

https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#uploading-data-to-a-server

 

๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋จผ์ € ํ™•์ธํ•ด์„œ ์ฐธ๊ณ ํ•  ๋งŒํ•œ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ณ  > ๊ตฌ๊ธ€๋ง์œผ๋กœ ์ž˜ ์ •๋ฆฌํ•ด๋‘” ๋ธ”๋กœ๊ทธ๋ฅผ ์ฐธ๊ณ ํ•ด์„œ ๊ตฌํ˜„ > ๋‹ค์‹œ ๊ณต์‹๋ฌธ์„œ ํ™•์ธ

์š”์ƒˆ๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„๋ณด๋‹ค ํ•™์Šต์— ๋” ์‹œ๊ฐ„์„ ๋“ค์ด๊ณ  ์‹ถ์–ด์„œ ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค. 

 

๊ณต์‹๋ฌธ์„œ ์˜ˆ์‹œ - Uploading Multipart Form Data

AF.upload(multipartFormData: { multipartFormData in
    multipartFormData.append(Data("one".utf8), withName: "one")
    multipartFormData.append(Data("two".utf8), withName: "two")
}, to: "https://httpbin.org/post")
    .responseDecodable(of: DecodableType.self) { response in
        debugPrint(response)
}

ํ™•์ธํ•ด๋ณด๋ฉด multipartFormData์— ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์€ ํด๋กœ์ €๋ฅผ ์ „๋‹ฌํ•˜๊ณ , to์— URL์„ ๋‹ด์•„์ฃผ๋ฉด ๋œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

upload ๋ฉ”์„œ๋“œ์— ์‚ฌ์šฉ๋˜๋Š” ์ „์ฒด ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

func upload(
	multipartFormData: @escaping (MultipartFormData) -> Void, 
    to url: URLConvertible, 
    usingThreshold encodingMemoryThreshold: UInt64 = MultipartFormData.encodingMemoryThreshold, 
    method: HTTPMethod = .post, 
    headers: HTTPHeaders? = nil, 
    interceptor: RequestInterceptor? = nil, 
    fileManager: FileManager = .default, 
    requestModifier: RequestModifier? = nil 
) -> UploadRequest
 
์‹ค์ œ ๊ตฌํ˜„์— ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด๋ณด์ž
func postImage(challengeId: Int, challengeName: String, image: UIImage) async throws -> AwsImage {	
    let url = "https://www.tistory.com/"
    
    // API ์š”์ฒญ๋ถ€๋ถ„์„ ํ™•์ธํ•ด์„œ ํ—ค๋”๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์ž
    let headers: HTTPHeaders = [
        "accept": "application/json",
        "Content-Type": "multipart/form-data"
    ]
    
    // ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์€ image๋ฅผ jpeg์••์ถ•ํ•ด์ฃผ์ž
    let jpgImageData = image.jpegData(compressionQuality: 0.2) ?? Data()
    
    // ์ด๋ฏธ์ง€์™€ ์ •๋ณด๋ฅผ ๋‹ด์•„ upload ์ˆ˜ํ–‰
    let response = await AF.upload(
    	multipartFormData: { multipartFormData in
        	// Int ๊ฐ’์„ ๋ณด๋‚ผ ๋•Œ
        	multipartFormData.append(Data(String(challengeId).utf8),
                         withName: "challengeId")
            // String ๊ฐ’์„ ๋ณด๋‚ผ ๋•Œ    
            multipartFormData.append(Data(challengeName.utf8),
                         withName: "challengeName")
            // Data๋กœ ๋ณ€ํ™˜ํ•œ Image ๊ฐ’ ๋ณด๋‚ผ ๋•Œ
        	multipartFormData.append(jpgImageData,
                                 withName: "file",
                                 fileName: "image.png",
                                 mimeType: "image/jpeg")}, 
        to: url, method: .post, headers: headers)
    .serializingDecodable(AwsResponse<AwsImage>.self) // ๋””์ฝ”๋”ฉํ•  ๋ชจ๋ธ
    .response

    // response์˜ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋ถ„๊ธฐ์ฒ˜๋ฆฌ
    // 400~500๋ฒˆ๋Œ€์˜ ์—๋Ÿฌ์ผ ๊ฒฝ์šฐ ๋””์ฝ”๋”ฉํ•œ ์‘๋‹ต์—์„œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑ
    switch response.result {
    case.success(let res):
        if (400..<599).contains(response.response?.statusCode ?? 0) {
            throw CustomError.invalidRequest(res.errMessage)
        }
        return res.data
    case.failure(let error):
        throw CustomError.invalidRequest(res.errMessage)
    }
}

 

 

์œ ์˜ํ•˜์„ธ์š” ๐Ÿš—

- ํ—ค๋”์— "Content-Type": "multipart/form-data"๋ฅผ ๋ช…์‹œํ•ด์ค˜์•ผ ์„œ๋ฒ„์—์„œ ์ •์ƒ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

- multipart ํ†ต์‹ ์—์„œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ String, Int, Double๋“ฑ์˜ ์ž๋ฃŒํ˜•์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, Dataํ˜•์‹์œผ๋กœ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค.

 

 

์ด๋ฏธ์ง€ ์••์ถ• ๋ถ€๋ถ„

image.jpegData(compressionQuality: 0.2) ?? Data()

 

compressionQuality ์ˆซ์ž๊ฐ€ 0์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก ๋งŽ์ด ์••์ถ•ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๋Š” ์ž‘์•„์ง€๋ฉฐ, ํ™”์งˆ์ด ๋–จ์–ด์ง„๋‹ค.

compressionQuality ์ˆซ์ž๊ฐ€ 1์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก ์กฐ๊ธˆ ์••์ถ•ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๋Š” ๋น„๊ต์  ํฌ๋ฉฐ, ํ™”์งˆ์€ ์œ ์ง€๋œ๋‹ค.

 

0.2๋กœ ์••์ถ•ํ•˜๊ณ  ์„œ๋ฒ„์— ์ „์†กํ•œ ๋‹ค์Œ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์™€ ํ™•์ธํ–ˆ์„ ๋•Œ ์ดฌ์˜ํ•œ ์ด๋ฏธ์ง€(์„œ๋ฒ„์— ์˜ฌ๋ฆด ์ด๋ฏธ์ง€)๊ฐ€ 500kb(์„œ๋ฒ„์—์„œ ์ œํ•œํ•œ ๊ฐ’)๊ฐ€ ๋„˜์ง€ ์•Š๊ณ  ๊ธ€์ž๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ์ •๋„์˜ ํ™”์งˆ์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๊ฐ’์œผ๋กœ ํ•ด๋‘์—ˆ๋‹ค.

jpegData()๊ฐ€ nil์ผ ๊ฒฝ์šฐ์— Data()๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ํ•˜๋Š” ๊ฒŒ ์•„๋‹Œ ์•„๋ž˜ ๋กœ์ง์ด ์‹คํ–‰๋˜์ง€ ์•Š๋„๋ก ์ˆ˜์ •ํ•ด์•ผ๊ฒ ๋‹ค..!

 

์ด๋ฏธ์ง€๋ฅผ ์—ฌ๋Ÿฌ์žฅ ์—…๋กœ๋“œํ•  ๊ฒฝ์šฐ

for๋ฌธ์œผ๋กœ ์ด๋ฏธ์ง€ ๋ฐฐ์—ด [uiImage]์„ ๋Œ๋ฉด์„œ multipartFormData์— appendํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

 

728x90