엑셀 파일 업로드를 구현해보자

post-thumbnail

이번에 구현하게 된 기능은 엑셀 파일 업로드이다.



먼저 input을 정의해주고 onChange 함수 작성

<input type="file" accept=".xlsx, .xls" onChange={handleFileChange} />
const uploadedFileRefPut = useRef<File | null>(null);
const handleFileChange = useCallback(async (event, fileRef) => {
    const selectedFile = event.target.files?.[0];

    if (selectedFile) {
       fileRef.current = selectedFile;
       const reader = new FileReader();
       reader.onload = async (e: ProgressEvent<FileReader>) => {
           if (e.target && e.target.result) {
              const data = new Uint8Array(e.target.result as ArrayBuffer);
              const workbook = XLSX.read(data, { type: "array", bookVBA: true });
              const worksheet = workbook.Sheets[workbook.SheetNames[0]];
              const jsonData: ExcelData[] = XLSX.utils.sheet_to_json(worksheet);
          }
      };

        reader.readAsArrayBuffer(selectedFile);
   }
}, []);

버튼 클릭 시 업로드를 수행 할 함수 작성

// 엑셀 파일로 일괄 수정
    const handlePutExcel = (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();

        if (!uploadedFileRefPut.current) {
            toast.error("엑셀 파일을 업로드 해주세요.");
            return;
        }

        const formData = new FormData();

        formData.append("file", uploadedFileRefPut.current);
        uploadUserExcelPut(formData);
    };




최종 구현된 코드는 위와 같지만 초기에 upload할 파일을 useState로 상태를 관리하고 있었다.

const [uploadedFile, setUploadedFile] = useState<File | null>(null);
  const jsonData: ExcelData[] = XLSX.utils.sheet_to_json(worksheet);
  setUploadedFile(jsonData);
}

이렇게 onChange 됐을 때 setUploadedFile로 상태를 업데이트 해 주었음에도 업로드 버튼을 클릭하면 계속 엑셀 파일이 없다는 toast 에러가 뜨는 문제가 발생했다.

디버깅을 해보니 uploadedFile이 null로 찍히고 있었다...
왜 null이지? 나는 분명 setUploadedFile로 업데이트를 해주었는데...


한참 헤맨 끝에 찾은 원인은 uploadedFile이 클로저이기 때문이었다.

❔ 원인
엑셀 파일 업로드라는 버튼을 클릭하면 input이 있는 모달이 뜨는데 모달이 열린 시점에는 uploadedFile 상태를 클로저로 기억하고 있어서 상태가 업데이트된 이후에도 null 값을 유지하고 있었다.


두 가지의 해결 방법이 있다.


❕방법 1

uploadedFile 상태가 변경될 때마다 handleUpload 함수를 새로 정의하기 위해 useCallback으로 감싸고 의존성 배열에 uploadedFile을 추가하기

    const handleUpload = useCallback(async () => {
        if (!uploadedFile) {
            toast.error("엑셀 파일을 업로드 해주세요.");
            return;
        }

        const formData = new FormData();
        formData.append("file", uploadedFile);

        await uploadUserExcel(formData);
    }, [uploadedFile]);

❕방법 2

useRef를 사용하여 파일 상태를 저장하고 이를 참조하여 항상 최신 상태를 유지하는 방법

위의 최종 코드와 동일

두 방법 모두 상태가 변경될 때마다 handleUpload 함수가 최신 상태를 참조하게 되어 uploadedFile 상태의 동기화를 유지할 수 있게 된다!

나는 상태변경 이후 추가적인 UI의 변경이 없고 리렌더링이 필요하지 않았기 때문에 두 번째 방법으로 구현하기로 했다!

자바스크립트의 기본기가 중요하다는 것을 다시금 느끼게 된 과정이었다.



🧷 참고

0개의 댓글