[TIL] 서버로 파일과 DTO를 같이 보내기

phdljr·2024년 1월 24일
0

TIL

목록 보기
60/70
post-custom-banner

프론트에서 이미지와 DTO를 백엔드로 보내줄 필요가 있었다.
단순히 body에 담아서 다 보내주면 될거라는 막연한 생각을 했지만, 어림도 없었다.

여러 시행착오를 겪으며 해결책을 찾게 되었으며, 이에 대해 글을 작성해보는 시간을 가져보겠다.


Content-Type

  • HTTP 요청에서 리소스의 미디어 타입을 나타내기 위해 사용되는 헤더 개체
    • 미디어 타입(MIME type)이란, 파일 또는 바이트 집합의 성격과 형식을 나타냄
    • MIME 타입은 가장 일반적으로 슬래시로 구분된 type과 subtype의 두 부분으로 구성됨
Content-Type: text/html; charset=utf-8
Content-Type: multipart/form-data; boundary=something

application type

  • 다른 타입 중 하나에 명시적으로 속하지 않는, 모든 종류의 이진 데이터
  • 어떤 방식으로든 실행되거나 해석될 데이터 또는 특정 어플리케이션이나 어플리케이션 범위를 사용해야 하는 이진 데이터
  • 일반 이진 데이터(또는 실제 타입을 알 수 없는 이진 데이터)는 application/octet-stream으로 사용됨

multipart type

  • 일반적으로 다른 MIME 타입들을 지닌 개별적인 파트들로 나누어지는 문서의 그룹을 나타냄
  • 여러 컴포넌트 조각으로 구성된 문서를 나타내며, 각 부분은 고유한 개별 MIME 타입을 가질 수 있음
  • 하나의 트랜잭션으로 함께 전송되는 여러 파일을 캡슐화할 수 있음
  • ex) 이메일에 여러 파일을 첨부할 때 multipart MIME 타입이 사용됨

multipart/form-data

  • 브라우저에서 서버로 완성된 HTML Form (en-US)의 내용을 전송 시 사용
  • 다중부분 문서 형식으로써, 경계(이중 대시 '--' 로 시작되는 문자열)로 구분되어지는 다른 파트들로 구성
  • 각 파트는 그 자체로 개체이며 자신만의 HTTP 헤더를 가지는데, 파일 업로드 필드를 위한 헤더로는 Content-Disposition, Content-Type이 존재
Content-Type: multipart/form-data; boundary=aBoundaryString
(other headers associated with the multipart document as a whole)

--aBoundaryString
Content-Disposition: form-data; name="myFile"; filename="img.jpg"
Content-Type: image/jpeg

(data)
--aBoundaryString
Content-Disposition: form-data; name="myField"

(data)
--aBoundaryString
(more subparts)
--aBoundaryString--

예시

<form
  action="http://localhost:8000/"
  method="post"
  enctype="multipart/form-data">
  <label>Name: <input name="myTextField" value="Test" /></label>
  <label><input type="checkbox" name="myCheckBox" /> Check</label>
  <label>
    Upload file: <input type="file" name="myFile" value="test.txt" />
  </label>
  <button>Send the file</button>
</form>
POST / HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=---------------------------8721656041911415653955004498
Content-Length: 465

-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="myTextField"

Test
-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="myCheckBox"

on
-----------------------------8721656041911415653955004498
Content-Disposition: form-data; name="myFile"; filename="test.txt"
Content-Type: text/plain

Simple file.
-----------------------------8721656041911415653955004498--

multipart/form-data를 통해 서버로 이미지와 DTO 보내기

let formData = new FormData();
formData.append(
  'recipeCreateRequestDto',
  new Blob([JSON.stringify(recipeCreateDto)], { type: 'application/json' }),
);
if (recipeCreateImage === null) {
  formData.append('recipeCreateImage', new Blob());
} else {
  let recipeCreateImageType;
  if (recipeCreateImage.type == 'image/png') {
    recipeCreateImageType = 'image/png';
  } else {
    recipeCreateImageType = 'image/jpeg';
  }
  formData.append(
    'recipeCreateImage',
    new Blob([recipeCreateImage], { type: recipeCreateImageType }),
  );
}

fetch(HOST + `/api/v1/recipes`, {
  method: 'POST',
  body: formData,
})
  .then((res) => res.json());

참조

https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/MIME_types

profile
난 Java도 좋고, 다른 것들도 좋아
post-custom-banner

0개의 댓글