typescript, Nestjs, typeORM, RDBMS
현재 회사에서 이미지 어노테이션과 세그멘테이션을 진행하는데, 이 과정에서 작업자가 개입하게 된다. 사진의 장수 또는 작업한 라벨의 개수만큼 정산을 하는데 지금 방식으로는 운영팀이 프로젝트가 끝나면 작업자 정산 데이터를 요청하고, 본인이 DB에 쿼리를 날려 정산 데이터를 전달해주곤 했다.
하지만 이러한 일련의 과정이 비효율적이기 때문에 웹에서 버튼 하나만 누르면 해당 프로젝트의 전체 작업자 목록과 그에 해당하는 정산 값들을 모아 엑셀로 다운받을 수 있는 엑셀 API를 만들어 보려고 한다.
Flow
- workbook 생성
- workbook의 worksheet 생성
- worksheet에 column 추가
- worksheet의 column에 데이터 추가
- 프론트에서 버튼을 클릭했을 때 엑셀 파일이 다운로드 되게하기
@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);
}
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로 정해놨다.
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을 써서 로컬에서만 다운이 됐었다. 아래 참고에서 맨 마지막 문서를 확인하자.
진짜 너무 잘 하셨네요!!!!!!(사실 제대로 안 봄)