본문 바로가기
펀잇

multipart

by 해-온 2023. 8. 6.

multipart

‘펀잇’에는 편의점 음식을 먹고 리뷰를 작성할 수 있는 폼이 있다.

사용자는 리뷰에 사진, 별점, 태그, 리뷰 내용, 재구매 의사를 남길 수 있다.


이 과정에서 이미지 파일을 서버로 보낼 때 문제가 생긴다.

어떤 문제가 생기는지 알아보자.


📁 File Upload

이미지는 File 형태이다.

<input ref={inputRef} type="file" accept="image/*" onChange={handle} />
 

보통 위의 형식처럼 input type을 file로 주면 파일 업로드를 할 수 있다.

이렇게 file을 서버로 전송하면 해당 file 데이터를 multipart/form-data형태로 받게 된다.

우리가 생각하는 ‘파일이름.확장자’ 형식이 아닌 이진 데이터 형식으로 받는다.


📚 FormData

이제 File 형태의 이미지와 내용을 한 번에 서버로 보내보자.

이때, formData 객체를 통해 보낼 수 있다.


formData란 form 데이터를 동적으로 생성하고 전송할 수 있는 객체이다.

append()를 사용해 내용을 key와 value 형식으로 보낼 수 있다.


formData.append('image', reviewImageFile, reviewImageFile.name);
 

이 formData를 사용해 데이터를 multipart/form-data 형식으로 보낼 수 있다.


🧑‍🦲 Content-type: multipart/form-data

form에 내용을 모두 작성하고 제출을 눌러보자.

작성한 데이터들은 Body에 넣어 HTTP Request로 서버에 전송된다.


이때, Body의 타입을 명시하는 것이 Content-type Headers이다.

기본적으로 form을 전송하면 Content-type은 application/x-www-form-urlencoded 이다.


그런데 우리는 이미지를 내용과 함께 전송해야 한다.

이미지는 file 형태이고, 내용은 json 형태로 둘의 Content-type이 다르다.

다른 종류의 데이터를 하나의 HTTP Request Body에 넣어야 한다면 어떻게 구분할 수 있을까?


이때, multipart 타입을 통해 해결할 수 있다.

multipart란 다양한 형태의 콘텐츠를 함께 전송할 때 사용하는 방법이다.


그럼, Content-type을 multipart/form-data로 지정하면 될까?


🚨 에러 발생

웁스. 아니다.



위와 같은 오류가 발생한다.


왜 이런 오류가 발생할까?

기본적으로 formData에 파일이 있을 경우 브라우저는 자동으로 boundary를 붙여준다.




그런데 Content-type을 설정해 주면 이게 Override 되어 boundary가 사라지게 된다.

그래서 위와 같은 오류가 발생하게 된 것이다.


그렇다면 Content-type을 안 적으면 해결될까?


🚨 에러 발생2

웁스. 아니다.




이젠 이런 오류가 발생한다.


왜 이런 오류가 발생할까?


formData를 전송할 때, 이미지 이외의 내용은 JSON.stringify 처리를 했다.

const 내용 = {
      rating,
      tagIds,
      content,
      rebuy,
    };

const 내용json변환 = JSON.stringify(내용);
 

서버에 데이터를 보낼 때, 데이터는 문자열 형태여야 하기에 객체를 문자열로 인코딩 한 후 전송해야 한다.

따라서 JSON.stringify() 를 사용하여 객체를 JSON 문자열로 변환했다.


여기서 json 데이터를 application/json 타입으로 명시해 주지 않아 octet-stream(8비트 단위의 이진 데이터)으로 인식하게 된 것이다.


이는 'Blob(Binary Large Object)'을 통해 해결할 수 있다.

Blob은 이름 그대로 바이너리 형태로 큰 객체를 저장할 수 있다.

아까 JSON.stringify()한 JSON 문자열을 Blob 처리해 보자.


const jsonBlob = new Blob([내용json변환], { type: 'application/json' });
 

Blob 처리를 하면 JSON 문자열을 이진 데이터로 변환해 준다.

type에 application/json 을 명시하여 데이터가 JSON 유형임을 알려준다.


이렇게 Blob 처리된 내용을 formData에 넣어준다.


formData.append('reviewRequest', jsonBlob);
 

그리고 body에 formData를 실어 POST로 보내면


const response = await fetch(url, {
        method: 'POST',
        body: formData,
        credentials: 'include',
      });
 



작성 성공!!!

댓글