본문 바로가기

programming/React

blob/json 형태로 리턴받은 데이터를 엑셀 파일로 추출하기(Get 방식 /Post 방식)

목적 : 테이블 데이터를 엑셀로 Export

 

방법1) 프론트에서 처리 -> UI 상 보여지는 테이블 데이터를 그대로 엑셀 추출
방법2) 백엔드에서 처리 -> 프론트에서 가공되지 않은 원본 데이터를 추출

 

1. 프론트 처리

엑셀 처리를 위한 라이브러리 사용

https://www.npmjs.com/package/xlsx

 

xlsx

SheetJS Spreadsheet data parser and writer. Latest version: 0.18.5, last published: 2 years ago. Start using xlsx in your project by running `npm i xlsx`. There are 4148 other projects in the npm registry using xlsx.

www.npmjs.com

import * as XLSX from "xlsx";

 

ExcelButton 컴포넌트의 onClick 함수

const worksheet = XLSX.utils.json_to_sheet(data);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
XLSX.writeFile(workbook, "OrderList.xlsx");

 

 

2. 백엔드에서 처리하여 excel 파일을 blob 형태로 리턴 -> excel 파일을 링크화하여 로컬에 다운

컴포넌트 호출 부분

import { getOrderListExcelFile } from "hooks/queries/order";


<ExportButton data={orderListData} excelExportFunction={getOrderListExcelFile} className="p-1 me-3 fs--1" />

 

 

API 요청 함수 부분

get 요청의 option에서 responseType : "blob" 로 지정해주어야 함

이 요청 함수를 <ExportButton> 컴포넌트의 excelExportFunction Prop으로 넘겨준다

// order list excel download
export const getOrderListExcelFile = async () => {
  return axiosInstance
    .get(`/order/download`, { responseType: "blob" })
    .then((res) => res.data)
    .catch((error) => error);
};

 

* 추가 Post 로 요청하기

// order list excel download
export const getOrderListExcelFile = async () => {
  return axiosInstance
    .post(`/order/download`, null, { responseType: "blob" })
    .then((res) => res.data)
    .catch((error) => error);
};

 

 

<ExportButton> 컴포넌트 

방법1(프론트), 방법2(백) 동시 커버

API로 리턴받은 데이터와 원하는 추출 파일 형식(Excel) 을 'downloadFile' 함수로 전달

import React from "react";
import { Button } from "react-bootstrap";
import * as XLSX from "xlsx";
import classNames from "classnames";
import { toast } from "react-toastify";
import { downloadFile } from "helpers/download-file";

interface IExportButtonProps {
  data: any;
  excelExportFunction?: () => Promise<any>;
  className: string;
}

const CONTENT_TYPE_EXCEL = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

export default function ExportButton({ data, excelExportFunction, className }: IExportButtonProps) {
  const onClickExcel = async () => {
    // 부모 컴포넌트에서 별도의 다운로드 API 함수(excelExportFunction)를 prop으로 내려보내줬을 경우
    if (excelExportFunction) {
      const res = await excelExportFunction();
      // 에러 발생시 res.response.data에 담긴 blob을 parse해서 error message 출력
      if (res.response && res.response.status !== 200) {
        const blobToText = await res.response.data.text();
        const errorResponse = JSON.parse(blobToText);
        toast.error(errorResponse.message);
        return;
      }
      downloadFile(res, CONTENT_TYPE_EXCEL);
    } else {
      // 부모 컴포넌트에서 다운로드 API를 내려보내지 않은 경우 프론트에서 처리
      const worksheet = XLSX.utils.json_to_sheet(data);
      const workbook = XLSX.utils.book_new();
      XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1");
      XLSX.writeFile(workbook, "OrderList.xlsx");
    }
  };
  return (
    <Button variant="falcon-default" className={classNames(className)} onClick={onClickExcel}>
      Export
    </Button>
  );
}

 

 

blob 말아서 다운로드 링크로 만들고 로컬에 다운로드 하는 함수

리턴 받은 res 데이터(blob), 추출할 컨텐트 타입(Excel 파일 형태) 를 매개변수로 전달받고

<a>링크를 만들고, body 에 추가한 후 click하여 다운로드 후 삭제 처리

import dayjs from "dayjs";
const currentDate = dayjs(new Date()).format("YYYY-MM-DD");

export const downloadFile = (file, contentType) => {
  const a = document.createElement("a");
  const blob = new Blob([file], { type: contentType });
  a.href = window.URL.createObjectURL(blob);
  a.target = "_blank";
  a.download = `downloaded_file_${currentDate}`;
  a.style.display = "none";
  document.body.appendChild(a);
  a.click();
  URL.revokeObjectURL(a.href);
  document.body.removeChild(a);
};