프로젝트를 진행하면서 엑셀 다운로드 작업 시 자주 사용하는 exceljs 대해 정리했다.
👉 선택 이유
npm trends를 보면 지난 1간 라이브러리 다운로드 횟수입니다. xlsx 다운로드 수가 더 많지만 ExcelJS를 선택한 이유는 다음과 같습니다.
exceljs
xlsx
추가 설명
* 스트림 읽기(Streaming Read) : 대량의 데이터를 처리할 때 메모리 사용을 최적화하고 성능을 개선하기 위해 사용되는 기법으로 데이터를 한 번에 모두 읽는 대신, 데이터의 일부분씩 순차적으로 읽어 처리합니다. 이 방법은 특히 대규모 파일을 다룰 때 유용합니다
*레거시 포맷 : 레거시 포맷 (예: xls)은 최신 포맷 (예: xlsx)과 구조가 다르기 때문에, 스트림 API를 사용하여 이러한 파일을 처리할 때 복잡성이 증가할 수 있습니다. 레거시 포맷의 경우, 데이터 구조와 파일 형식의 차이로 인해 스트리밍 읽기와 쓰기 작업이 더 복잡할 수 있습니다.
결론
exceljs는 복잡한 Excel 파일의 서식, 스타일링, 고급 기능을 지원하고, JSON과의 호환성으로 데이터 처리 및 조작이 유리하여, 프로젝트의 요구 사항에 적합하다고 판단되었습니다. xlsx는 성능 면에서는 우수하지만, 서식 및 스타일링의 제약과 복잡한 데이터 조작의 어려움으로 인해 exceljs를 선택하게 되었습니다.
사용법
exceljs npm
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; } `;
업로드 된 엑셀 파일