multipart/form-data를 사용하는 이유 + Spring의 @RequestPart

김예지·2024년 1월 22일
0

트러블슈팅

목록 보기
1/4

문제 상황

Jmeter에서 파일과 json을 동시에 보내기 위해 이런 저런 것들을 변경하다 보니 multipart/form-data가 무엇인지에 대해 궁금증이 생겼다.

json을 전송할 때에는 Content-Type을 application/json으로 설정하면 되고, 파일을 전송할 때는 multipart/form-data로 설정했는데 그럼 두개를 동시에 보낼 때는 어떤 것으로 설정해야 하는 것일까?

Multipart 및 form 태그의 enctype

우선, 이미지 파일은 바이너리로 구성된 것이므로 png나 jpg 파일 자체가 전송되는 것이 아니라 바이너리가 HTTP request body에 담아 전송된다.
그리고 바이너리는 그 자체로 전송 시 효율이 높기 때문에 인코딩을 하지 않아도 된다.

HTML form 태그의 enctype(인코딩 방법) 속성값

  • application/x-www-form-urlencoded
    : 모든 문자가 서버로 보내기 전에 인코딩 됨(default 값)
  • text/plain
    : 공백 문자(space)만 '+' 기호로 변환. 나머지 문자는 인코딩 하지 않음.
  • multipart/form-data
    : 모든 문자를 인코딩하지 않음.

enctype 속성이 multipart/form-data로 설정된 경우, form은 전송할 데이터를 MIME(Multipurpose Internet Mail Extensions) 형식으로 인코딩한다.
multipart/form-data는 여러 부분(parts)으로 나뉘어진 데이터를 전송하기 위한 형식으로, 파일 업로드와 같이 이진(binary) 데이터를 효과적으로 처리할 수 있다.

POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryABC123

------WebKitFormBoundaryABC123
Content-Disposition: form-data; name="username"

JohnDoe
------WebKitFormBoundaryABC123
Content-Disposition: form-data; name="profile_picture"; filename="profile.jpg"
Content-Type: image/jpeg

(Binary image data goes here)
------WebKitFormBoundaryABC123--

multipart/form-data로 전송된 request는 각 파트마다 바운더리로 구분되어 있으며, 개별적으로 명시된 Content-Type으로 처리된다. Content-Type이 명시되지 않은 경우 해당 데이터는 폼 필드로 처리된다.

그런데 파일은 인코딩이 필요 없다고 해도, 텍스트도 그냥 multiaprt/form-data로 전송해도 되는 것일까?
된다! 텍스트 데이터는 인코딩되지 않고 단순히 원본 문자열 그대로 전송된다.

그렇다면 맨 처음 질문으로 돌아와서, Base64로 인코딩한 이미지의 문자열을 json에 담아서 application/json 보내는 방법은 어떨까?

이 방법은 적은 용량의 파일을 보낼 때는 가능하다.
json 규격 자체에 maxSize에 대한 명세는 없으나, Json Parser에서 보통 4MB까지만 지원하므로 그냥 맘 편히 multipart/form-data를 사용하는 것이 좋다.
(물론 요구사항에 따라 필요할 수 있으므로, 알아 두는 것은 좋다!)

Spring에서 Multipart 사용하기

보통 json만 전송할 때에는 @RequestBody 어노테이션을 사용하는 것과 달리 multipart/form-data로 전송할 때는 @RequestPart를 사용해야 한다.

@RequestBody는 주로 json이나 xml과 같은 본문(body)에 직렬화된 데이터를 처리할 때 사용되므로, multipart로 오는 다양한 형태의 데이터를 모두 @RequestBody로 받는 것은 어렵다.

사용 예제는 다음과 같다.

@PostMapping(value = "/test")
public ResponseDto<RecipeResponseDto.RecipeStatusDto> testCreateRecipe(
           @RequestPart(value = "content") RecipeRequestDto.CreateRecipeDto request,
           @RequestPart(value = "thumbnail") MultipartFile thumbnail,
           @RequestPart(value = "stepImages") List<MultipartFile> stepImages) {}

0개의 댓글

관련 채용 정보