React

ExcelJS 다운로드 & 엑셀 파일 업로드

minsun309 2024. 8. 24. 12:40

프로젝트를 진행하면서 엑셀 다운로드 작업 시 자주 사용하는 exceljs 대해 정리했다.

 

👉 선택 이유

npm trends를 보면 지난 1간 라이브러리 다운로드 횟수입니다. xlsx 다운로드 수가 더 많지만 ExcelJS를 선택한 이유는 다음과 같습니다.

 

  exceljs xlsx
스타일링과 서식 지정 세밀한 스타일링이 필요할 때 유리
( 다양한 서식 기능, 차트, 이미지, 다양한 셀 유형을 지원 )
복잡한 서식이나 스타일링에는 제한
(기본적인 읽기, 쓰기 기능을 제공)
호환성과 데이터 처리 Excel 파일 => JSON 형식, JSON 데이터 => Excel로 변환하는 기능이 있음
----
세밀한 데이터 조작, 복잡한 데이터 처리 요구 사항 가능
대량의 데이터를 신속하게 처리할 수 있지만, 복잡한 데이터 변환이나 고급 조작에는 제약
파일 포맷 xlsx, csv xlsx, csv, html, json 등
*스트림 지원 O
( 복잡한 스트림 처리 작업을 더 간편하게 할 수 있음 )
X
( *레거시 포맷을 지원하기 때문에 스트림 API가 복잡하거나 사용하기 어려움)

* 스트림 읽기(Streaming Read) : 대량의 데이터를 처리할 때 메모리 사용을 최적화하고 성능을 개선하기 위해 사용되는 기법으로 데이터를 한 번에 모두 읽는 대신, 데이터의 일부분씩 순차적으로 읽어 처리합니다. 이 방법은 특히 대규모 파일을 다룰 때 유용합니다

 

*레거시 포맷 : 레거시 포맷 (예: xls)은 최신 포맷 (예: xlsx)과 구조가 다르기 때문에, 스트림 API를 사용하여 이러한 파일을 처리할 때 복잡성이 증가할 수 있습니다. 레거시 포맷의 경우, 데이터 구조와 파일 형식의 차이로 인해 스트리밍 읽기와 쓰기 작업이 더 복잡할 수 있습니다.

 

결론

exceljs는 복잡한 Excel 파일의 서식, 스타일링, 고급 기능을 지원하고, JSON과의 호환성으로 데이터 처리 및 조작이 유리하여, 프로젝트의 요구 사항에 적합하다고 판단되었습니다. xlsx는 성능 면에서는 우수하지만, 서식 및 스타일링의 제약과 복잡한 데이터 조작의 어려움으로 인해 exceljs를 선택하게 되었습니다.


사용법

exceljs npm

 

exceljs

Excel Workbook Manager - Read and Write xlsx and csv Files.. Latest version: 4.4.0, last published: 10 months ago. Start using exceljs in your project by running `npm i exceljs`. There are 1530 other projects in the npm registry using exceljs.

www.npmjs.com

 

exceljs 설치

npm install exceljs

 

엑셀 파일 생성 및 다운로드

import ExcelJS from "exceljs"

