Project Restoration - 댕근이다옹(DangGeuneDaong)
프로젝트에서 업로드 기능을 구현하면서 파일과 함께 JSON 데이터를 함께 보내야 할 상황을 마주하게 되었습니다. API 명세서에서 요구한 내용은 다음과 같습니다.
{
File:[] // 파일 객체
request:{
userId: 'geniee1220',
mainCategory: 'DOG'
...
}
}
평소 서버에 데이터를 보낼 때 HTTP의 헤더에 일반적인 데이터(JSON 객체)를 보낼 때는 "Content-Type: application/json"을, 파일을 전송할 때는 "Content-Type: 'multipart/form-data” 을 사용해서 요청 본문(body)의 유형을 명시해주었는데요. 이런 경우에는 어떻게 해결해야 할까요?
주로 파일, 이미지, 오디오 등 멀티미디어 파일은 바이너리 데이터로 이루어져 있기 때문에, HTTP 에서 이미지나 파일을 올리고자 할 때는 new FormData() 객체를 생성한 다음 서버로 보내는 것이 일반적인 방법입니다.
이 때 HTTP 헤더의 Content-type에는 multipart/form-data 형식을 지정하는데, 파일 뿐만 아니라 일반적인 텍스트 데이터를 함께 전송할 수 있기 때문입니다.
반면에 Content-type을 application/json 으로 지정하는 경우 바이너리 데이터로 구성된 멀티미디어 파일은 전송할 수 없기 때문에 이번 경우처럼 파일과 문자열 데이터를 함께 보내야 하는 경우에는 Request Header에 multipart/form-data를 지정해야 한다는 결론을 도출할 수 있었습니다.
따라서 body의 JSON 타입 데이터에 헤더와는 다른 Content-type, application/data 지정해주는 것이 관건이라고 생각했습니다.
제가 주목한 것은, Blob(Binary Large Object, 블랍) 생성자의 options으로 MIME(Multipurpose Internet Mail Extensions) 타입을 지정해줄 수 있다는 것이었습니다.
❗️MIME 타입은 파일의 컨텐츠 유형을 나타내는 문자열로, 일반적으로 HTTP 요청 및 응답에서 Content-Type 헤더를 정의하는 데 사용됩니다.
export const addPost = async (data: PostModel) => {
const formData = new FormData();
try {
// 데이터 객체에서 파일들(files)을 분리하고, 나머지 속성들(requestObject)을 추출합니다.
const { files, ...requestObject } = data;
// requestObject를 JSON 문자열로 변환하여 Blob 객체로 만듭니다.
const requestBlob = new Blob([JSON.stringify(requestObject)], {
type: 'application/json',
});
// FormData에 requestBlob을 추가합니다.
formData.append('request', requestBlob);
if (files.length == 0) {
// 파일이 없는 경우, FormData에 빈 문자열을 추가합니다.
formData.append('files', '');
} else {
files.forEach((file: File) => {
formData.append('files', file);
});
}
// instance를 사용하여 api 주소로 multipart/form-data로 요청을 보냅니다.
// instance를 사용했기 때문에, 인증 토큰 입력 부분은 생략합니다.
const response = await instance.post(
'api',
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
},
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round(
((progressEvent?.loaded ?? 0) * 100) / (progressEvent?.total ?? 1)
);
console.log(`Upload progress: ${percentCompleted}%`);
},
}
);
return response;
} catch (error: any) {
throw new Error(error);
}
};
Reference
⓵ https://heropy.blog/2019/02/28/blob/
⓶ https://velog.io/@huewilliams/파일과-JSON-데이터를-동시에-보내기-1편-feat.Multipartform-data
⓷ https://velog.io/@st4889/React-첨부파일-업로드-다운받기-구현하기api로-전송#-axios로-첨부파일을-서버에-보내자