들어가며
프론트단을 개발하면서 데이터를 post 해야하는 상황이 항상 생긴다.
이번에는 유저가 파일과 텍스트를 올렸을 때 그 안의 내용을 post 하는 상황이었다.
formData를 처음 써보는거라 많은 고민을 했다.
그리고 백앤드 개발자 분에게 전송이 제대로 되었는지 계속 물어봐야해서 좀 미안하기도 했다..
아무튼, formData로 데이터 post 하는법을 알아보자~!
본문
먼저 폼을 만들어준다.
나는 유저가 설명을 입력하고 파일을 업로드하도록 만들었다.
<form onSubmit={handleSubmit}>
<textarea
placeholder="설명을 작성해주세요."
value={data.description}
onChange={handleDescriptionChange}
/>
<input
type="file"
id="fileInput"
name="fileInput"
accept=".pdf, .doc, .docx, .txt"
onChange={handleFileChange}
/>
<button type="submit">제출!</button>
</form>
여기서 textarea에 받은 value 값과 input을 이용해 올린 파일이 데이터로 전송될 것이다.
처음에는 리액트쿼리를 이용하지 않고 짰다가 리액트 쿼리를 이용하면 좀 더 간편하게 보여진다고 해서 리액트 쿼리를 이용했다.
리액트 쿼리를 이용해 post 요청하기
// 제출 post
const postMutation = useMutation(
(formData) =>
axios.post("http://서버주소", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
}),
{
onSuccess: () => {
alert("제출 완료 되었습니다.");
setShowModal(false);
},
onError: (error) => {
// 에러 처리
console.error("Error:", error);
},
},
);
폼 데이터를 요청할때는 꼭! "Content-Type": "multipart/form-data" 을 지정해줘야 한다.
그리고 버튼을 눌렀을 때 submit 되도록 코드를 짜보자.
const handleSubmit = async (e) => {
e.preventDefault();
try {
const formData = new FormData();
// 파일이 있으면, "file" 이라는 key값에 선택된 파일을 append 해주기
if (selectedFile) {
formData.append("file", selectedFile);
}
// 유저가 올린 데이터를 데이터로 만들어주는 부분
const jsonData = [
{
id: 1,
description: data.description,
},
];
// "지정해준key"에 new Blob를 이용해서 jsonData를 묶어준다.
// 이부분이 가장 중요!!!!!!!!!!!
formData.append(
"지정해준key",
new Blob([JSON.stringify(jsonData)], {
type: "application/json",
}),
);
// 폼데이터를 모두 append하고 호출하는 부분
postMutation.mutate(formData);
// formData가 잘 들어가는지 폼데이터 콘솔 출력해보기!
for (const x of formData) {
console.log(x);
}
} catch (error) {
console.error("Error:", error);
}
};
나는 유저가 파일을 올려도되고 안올려도 되었기 때문에 조건문을 통해 파일이 있으면 올리고 없으면 데이터를 전송하지 않는다.
설명란에는 꼭 있어야하기 때문에 기존 코드에는 "유효성 검사"를 하는 코드도 추가되어있다.
try 부분이 가장 애를 먹었는데, 이유는 어떤식으로 데이터를 묶어서 백앤드 서버에 전송해야하는지가 문제였다.
백앤드 개발자분이 postman으로 post 하는 데이터를 보여주셨을 때,
key | value |
postBox (예시) | { id: 1, (해당 글의 번호) description: "테스트 설명란입니다." (해당 글의 설명란 - 유저가 쓰는 부분) } |
이런식으로 key와 value값이 나뉘어있었다.
처음에는 저 value값을 어떤식으로 append 시켜주어야 하는지 고민과 문제가 많았다..
원래 formdata를 append 할 때는 key에 id, description이 오고 value에 1, "테스트 설명란입니다." 가 와야
데이터를 전송할 때 한개씩 보내버리면 되서 편하지만, 여러가지 경우의 수가 있기 때문에? 저런식으로 보냈어야 했다.
문제의 원인
나는 처음에 이런식으로 보냈다.
formData.append(
"지정key값",
JSON.stringify({
id: 6686,
description: data.description,
}),
);
이런식으로 보냈을 때 백앤드분 서버에 오류가 뭐라고 떴냐면,
Content-Type 'application/octet-stream' is not supported
뭐 이런식으로 떴다.. 이것을 지피티 한테 물어보니까
이런식으로 content-type이 맞지 않을 때 문제가 생긴다고 했다.
백앤드분께 이걸 말씀드리니까 "application/json" 형태로 설명란을 받아야 한다고 하셨다.
하지만.. 난 파일때문에 "Content-Type": "multipart/form-data" 이것을 꼭 써야한다고 말씀드렸고 함께 찾아주셨다.
문제 해결
결론은 파일은 파일대로 "multipart/form-data" 로 보내야하고, 설명란 내용은 "application/json" 으로 보내야 했던 것이다.
그래서 코드를 이런식으로 고쳤다.
formData.append(
"지정해준key",
new Blob([JSON.stringify(jsonData)], {
type: "application/json",
}),
);
form에 객체 생성에 필요한 값들을 JSON 형식으로 구조화한 뒤 JSON.stringify() 함수를 통해 JSON 문자열로 변환한다.
이것을 Blob(Binray Large OBject)로 변환해 form에 추가하는 것이다.
그리고 요청을 보내면 JSON 객체가 "data"라는 이름으로 하나의 요청 파트를 차지하게 된다.
이때 data를 바로 추가하는 것이 아니라 Blob으로 생성하면서 타입을 "application/json"으로 지정하는 것이 중요하다!
이렇게 보내야 해당 요청 부분이 json 타입으로 전송 되어서 메세지 컨버터의 객체 매핑이 지원 된다고 한다.
마무리
많은 방법을 사용하다가 결국 해결하게 되었다!
폼데이터로 요청하는 것도 처음 해봐서 정리해놓으면 좋을 것 같아서 간단하게 정리해봤다.
그래도 잘 도와주셔서 빨리 해결할 수 있었던 것 같다. 백앤드 개발자분이 만든 코드라서 무조건 협업이 필요했던 과정이었다.
계속 여쭤보는게 민폐인 것 같아서 처음에는 망설였지만 잘 답변해주셔서 감사했다.
프론트엔드 개발자들이랑 백앤드 없이 개발 프로젝트를 하면서 파이어베이스만 써봤는데 실제로 백앤드분이랑 협업하는 과정을 겪으니 의사소통이 중요하다는 것을 깨달았다.
이제야 처음이고 앞으로 데이터 요청할게 산더미지만 하나는 성공했으니 시작이 나쁘지 않은것 같다.
더 복잡한게 많이 남아서 열심히 해봐야지..
아무튼 폼데이터 첫 시작 조금은 성공..! 😮
'개발' 카테고리의 다른 글
[React] typescript에서 interface와 type의 차이점을 알아보자 (0) | 2024.01.25 |
---|---|
[React/CRA] CRA + typescript + Eslint + prettier 초기 세팅하기 (1) | 2024.01.23 |
[성능 최적화] 웹 성능 최적화에 대한 고찰과 최적화 하는 방법을 알아보자 (3) | 2024.01.18 |
[자료구조] 해시 (Hash, Hash Table, Hash Function)와 자바스크립트에서의 해시맵 (4) | 2024.01.07 |
[자료구조] 이분 탐색/이진 탐색 (Binary Search) (1) | 2024.01.04 |