export default function ExcelDownload() {
  const data = [
    {
      userID: "12345678",
      userName: "ABC",
      userEmail: "abc@gmail.com",
      createTime: "2023-11-26 10:11:00",
    },
    {
      userID: "46578945",
      userName: "DEF",
      userEmail: "def@gmail.com",
      createTime: "2023-11-26 12:51:10",
    },
    {
      userID: "98745651",
      userName: "가나",
      userEmail: "가나@gmail.com",
      createTime: "2023-11-26 22:30:00",
    },
  ];

  const [userDataLoading, setUserDataLoading] = useState(false);
  const handleExcelDownload = async () => {
    setUserDataLoading(true); // 로딩

    const workbook = new ExcelJS.Workbook();
    setUserDataLoading(false);  // 로딩
    const worksheet = workbook.addWorksheet("My Sheet");
    worksheet.columns = [
      {
        header: "userID",
        key: "userID",
        width: 20,
        style: { alignment: { horizontal: "left" } },
      },
      {
        header: "닉네임",
        key: "userName",
        width: 20,
        style: { alignment: { horizontal: "left" } },
      },
      {
        header: "이메일",
        key: "userEmail",
        width: 30,
        style: { alignment: { horizontal: "left" } },
      },
      {
        header: "생성날짜",
        key: "createTime",
        width: 30,
        style: { alignment: { horizontal: "left" } },
      },
    ];
    for (let i = 0, j = data.length; i < j; i++) {
      worksheet.addRow({
        userID: data[i].userID,
        userName: data[i].userName,
        userEmail: data[i].userEmail,
        createTime: data[i].createTime.replace(/-/g, "."),
      });
    }

    workbook.xlsx.writeBuffer().then((buffer) => {
      const blob = new Blob([buffer], {
        type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      });
      const excelDonwloadUrl = window.URL.createObjectURL(blob);
      const link = document.createElement("a");
      link.href = excelDonwloadUrl;

      link.setAttribute("download", `엑셀내역_${formatDate(new Date())}.xlsx`);
      document.body.appendChild(link);
      link.click();
    });
  };

  return (
    <>
      <section>
         <button css={excelBtn} onClick={() => handleExcelDownload()}>
            <Image
              src={"/excelIcon.png"}
              alt="excelIcon"
              width={18}
              height={18}
            />
            <span>엑셀 다운로드</span>
         </button>
      </section>
      {userDataLoading && (
        <div css={excelDownloadLoading}>
          <span>엑셀 다운로드중입니다.</span>
        </div>
      )}
    </>
  );
}

const excelBtn = css`
  width: 200px;
  padding: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #207245;

  img {
    filter: grayscale(1) brightness(3);
  }

  span {
    color: #fff;
    margin-left: 5px;
    font-weight: 500;
  }
`;

const excelDownloadLoading = css`
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: 20;
  background-color: rgba(0, 0, 0, 0.6);

  span {
    margin-top: -15px;
  }

  @media (max-width: 1023px) {
    height: 100vh;
  }
`;

 

다운로드 된 엑셀 파일

 

 

엑셀 파일 업로드

import ExcelJS from "exceljs"

export default function ExcelUpload() {
	const [selectedFile, setSelectedFile] = useState<File | null>(null);
  const onFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    if (event.target.files && event.target.files[0]) {
      setSelectedFile(event.target.files[0]);
    }
  };

 const onReadExcelClick = async () => {
    //서버로 파일을 업로드할 때 사용
    const formData = new FormData();
    formData.append("file", selectedFile);
    try {
      const response = await fetchCreateListByExcel(url, token, formData)
    } catch (error) {
      console.log(error)
    }

    console.log(selectedFile);

    // 엑셀 파일 읽기
    if (selectedFile) {
      const workbook = new ExcelJS.Workbook();
      const fileBuffer = await selectedFile.arrayBuffer();

      await workbook.xlsx.load(fileBuffer);

      workbook.eachSheet((sheet) => {
        sheet.eachRow((row, rowNumber) => {
          console.log(`Row ${rowNumber}: ${row.values}`);
        });
      });
    }
 };

 return (
    <>
      <section>
          <div css={excelUploadBtn}>
            <input
              id="file-input"
              type="file"
              style={{ display: "none" }}
              onChange={onFileChange}
            />
            <label htmlFor="file-input" className="inputLabel">
              {selectedFile ? (
                <span style={{ color: "#BCBCBC" }}>{selectedFile.name}</span>
              ) : (
                <span style={{ color: "#BCBCBC" }}>
                  등록할 파일을 첨부해주세요
                </span>
              )}
            </label>
            <button className="uploadBtn" onClick={() => onReadExcelClick()}>
              엑셀 업로드
            </button>
          </div>
      </section>
    </>
  );
}

const excelUploadBtn = css`
  width: 500px;
  padding: 10px;

  .inputLabel {
    padding: 10px 50px;
    border: 1px solid #fff;
  }

  .uploadBtn {
    background-color: #207245;
    margin-left: 10px;
    font-weight: 500;
    padding: 13px;
    color: #fff;
  }
`;

 

 

업로드 된 엑셀 파일

  • 미 업로드

 

  • 업로드 시 파일 명 보임

 

  • 엑셀 내용