sheet JS 사용해 보기

2023. 1. 11. 18:16프론트엔드

생성일: 2023년 1월 11일
태그: 프론트 엔드


😊 의영님 업무 기록을 엑셀 파일로 뽑아 주면 좋을것 같아요…

한창 외주 개발 미팅 중에 늘 들었던 말이다. 이제 아웃 라인이 거의 다 완성이 됐으니 진짜 건들여야 할 때가 온것이다.

JS 에서 엑셀을??

말도 안되는 소리로 들리신다면 크나큰 착각입니다. 업무 내역이나, 판매량, 모든 통계 자료에서 쓰이는 엑셀은 개발프로그램과 당연히 뗄레야 뗄 수 없는 관계 인 것이다. 그럼 당연히 ! 엑셀용 라이브러리가 있겠지 라고 찾아보게 됐다…

Sheet JS

SheetJS Community Edition | SheetJS Community Edition

역시 구글 신은 답을 내려 주셨다!

sheetjs라고 js 프로그램에서 엑셀 파일을 내보내거나 가져올 수 있게 하는 라이브러리 였습니다. 지금 개발 환경이 리액트 + 일렉트론이니까 당연히 인터넷에는 수많은 예제가? 존재 하지 않았습니다..

그래도 포기 할 수 없다. 공식문서가 있는데? 영어로 된 예제를 하나하나 보겠습니다.

install

yarn add https://cdn.sheetjs.com/xlsx-0.19.1/xlsx-0.19.1.tgz
  • 설치 방법은 동일합니다. 프로젝트에 맞는 패키지 매니저로 추가해 줍시다.

usage

CommonJS require

By default, the module supports require and it will automatically add support for streams and file system access:

var XLSX = require("xlsx");

CommonJs 로는 이런식으로 모듈을 불러오면 됩니다.

import * as XLSX from 'xlsx';

/* load 'fs' for readFile and writeFile support */
import * as fs from 'fs';
XLSX.set_fs(fs);

/* load 'stream' for stream support */
import { Readable } from 'stream';
XLSX.stream.set_readable(Readable);

/* load the codepage support library for extended support with older formats  */
import * as cpexcel from 'xlsx/dist/cpexcel.full.mjs';
XLSX.set_cptable(cpexcel);

ES6 import 도 사용가능합니다.

Create

Acquiring the data is straightforward with fetch:

const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json();

raw_data.json

"id": { /* (data omitted) */ },
  "name": {
    "first": "John",          // <-- first name
    "last": "Adams"           // <-- last name
  },
  "bio": {
    "birthday": "1735-10-19", // <-- birthday
    "gender": "M"
  },
  "terms": [
    { "type": "viceprez", /* (other fields omitted) */ },
    { "type": "viceprez", /* (other fields omitted) */ },
    { "type": "prez", /* (other fields omitted) */ }
  ]
}
  • sheet js 에서 보면 acquire → filter → reshaping 과정을 거치면서 엑셀로 데이터를 가공합니다.
  • acquire는 json파일을 원본으로 얻는 것이고, filter는 불필요한 데이터를 거르는 것이고 reshaping 은 가공한 데이터를 엑셀에 집어넣는 과정인듯합니다.
  • acquire랑 filter에 대한 사전 지식은 아는걸로 가정하고 reshaping 과정을 봅시다.

For this example, the name will be the first name combined with the last name (row.name.first + " " + row.name.last) and the birthday will be available at row.bio.birthday. Using Array#map, the dataset can be massaged in one call:

const rows = prez.map(row => ({
  name: row.name.first + " " + row.name.last,  birthday: row.bio.birthday
}));

The result is an array of "simple" objects with no nesting:

[  
    { name: "George Washington", birthday: "1732-02-22" },  
    { name: "John Adams", birthday: "1735-10-19" },  *// ... one row per President*
]
  • 원하는 형식을 행과 열에 맞추면서 엑셀 데이터가 되기 직전의 모양 까지 json 배열로 만들어둔 모습입니다.
  • 이렇게 clean dataset 을 만들었으면, 다음은 엑셀로 내보내기를 할 차례 입니다.

With the cleaned dataset, XLSX.utils.json_to_sheet generates a worksheet:

const worksheet = XLSX.utils.json_to_sheet(rows);

