스프링부트 excel 대용량 다운로드 처리 SXSSF

2022. 10. 12. 10:24스프링

jxls 탬플릿으로 프로젝트를 진행했는데 만 건 이상 데이터는 무려 7초나 걸리는 상황이라 교체를 할 수 밖에 없었다.

간단하게 개발하려다 고생만 더 했다. 

poi 라이브러리에서 SXSSF는 대용량 처리에 적합하다.

기존의 HSSF, XSSF는 전체 데이터를 메모리에 저장해서 사용하기 때문에 메모리가 다 차면 문제가 생긴다.

SXSSF는 메모리 부족을 방지하기 위해 메모리에 있는 데이터를 주기적으로 임시 파일로 옮긴다.

서버에 메모리를 주기적으로 비워주며 성능부하가 덜하다.

교체 결과 7초 걸리던 시간이 1초로 단축됐다.

// workbook 생성
SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(-1);
Sheet sheet = sxssfWorkbook.createSheet(sheetName);
// 임시파일 비우기 
sxssfWorkbook.setCompressTempFiles(true);

일단 워크북을 생성하고 임시파일을 압축하기 위한 설정을 true로 해준다.

createeSheet 파라미터에는 sheet에 이름을 넣어준다 String 형태로

 

// 임시데이터 비우기
((SXSSFSheet)sheet).flushRows(list.size());

   res.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", URLEncoder.encode("테스트.xlsx", "UTF-8")));
OutputStream target = res.getOutputStream();

sxssfWorkbook.write(target);
target.close();
sxssfWorkbook.dispose();

res는 메소드에 HttpServletResponse  res 파리미터다. 

 

poi 라이브러리 같은 경우 자바단에서 모든 스타일을 먹여야 하기 때문에 조금 귀찮다.

 

// 1열 생성
Row hdrRow = sheet.createRow(0);
// 셀생성 A1~F1
for(int i=0; i<=5; i++) {
   Cell c0 = hdrRow.createCell(i);
   c0.setCellStyle(defaultStyle);
}

이런식으로 생성을 하고 

sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 5));
Row row =  sheet.getRow(0);
Cell cell = row.getCell(0);
cell.setCellValue("공급자");
cell.setCellStyle(defaultStyle);

병합도 한다. 저 병합은 0번째 row부터 0번째 row까지 여기서는 그럼 0번째 로우 한줄이다.

예를 들어 0, 1이면 0번째로우,1번째로우를 합친 것 

뒤에 두개는 범위다 0번째 셀부터 5번째 셀까지 병합하라는 뜻이다.

 

// defaultStyle 생성
XSSFCellStyle defaultStyle = (XSSFCellStyle) sxssfWorkbook.createCellStyle();
Font font = sxssfWorkbook.createFont();
font.setFontHeightInPoints((short) 10);
font.setFontName("맑은 고딕");
defaultStyle.setFont(font);
defaultStyle.setFillForegroundColor(new XSSFColor(new byte[] {(byte) 192,(byte) 192,(byte) 192}, null));
defaultStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 테두리 설정
defaultStyle.setBorderTop(BorderStyle.THIN);
defaultStyle.setBorderLeft(BorderStyle.THIN);
defaultStyle.setBorderRight(BorderStyle.THIN);
defaultStyle.setBorderBottom(BorderStyle.THIN);
// 중앙 정렬
defaultStyle.setAlignment(HorizontalAlignment.CENTER);
defaultStyle.setVerticalAlignment(VerticalAlignment.CENTER);

스타일 또한 마음대로 줄 수 있다.

주의점은 

cell.setCellValue(ct.getName() != null ? ct.getName() : "");

이렇게 삼항연산자 처리를 안해주고 그냥 넣고 돌리면 null을 만나면 exception 떨어진다.

이런 식으로 넣는게 좋다

 

스타일, 로우, 셀 생성 하기 위한 메소드를 따로 구현하는 편이 나을 거 같다. 시간이 없어서 이렇게 했지만

나중에 Util을 하나 만들어서 리팩토링해야 할 것 같다.