exceljs를 이용해 엑셀 파일 다운로드 받기

손연주·2022년 5월 8일
1

typescript, Nestjs, typeORM, RDBMS

현재 회사에서 이미지 어노테이션과 세그멘테이션을 진행하는데, 이 과정에서 작업자가 개입하게 된다. 사진의 장수 또는 작업한 라벨의 개수만큼 정산을 하는데 지금 방식으로는 운영팀이 프로젝트가 끝나면 작업자 정산 데이터를 요청하고, 본인이 DB에 쿼리를 날려 정산 데이터를 전달해주곤 했다.

하지만 이러한 일련의 과정이 비효율적이기 때문에 웹에서 버튼 하나만 누르면 해당 프로젝트의 전체 작업자 목록과 그에 해당하는 정산 값들을 모아 엑셀로 다운받을 수 있는 엑셀 API를 만들어 보려고 한다.

Flow

  1. workbook 생성
  2. workbook의 worksheet 생성
  3. worksheet에 column 추가
  4. worksheet의 column에 데이터 추가
  5. 프론트에서 버튼을 클릭했을 때 엑셀 파일이 다운로드 되게하기

구조

  @Post('excel')
  @ApiOperation({ summary: 'Get a excel of profit data' })
  async downloadProfit(@Body() params: ExcelProfitDto, @Res() res: Response) {
    await this.profitService.downloadProfitWithExcel(params, res);
  }
  async downloadProfitWithExcel(params: ExcelProfitDto, res: Response) {
    const rawDatas = await this.findBySubtaskIdForExcel(params);
    const columns = await this.excelService.makeColumnsBySubtaskId();
    await this.excelService.getExcelFileForProfitData(res, columns, rawDatas);
  }

함수 작성

column 만들기

  makeColumnsBySubtaskId = () => {
    const columns = [
      { header: 'subtaskId', key: 'subtaskId', width: 20 },
      { header: 'userId', key: 'userId', width: 20 },
      { header: 'name', key: 'name', width: 20 },
      { header: 'email', key: 'email', width: 35 },
      { header: 'workerProfit', key: 'workerProfit', width: 20 },
      { header: 'reviewerProfit', key: 'reviewerProfit', width: 20 },
    ];
    return columns;
  };

DI를 위해서 컬럼을 return하는 함수를 따로 만들었다. 이는 엑셀의 틀을 만드는 거다. 사실 엑셀 써본적 없는데 이번 기회로 엑셀을 만져봤다.. 어쨌든 width는 결과값 보면서 조금씩 조정하면 된다. 나는 email 문자열이 긴 게 있어서 35로 정해놨다.

Flow 1~5번 코드 파악

  async getExcelFileForProfitData(
    res: Response,
    columns: Array<object>,
    rawData: { [key: string]: string | number }[],
  ) {
    
    // 1. workbook 생성
    const workbook = new Workbook();
    // 2. worksheet 생성
    const worksheet = workbook.addWorksheet('Profit Sheet');
    // 3. 미리 만들어놨던 컬럼 함수 return값을 파라미터로 받아 추가하기
    worksheet.columns = columns;
	
    // 4. 미리 작성해둔 ORM query문에서 파라미터로 받아온 데이터들을
    for (const raw of rawData) {
      const { subtaskId, userId, name, email, workerProfit, reviewerProfit } = raw;
		
      // 4. column에 추가하기
      worksheet.addRow({
        subtaskId,
        userId,
        name,
        email,
        workerProfit,
        reviewerProfit,
      });
    }

    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    res.setHeader(
      'Content-Disposition',
      'attachment; filename=' + 'userProfit.xlsx',
    );
	
    // 5. 다운로드 받기
    await workbook.xlsx.write(res);
    res.end();
  }

엑셀 파일이 다운로드가 안될 때

workbook.xlsx.writeFile를 쓰지 말고 response.setHeader 해준 뒤 workbook.xlsx.write 해준 다음 response.end()로 작성한다.

본인도 처음에는 writeFile을 써서 로컬에서만 다운이 됐었다. 아래 참고에서 맨 마지막 문서를 확인하자.

참고

profile
할 수 있다는 생각이 정말 나를 할 수 있게 만들어준다.

2개의 댓글

comment-user-thumbnail
2022년 5월 13일

진짜 너무 잘 하셨네요!!!!!!(사실 제대로 안 봄)

1개의 답글