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

0
post-thumbnail
post-custom-banner

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



먼저 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의 변경이 없고 리렌더링이 필요하지 않았기 때문에 두 번째 방법으로 구현하기로 했다!

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



🧷 참고

post-custom-banner

0개의 댓글