⚛️React | 리액트 파일 input 다루기 2탄 - API 요청 시 formData 다루기 (json Data 함께 넣기, 여러개 파일 넣기)

dayannne·2024년 4월 7일
0

React

목록 보기
11/13
post-custom-banner

1. formData 안에 json Data 넣기

보통 이미지/오디오 업로드 시 formData를 사용해 API 요청을 하게 되는데,
백엔드에서 formData 안에 application/json 형식의 json data와 파일을 함께 넣어서 요청하는 방식으로 구현해 주셨다.
처음에 보고 읭 json data를 formData에 넣을 수 있다고? 싶었는데 알고 보니 방법이 있었다.

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
	const file = event.target.files[]
 
    if(file){
       const data = { fileType : "abcde" } // 백엔드에서 요청한 json 예시
       const formData = new FormData();

       formData.append(
          "data",
          new Blob([JSON.stringify(data)], {
            type: "application/json",
          }) // application/json 형식으로 넣어 주기
       );
       // 하나의 파일
       formData.append("image", file) // multipart/form-data 형식, 업로드할 파일 집어넣기

       // ...API 요청
     }

	// ...
}

Blob을 사용해 첫번째 인수에 JSON.stringify()로 변환해 준 데이터, 그리고 type을 application/json로 지정하면 application/json 형식으로 json data를 보낼 수 있다.


2. formData 안에 여러 개의 파일 집어넣기

파일 업로드 API가 여러 개의 파일 업로드가 가능하도록 구현되어 있는 경우이다. 어떻게 여러 개의 파일을 동시에 보낼 수 있는지 싶었는데 이것도 아주 간단한 방법이!

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  const files = event.target.files;

  if (files) {
    const formData = new FormData();
	// 여러 개의 파일
    Object.values(files).forEach((file) =>
       if(type){
      	formData.append("files", file, file.name)
       }
    );
    // ...API 요청
  }
  //...
}

Object 형식인 files의 values(file)만 가져와서 각 file마다 key name, file, file 이름을 formData에 담아 주면 끝!


3. FileReader로 보여주는 방식에서 API 응답 데이터를 가져와 보여주기 (feat. useMutation)

이대로 마무리하기 아쉬워서...
백엔드 API가 만들어 지기 전 FileReader 객체로 화면에서 파일 관련 정보(이미지 미리보기, 오디오 정보 등) 확인해 볼 수 있도록 로직을 먼저 구현해 놓고 API를 나중에 적용하게 될 때의 경우이다.

setState로 값을 변경하는 방식은 동일하게 적용하되,
아예 업로드 API 요청 후 응답 데이터 값을 가져와 뿌려주도록 'file 이벤트 함수에서 useMutation을 통한 API 요청 + setState를 통한 값 변경' 방식의 적용은 다음과 같이 구현할 수 있다.
(이전의 json Data 넣기, 여러 개의 파일 집어넣기까지 모두 포함해 보았다!)

  • 변경 전

    // ** 1) Recoil atom의 useSetRecoilState / 또는 useState 의 setState props 가져오기 
    // ** 2) POST API 요청을 위한 useMutate 훅 가져오기
    const { mutate:audioUpload } = usePostAudioUpload();
    
    const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
      const files = event.target.files;
    
      const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onloadend = (e) => {
          const newAudio = new Audio(e.target?.result as string);
    
          newAudio.onloadedmetadata = () => {
            setData((prev) => {
              // audio file과 재생시간 값 가져오기
              const updatedData = prev.map((prevData, i) => {
                if (index === i) {
                  return {
                    ...prevData,
                    audio: file,
                    duration: newAudio.duration,
                  };
                }
                return data;
              });
              return updatedData;
            });
          };
      };
    }
  • 변경 후

    // ** 1) Recoil atom의 useSetRecoilState / 또는 useState 의 setState props 가져오기 
    // ** 2) POST API 요청을 위한 useMutate 훅 가져오기
    const { mutate:audioUpload } = usePostAudioUpload();
    
    const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
      const files = event.target.files;
    
      if (files) {
        const data = { fileType : "abcde" };
        const formData = new FormData();
    
        formData.append(
          "data",
          new Blob(data)], {
          type: "application/json",
          })
        );
        // 여러 개의
        Object.values(files).forEach((file) =>
           formData.append("files", file, file.name)
        );
    	
        // 오디오 업로드 API 요청
        audioUpload(formData, {
          onSuccess: (data) => {
            setData((prev) => {
              const updatedData = prev.map((prevData, i) => {
                if (index === i) { // 기존 data의 index 값과 비교
                  return {
                    ...prevData,
                    audio: data[i].url,
                    duration: data[i].duration,
                  };
                }
                return audio;
              });
              return updatedData;
            });
          },
        });
      }
    }
profile
☁️
post-custom-banner

0개의 댓글