모델 회원가입을 마무리 하려고 민서가 만들어둔 데이터를 서버에 전달하는 것을 내가 맡아서 하던 중이었다.
나는 그대로 POST 요청을 했는데 어랍쇼? 자꾸 에러가 났다
그래서 스웨거를 확인해봤다
multipart/form-data
이게뭔데.. 먼저 multipart
가 무엇인지 알아보자
클라이언트와 서버 간에 전송되는 HTTP 요청 또는 응답에서 여러 종류의 데이터를 동시에 전송하기 위해 사용되는 방식이다
일반적으로 파일 업로드와 관련된 데이터를 전송하는데 주로 사용된다
멀티파트 요청은 'Content-Type' 헤더에 'multipart/form-data' 값을 가지며 여러개의 파트(part)로 구성된다. 각 파트는 개별적인 데이터 조각으로 파일이나 텍스트 데이터 등을 포함할 수 있다. 각 파트는 헤더와 본문(body)으로 구성되어 있으며 헤더에는 파트의 메타 데이터가 포함되어 있고 본문에는 실제 데이터가 포함된다.
<input
type="file"
accept="image/*"
onChange={(e) => {
uploadImg(e);
}}
/>
formData는 form 필드와 그 값을 나타내는 일련의 key/value 쌍을 쉽게 생성할 수 있는 방법을 제공한다.
new FormData()
를 통해 빈 객체를 만들 수 있고 FormData.append
을 사용하여 key/value 쌍을 추가할 수 있다
그런데 민서의 코드는 이렇게 돼있었다.
const imgUrl = URL.createObjectURL(imgObj[0]);
위처럼 URL로 한다고 하더라도 blob:localhost ~~… 이런식으로 나오고 있었다 서버에서 s3이미지 url을 가져와서 보내는 형식이 아니라면 우리는 File을 그대로 보내줘야 하는 것이다.
profileImg를 formData 객체로 선언하고 넣어준 다음 회원가입에 필요한 데이터와 함께 requestBody에 넣어주고 Content-type은 multipart/form-data로 설정해줬다. 그래서 서버에서는 MultiPart형식의 이미지 url을 받아야하는데 계속 [Object FormData]형식으로 받았다
알고 보니 그렇게 하는 것이 아니었고 reaquestBody를 FormData 형식으로 넘겨줘야 하는 것인데 이렇게 하니까 designerInfo 데이터를 서버에서 읽을 수가 없었다 이걸 어떻게 처리해야할까,,,
우리가 보내야 할 데이터 profileImg와 designerInfo는 완전히 다른 Content-type이다. 두 종류의 데이터가 하나의 HTTP Request Body
에 들어가야 하는데, 한 Body
에서 이 2 종류의 데이터를 구분에서 넣어주는 방법이 필요하다. 그래서 우리는 multipart
타입을 사용해야하는 것이다.
결론부터 말하자면 FormData 객체로 만들어주고 이 객체에 profileImg 파일과 designerInfo json데이터를 넣어줬다. 그리고 이 객체를 reaquestBody로 넣어줬다!
const requestBody = new FormData();
const jsonSignUpData = JSON.stringify(signUpData);
const deisgnerInfo = new Blob([jsonSignUpData], { type: 'application/json' });
requestBody.append('designerInfo', deisgnerInfo);
requestBody.append('profileImg', profileImg.file);
🤯 그러다가 갑자기 의문이 들었다... 저걸 왜 json으로 바꿔줘야하지?
우리가 평소에 보내는 requestBody는 JavaScript 객체이다. 우리가 따로 변환해서 보내지 않았다. 그렇다면?
일반적으로 JavaScript 객체가 제공되면 이 객체는 내부적으로 JSON 문자열로 변환되어 HTTP 요청 본문에 포함됩니다.
🤯 그렇다면 여기서 Blob이 뭐지?
Blob 객체는 파일류의 불변하는 미가공 데이터를 나타냅니다. 텍스트와 이진 데이터의 형태로 읽을 수 있다
아하!! 그래서 JSON 형태를 유지해서 서버에서 읽을 수 있겠구나...
🤯 그렇다면 File은? 왜 그냥 넣어준거지?
File 객체는 Blob의 한 종류로, Blob을 사용할 수 있는 모든 맥락에서 사용할 수 있습니다.
✨ 최종 코드
const postDesignerSignUp = async () => {
const signUpData: DesignerInfo = {
name: name.data,
gender: gender.data,
...
};
const requestBody = new FormData();
const jsonSignUpData = JSON.stringify(signUpData);
const deisgnerInfo = new Blob([jsonSignUpData], { type: 'application/json' });
requestBody.append('designerInfo', deisgnerInfo);
requestBody.append('profileImg', profileImg.file);
try {
const data = await api.post('/auth/signup/designer', requestBody, {
headers: {
'Content-Type': `multipart/form-data`,
},
...
};
};
네트워크 메서드가
FormData
객체를 바디로 받는다는 건FormData
의 특징입니다. 이때 브라우저가 보내는 HTTP 메시지는 인코딩되고Content-Type
속성은multipart/form-data
로 지정된 후 전송됩니다.
헉 그럼 header content 타입도 설정안해줘도 되나?????
const data = await api.post('/auth/signup/designer', requestBody);
왕 ! 성공 ~~!!!! 🥳🥳🥳🥳
나와 함께 이 문제를 해결한 우리 멋쟁이 서버리드 안현주씨의 아티클 ~ ㅋㅋ 볼람볼 ~
https://hellozo0.tistory.com/414
저는 이거 실패했었는데 추후에 이 글을 보고 다시 도전해보겠습니다. 이런 글 작성해주셔서 너무너무 고맙습니다!!