프론트엔드 업무에서 가장 빈번하게 다루는 도구들 중에는 프린터기, 스캐너, 엑셀, PDF, Word 등이 있습니다.
(저는 커머셔 회사의 IT 부서여서 더 빈번하게 다룹니다. 🤦♂️)
일반적으로 테이블 형태로 정리된 데이터는 매니지먼트 팀에서 엑셀로 추출하여 확인하길 원합니다.
이러한 엑셀 파일을 활용하여 보고서를 작성하거나 다양한 용도로 활용할 수 있습니다.
그래서 오늘은 React를 사용하여 엑셀 파일을 다루는 방법을 알아보려고 합니다.
라이브러리 사용법을 올리는 것에 대해 의문을 품으실 수 있지만,
공식 문서보다 좀 더 친절한 설명서를 제공하려고 합니다. (영어로만 된 문서도 있으니까요!)
단순히 코드만 공유하는 것뿐만 아니라,
잡다한 TMI(Too Much Information)도 함께 담을 예정이니 많은 기대 부탁드립니다.
이번 리액트 - 엑셀 시리즈는 총 3개의 파트로 구성될 예정입니다.
1. 라이브러리 사용방법 기초편
2. 컴포넌트화하여 재사용성 향상
3. 다중 탭 다루기
오늘은 이 중에서도 가장 간단한 파트인 라이브러리 사용방법 기초편을 살펴보려고 합니다.
목표: 데이터를 테이블 형식의 엑셀 파일로 다운로드
먼저 엑셀을 다루기 위해 가장 유명한 두 가지 라이브러리가 있습니다.
(리액트 설치 등은 생략하겠습니다.)
1. xlsx
- 이 라이브러리는 npm 기준 480위에 해당하는 다운로드 수로 매우 인기가 많습니다.
- 가볍게 보고서 작성이나 데이터 정리에 사용하기에 제격인 것 같습니다.
- 이 라이브러리는 기능적인 측면에서 압도적인 성능을 보여줍니다.
- 따라서 데이터 처리와 엑셀 다운로드에 중점을 두는 사용 용도에는 최적입니다.
- 스타일링은 추가 요금이 붙으니 참고하시기 바랍니다.
2. xlsx-js-style
- 이 라이브러리는 npm 기준으로는 위의 라이브러리보다 인기가 적습니다.
- 그러나 이름에서 알 수 있듯이, xlsx-js-style은 xlsx의 스타일링을 무료로 제공합니다.
- 기본 구조는 xlsx를 사용하기 때문에, xlsx를 알고 있다면 배우기 어렵지 않다고 생각합니다.
그래서 오늘은 xlsx를 선택하여 진행 할 예정입니다.
추가로 사용할 다른 라이브러리는 아래와 같습니다.
3. file-saver
- 파일 다운로드를 도와줄 라이브러리입니다.
- npm trends가 증명하듯이, 파일 다운로드의 1인자 라이브러리입니다. 🎅
설치
// 자바스크립트
npm i xlsx file-saver
// 타입스크립트만 설치해주세요.
npm i --save-dev @types/file-saver
import
이제 라이브러리를 내가 사용할 파일의 최상단에서 import해주어야 하는데
여기서도 방법은 두가지로 나뉩니다.
1. 그냥 전체 라이브러리 통 들고와서 사용하기
import * as XLSX from "xlsx";
import * as FileSaver from "file-saver";
2. 내가 사용할 내용만 가지고 와서 사용하기
import { utils, write } from "xlsx";
import { saveAs } from "file-saver";
여기서 어떤 라이브러리를 선택할지 의문이 든다면, 가독성이 좋은 라이브러리를 선택하면 됩니다.
1번 옵션은 모든 기능을 가져오지만, 필요하지 않은 기능을 가져올 필요는 없는게 맞습니다.
핸드맨들이 배수관을 뚫을 때, 정원가위를 가져갈 필요가 없는 것처럼, 라이브러리에서도 필요한 기능만 가져다 사용하는 것이 좋습니다.
라이브러리 모듈이 너무 크거나 무거운 경우에는 2번을 권할 수 있겠지만, 그게 아니라면 크게 중요하지 않습니다.
🔅 그래서 저는 한눈에 알아보기 쉬운 1번을 선택하였습니다.
Frame UI
버튼을 누르면 엑셀파일이 다운로드 되게하려는게 목적이고,
클라이언트 프레임은 간단하게, 화면에 스타일링 안된 버튼 하나만 둘 예정입니다.
<button>엑셀 다운 버튼</button>
엑셀에 들어갈 데이터는 아래와 같습니다.
const data = [
{
name: "Jack",
age: 30,
region: "USA",
occupation: "Lawyer",
},
{
name: "Emily",
age: 25,
region: "Canada",
occupation: "Engineer",
},
{
name: "Sophia",
age: 42,
region: "UK",
occupation: "Doctor",
},
{
name: "Michael",
age: 35,
region: "Australia",
occupation: "Teacher",
},
{
name: "Emma",
age: 28,
region: "Germany",
occupation: "Designer",
},
];
🔅 전체코드:
import * as XLSX from "xlsx";
import * as FileSaver from "file-saver";
const data = [
{
name: "Jack",
age: 30,
region: "USA",
occupation: "Lawyer",
},
{
name: "Emily",
age: 25,
region: "Canada",
occupation: "Engineer",
},
{
name: "Sophia",
age: 42,
region: "UK",
occupation: "Doctor",
},
{
name: "Michael",
age: 35,
region: "Australia",
occupation: "Teacher",
},
{
name: "Emma",
age: 28,
region: "Germany",
occupation: "Designer",
},
];
const App = () => {
return (
<>
<button>엑셀 다운 버튼</button>
</>
);
};
export default App;
이제 버튼을 클릭하면, 가장 기본적으로 엑셀 파일이 위의 데이터와 함께 다운되는거 까지 해보려 합니다.
아래코드는 기본 엑셀 세팅입니다. 파일의 타입, 종류 이름등을 정할수 있습니다.
파일 이름 외엔 건드리지 않는걸 추천합니다.
const excelFileType =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8";
const excelFileExtension = ".xlsx";
const excelFileName = "엑셀파일이름";
아래 코드는 엑셀파일을 만드는 코드이고, 설명은 안에 주석으로 추가했습니다.
const excelDownload = async (data) => {
//처음에 ws라는 worksheet 생성
//worksheet은 세로로 들어가기 때문에 가로로 데이터를 넣을때는 콤마로 구분
// 빈 row를 추가할때는 [``] 이렇게 추가
const ws = XLSX.utils.aoa_to_sheet([
[`엑셀데이터`],
[``],
[`이름`, `나이`, `직업`, `국적`],
]);
// 그리고 내 데이터를 looping해서 보면 이름, 나이, 직업, 국적 이렇게 4 column이 준비되어있고,
// 해당 column에 하나씩 추가
data.map((v) => {
XLSX.utils.sheet_add_aoa(ws, [[v.name, v.age, v.occupation, v.region]], {
origin: -1,
});
ws["!cols"] = [{ wpx: 100 }, { wpx: 100 }, { wpx: 100 }, { wpx: 100 }];
return false;
});
// 마지막으로 wb에 이제 내가 만든 worksheet과 sheet name을 정해야 하는데,
// sheet name은 영어를 사용. (만약 엑셀에 데이터가 안나오면 sheet name을 'data'로 변경해서 체크).
const wb = {
Sheets: { data: ws },
SheetNames: ["data"],
};
// 여기서부턴 실제 엑셀파일 준비하는 곳
const excelButter = XLSX.write(wb, { bookType: "xlsx", type: "array" });
const excelFile = new Blob([excelButter], { type: excelFileType });
// 여기에 1초의 간격을 두어서 혹시 데이터가 다 안 받아진 상태에서 엑셀이 준비되는걸 방지
// 사실 이 아래 await new Promise... 이 코드는 그냥 safe를 위한거지 없어도 상관x
await new Promise((resolve) => setTimeout(resolve, 1000));
// 파일세이버를 이용해 파일을 생성
FileSaver.saveAs(excelFile, excelFileName + excelFileExtension);
};
버튼함수에서 위의 엑셀함수를 불러줍니다.
const handleDownloadExcel = () => {
excelDownload(data);
};
버튼의 onClick 속성을 이용해서 클릭 이벤트를 연결합니다.
<button onClick={handleDownloadExcel}>엑셀 다운 버튼</button>
🔅 전체코드 (JS)
import * as XLSX from "xlsx";
import * as FileSaver from "file-saver";
const data = [
{
name: "Jack",
age: 30,
region: "USA",
occupation: "Lawyer",
},
{
name: "Emily",
age: 25,
region: "Canada",
occupation: "Engineer",
},
{
name: "Sophia",
age: 42,
region: "UK",
occupation: "Doctor",
},
{
name: "Michael",
age: 35,
region: "Australia",
occupation: "Teacher",
},
{
name: "Emma",
age: 28,
region: "Germany",
occupation: "Designer",
},
];
const App = () => {
const excelFileType =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8";
const excelFileExtension = ".xlsx";
const excelFileName = "엑셀파일이름";
const excelDownload = async (data) => {
const ws = XLSX.utils.aoa_to_sheet([
[`엑셀데이터`],
[``],
[`이름`, `나이`, `직업`, `국적`],
]);
data.map((v) => {
XLSX.utils.sheet_add_aoa(ws, [[v.name, v.age, v.occupation, v.region]], {
origin: -1,
});
ws["!cols"] = [{ wpx: 100 }, { wpx: 100 }, { wpx: 100 }, { wpx: 100 }];
return false;
});
const wb = {
Sheets: { data: ws },
SheetNames: ["data"],
};
const excelButter = XLSX.write(wb, { bookType: "xlsx", type: "array" });
const excelFile = new Blob([excelButter], { type: excelFileType });
await new Promise((resolve) => setTimeout(resolve, 1000));
FileSaver.saveAs(excelFile, excelFileName + excelFileExtension);
};
const handleDownloadExcel = () => {
excelDownload(data);
};
return (
<>
<button onClick={handleDownloadExcel}>엑셀 다운 버튼</button>
</>
);
};
export default App;
🔅 전체코드(TS)
import * as XLSX from "xlsx";
import * as FileSaver from "file-saver";
const data: Person[] = [
{
name: "Jack",
age: 30,
region: "USA",
occupation: "Lawyer",
},
{
name: "Emily",
age: 25,
region: "Canada",
occupation: "Engineer",
},
{
name: "Sophia",
age: 42,
region: "UK",
occupation: "Doctor",
},
{
name: "Michael",
age: 35,
region: "Australia",
occupation: "Teacher",
},
{
name: "Emma",
age: 28,
region: "Germany",
occupation: "Designer",
},
];
interface Person {
name: string;
age: number;
region: string;
occupation: string;
}
const App = () => {
const excelFileType =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8";
const excelFileExtension = ".xlsx";
const excelFileName = "엑셀파일이름";
const excelDownload = async (data: Person[]) => {
const ws = XLSX.utils.aoa_to_sheet([
[`엑셀데이터`],
[``],
[`이름`, `나이`, `직업`, `국적`],
]);
data.map((v: Person) => {
XLSX.utils.sheet_add_aoa(ws, [[v.name, v.age, v.occupation, v.region]], {
origin: -1,
});
ws["!cols"] = [{ wpx: 100 }, { wpx: 100 }, { wpx: 100 }, { wpx: 100 }];
return false;
});
const wb = {
Sheets: { data: ws },
SheetNames: ["data"],
};
const excelButter = XLSX.write(wb, { bookType: "xlsx", type: "array" });
const excelFile = new Blob([excelButter], { type: excelFileType });
await new Promise((resolve) => setTimeout(resolve, 1000));
FileSaver.saveAs(excelFile, excelFileName + excelFileExtension);
};
const handleDownloadExcel = () => {
excelDownload(data);
};
return (
<>
<button onClick={handleDownloadExcel}>엑셀 다운 버튼</button>
</>
);
};
export default App;
다운로드 된 엑셀 내부: (스타일링이 없어서 아래처럼 plain 하게 나오지만, 그래도 데이터 추출 성공.)
첫 목표인 데이터를 엑셀에 테이블형식으로 다운로드하기가 끝났습니다.
물론 API를 통해서 가져온 배열형식의 데이터를 엑셀함수의 파라미터 부분에 주고 내부를 수정해주면 작동합니다.
데이터를 엑셀로 추출하는 과정을 컴포넌트화하지 않으면, 여러 곳에서 데이터를 엑셀로 추출해야 할 경우 중복 코드가 많아질 수 있습니다.
재사용성을 고려하여 글로벌하게 컴포넌트화하여 코드를 개선하는 것이 좋습니다. (이거 못하면 중급개발자 못감.)
다음 장에서는 2. 컴포넌트화하여 재사용성을 높이는 방법에 대해 다룰 예정입니다.
이를 통해 코드를 더욱 효율적으로 구성할 수 있을 거라 생각됩니다.
'React > Library' 카테고리의 다른 글
리액트 엑셀 - 5. 데이터 필드 변경 (xlsx, file-saver) (0) | 2023.08.23 |
---|---|
리액트 엑셀 - 4. 여러 탭 활용하기 (xlsx, file-saver) (0) | 2023.06.29 |
리액트 엑셀 - 3. 정렬하기 (xlsx, file-saver) (0) | 2023.06.27 |
리액트 엑셀 - 2. 컴포넌트화하여 재사용성 향상 (xlsx, file-saver) (0) | 2023.06.27 |