24.11.27 - application/json, multipart/form-data, 이미지 파일 POST

강연주·2024년 11월 27일

📚 TIL

목록 보기
103/186

한 페이지에서 데이터 통신 두 번 하는 코드 쓰기 너무 싫어서 괴로운데,
(뭔가 길어지고 지저분해지고 통제를 벗어나는 것 같고...! 서버에 부담이 얼마나 될지는 백엔드분께 질문 드려봐야 알겠지만, 실질적인 지표보다는 그냥 느낌이 별로^ㅇ^)
appliaction/json 형식으로 다른 인풋 데이터들과 함께 이미지 전송하는 방법을 못 찾아냈고, 많은 인풋 값들이 존재하는 상황에서 이미지에 얼마나 시간과 노력을 들이는 게 맞는가를 모르겠다. 급하게 허둥대지 않되 부지런히 하자고 생각은 하고 있지만...🥹


GPT 선택지

  1. FormData API 사용
  2. Base64 인코딩하여 JSON으로 포함
  3. multipart/form-data로 서버 전송
  4. 이미지 업로드 후 URL 포함
  5. Input의 type="file" 사용
  6. 파일 객체를 state로 관리
  7. React Dropzone과 같은 라이브러리 활용

<input type="file" />를 사용하면 파일은 FormData 객체를 통해 전송되어야 하며, application/json 컨텐츠 타입으로는 파일 데이터를 직접 전송할 수 없다. 이로 인해 다음과 같은 문제가 발생한다.

  • JSON 형식과 파일 데이터의 호환성 부족
    : 파일은 바이너리 데이터로, JSON으로 표현하기 어렵다.
    application/json 컨텐츠 타입으로는 파일을 직접 포함할 수 없으므로
    파일을 전송하려면 별도의 방식이 필요합니다.

  • 서버가 JSON 외의 데이터 처리 미지원
    : 서버가 JSON 형식만 처리하도록 설계되었다면, 파일 데이터가 전송되지 않거나 무시될 가능성이 있다.

  • 파일 데이터 직렬화 문제
    : 파일을 JSON에 포함하려면 Base64로 인코딩해야 하며,
    이는 파일 크기를 크게 만들어 네트워크 사용량이 증가한다.


GPT 해결 방안

  • FormData와 multipart/form-data 사용
    : 파일과 다른 데이터를 함께 전송하려면 FormData 객체를 사용하고,
    Content-Type을 multipart/form-data로 설정해야 한다. ➡️ 기각

  • 파일 업로드와 JSON 데이터를 별도로 전송
    : 파일 업로드 API를 따로 구현하여 먼저 파일을 업로드하고,
    업로드된 파일의 URL이나 경로를 JSON 데이터에 포함해 전송한다.

  • Base64 인코딩 사용 (비효율적이므로 권장되지 않음)
    : 파일 데이터를 Base64로 인코딩해 JSON에 포함할 수 있지만,
    파일 크기가 커지고 전송 속도가 느려진다.

결론
<input type="file" />를 사용할 경우, application/json 방식으로는 파일을 전송할 수 없으므로 다른 전송 방식(FormData나 별도 파일 업로드 API)으로 변경.


🚚 image 전송과 함께 데이터는 json으로 보내고 싶은 경우

이런 문제는 보통 이미지와 JSON데이터를 함께 보내지 않습니다.
API를 전송할 때는 보통 content/type을 application/json으로 전송하는데 이것은 multipart/formdata 방식이 아니기 때문입니다.
이런 문제는 보통 다음과 같이 해결합니다.

1. 분리 요청
이미지만 AJAX등을 통해서 별도 요청으로 먼저 서버에 전송합니다.
서버는 이미지를 저장하고, 저장된 이미지의 id(또는 파일명)를 클라이언트에 전송합니다.
클라이언트는 이미지의 id와 전송할 데이터를 application/json으로 전송합니다.

2. base64 인코딩
이미지를 base64로 인코딩해서 application/json으로 전송할 데이터와 base64로 인코딩된 이미지를 함께 전송합니다.
관련해서 이미지 ajax 전송, 이미지 base64 인코딩 전송등으로 검색해보시면 도움이 되실거에요.


➕ Image와 Json을 함께 서버에 업로드하는 대표적 3가지 방법


🥟 리액트에서 이미지 파일과 json data를 함께 formData에 넣어 전송

FormData은 HTML 폼 데이터로, 폼의 각 필드와 값을 나타내는 키/값 쌍들의 집합을 쉽게 구성할 수 있는 방법을 제공한다. 바디에 FormData 객체를 넣으면, HTTP 메시지는 인코딩되고, Content-Type 속성은 multipart/form-data로 지정한 후에 데이터가 전송된다.

FormData에 ”profile”과 ”signup”에 각각 파일과 이메일 닉네임, 등의 데이터를 담아 보냈더니 에러가 발생했다. 알고보니 파일은 multipart/form-data로, 그 외 json 데이터들은 application/json 형식으로 따로 보내야했다.

Blob(Binary Large Object, 블랍)은 바이너리 형태중에서도 큰 객체를 의미하는데, 이미지, 비디오 사운드와 같은 멀티미디어 데이터를 다룰 때 사용된다.따라서 file은 multipart/form-data, data는 application/json 형식으로 보내기 위해 Blob을 사용했다.


🥔 multipartFile과 application/json 동시에 보내는 방법

🖥️ const files = this.fileList; //file 목록
const data = {
	name: "name",
  	grade: 3
}

const formData = new FormData();
const json = JSON.stringify(data);
const blob = new Blob([json], { type: 'application/json' });

formData.append("data", blob);

for(let i = 0; i<files.length; i++) {
 	formData.append("multipart", files[i]); 
}

try {
	const res = await.axios.post('url', formData, {
        headers: {
            'Content-Type': 'multipart/form-data'
          {
    });
} catch (error) {
	console.error("error:", error);
}

문제는 프론트엔드에서 텍스트와 이미지를 동시에 보내야 했는데
Headers에 Content-type는 하나만 설정이 가능하다는 것이었다.

다행히 http에서는 Content-type을 multipart으로 설정하면 두가지 이상의 타입을 사용할 수 있다.

위와 같이 텍스트는 Blob에 넣고 이미지와 함께 formData에 넣어 content-type를 multipart/form-data로 설정해주니 성공하였다.


➡️ Blob 가나요? ➡️ 서버에서 multipart/form-data 못 받아주면 못 갑니다.. ➡️ multipart/form-data로 변경!

profile
아무튼, 개발자

0개의 댓글