XLSX.utils.book_new creates a new workbook and XLSX.utils.book_append_sheet appends a worksheet to the workbook. The new worksheet will be called "Dates":

const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");

sheet.js의 XLSX.utils.json_to_sheet 를 사용해서 json 배열을 워크 시트로 만들고 , workbook을 새로 생성해서 “Dates”라는 파일이름의 워크 시트를 생성합니다.

그럼 이제 내보낼 준비가 다 됐습니다.

** 여기서 글자의 css 나 엑셀에 효과를 더하고 싶으면 유료버전을 사용하면 된답니다. ***

export

❗주의 사항

XLSX.writeFileXLSX only writes XLSX files and is recommended when the export will always be in the .xlsx format. writeFileXLSX is more amenable to tree shaking. This example uses XLSX.writeFile since writeFileXLSX does not support other common export formats like .xls or .xlsb or .csv.

compression: true enables ZIP compression for XLSX and other formats.

  • 대충 XLSX.writeFileXLSX는 .XLSX 확장자만 내보내는걸 지원하는것 같습니다.
XLSX.writeFile(workbook, "Presidents.xlsx", { compression: true });

이제 writeFile 함수를 써서 엑셀을 내보낼 차례 입니다.

모든 데모 코드입니다.

function Presidents() { return ( <button onClick={async () => {
  /* fetch JSON data and parse */
  const url = "https://sheetjs.com/data/executive.json";
  const raw_data = await (await fetch(url)).json();

  /* filter for the Presidents */
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

  /* flatten objects */
  const rows = prez.map(row => ({
    name: row.name.first + " " + row.name.last,
    birthday: row.bio.birthday
  }));

  /* generate worksheet and workbook */
  const worksheet = XLSX.utils.json_to_sheet(rows);
  const workbook = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");

  /* fix headers */
  XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

  /* calculate column width */
  const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
  worksheet["!cols"] = [ { wch: max_width } ];

  /* create an XLSX file and try to save to Presidents.xlsx */
  XLSX.writeFile(workbook, "Presidents.xlsx", { compression: true });
}}><b>Click to Generate file!</b></button> ); }

실전

  • 튜토리얼을 보니까 자신감이 생기는것 같습니다. 한번 나만의 데이터를 가지고 코드를 짜봅니다.
  1. acquire
     useEffect(() => {
         const getEmpList = async () => {
           const empList = await AdminHandler.getEmployeeList();
           setEmpList(empList);
           return;
         };
         getEmpList();
       }, []);
  2. 사원 목록을 불러와 봅니다.
  3. filter - reshaping
  4. 이미 핸들러에 다 필터링 리쉐이핑 됐기 때문에 생략합니다.
  5. create
     const getEmpExcelHandler = () => {
         console.log("write Excel ");
         const worksheet = XLSX.utils.json_to_sheet(empList);
         const workbook = XLSX.utils.book_new();
         XLSX.utils.book_append_sheet(workbook, worksheet, "employees");
       };
    • 그럼 이제 내보낼 준비가 다 됐습니다.
  6. 이제 한번 생성을 해봅니다.
    export위에 코드 밑에 별다른 작업이 없으면 한 줄 추가 해줍니다.
    XLSX.writeFile(workbook, "Employees.xlsx", { compression: true });

내보내기가 잘 되는 모습입니다.

⚠️ 주의 사항

  • 데이터를 가공 하지 않으면 어떻게 되나?? ( 이중 json의 경우 )

→ 정답은? 데이터가 날아가버린다용~ 꼭 가공 작업이 필요할 것 같습니다!

글을 마치면서

  1. 이번 글은 sheetjs 의 사용기를 적어 봅니다. 한글 포스팅이 없어서 제가 직접 써봤습니다.
  2. 다양하게 접목을 하는 방법도 있지만 이번 글에서는 기본적인 기능만 다뤄봤습니다. 매우 쉽게 사용할 수 있는게 sheet js의 장점인것 같습니다.
  3. 클라이언트에서만 데이터를 가공하고 내보낼 수 있으므로 서버의 자원을 조금 덜 사용할 수 있을 것이지만, 가공할 데이터가 너무 크면 속도저하가 발생할 수 있는 원인이 될것 같습니다.