스프링부트 - SXSSF 형식으로 엑셀 다운로드 모듈만들기
개요
회사에 입사 후 기존의 엑셀 처리는 Jxls 방식으로 처리되었었다. 이 방식은 미리 템플릿을 원하는 페이지나 형식마다 따로 만들어야했고,
대용량의 데이터를 처리할 땐 너무 오래걸리거나 이로인해 거래처에서 여러번 시도하다가 동시에 Request 가 생겨 OOM 발생이 발생해 서버에 무리를 주는 상황이 발생한 경우도 있었다. 이런 리스크나 성능면에서 떨어져 poi 라이브러리에서 제공되는 sxssf 방식으로 변경업무를 진행했다.
SXSSF 방식은 일정 용량을 넘어선 처리에선 자동으로 메모리를 관리해 서버에 부담이 적다는 점과 속도가 무난하다는 장점이 있다.
더 다양한 이유와 설명이 있겠으나, 자세한 점은 생략하고 본론으로 들어가겠다.
프로세스
기존의 프로세스는 EXCEL 버튼을 클릭할 경우, 페이지에서 요청값을 URL을 통해 보내며 이 정보를 Ajax를 통해 Controller 에서 Mapping한 클래스가 확인하고 HashMap 형식으로 대입하여 mybatis 에서 DB로 정보를 처리하고 리턴값을 통해 템플릿에 데이터를 대입시켜 다운로드가 가능한 엑셀파일로 변환하는 방식이었다.
미리 준비된 템플릿으로 정보를 대입시켜야 하는 번거로움과 템플릿들을 같이 서버에 빌드해야하는 점이 단점이었다.
SXSSF 방식은 poi라이브러리에서 제공되는 클래스로 Java에 백엔드 단에서 모든 편집처리가 가능하고, Jxls 방식처럼 템플릿을 준비할 필요가 없이 미리 설정된 알고리즘으로 처리를 할 수 있어 형식에 맞게 모듈을 바꿔 재사용성이 높다는 점이 장점이다.
그리고 특히나 중요한점이 메모리 관리에 있어 높은 안정성을 생각해 SXSSF 방식으로 구현하기로 하였다.
먼저 회사 DB에서 mybatis가 처리하는 형식은 HashMap 타입으로 자료를 ArrayList형식으로 받고 또 그것을 HashMap으로 감싸
파라미터로 보내 Excel을 처리하는 클래스에서 받아 처리하는 방식으로 데이터가 엄청 복잡하게 감싸져있어 이 데이터를 어떻게 처리해야 성능저하 없이 원활하게 처리할 수 있을지 많이 고민했었고, 자료형 공부에 다시 생각할 수 있는 시간이 되었다.
/*
* SXSSFWorkBook 방식으로 excel 다운로드 처리하기.
*/
public void excelDownload(HttpServletRequest request, HttpServletResponse response,
Map<String, Object> ExcelMap, String fileName, String templateFile, String string)
throws ParsePropertyException, IOException, org.apache.poi.openxml4j.exceptions.InvalidFormatException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
SXSSFWorkbook workbook = null;
Row row = null;
Cell cell = null;
Sheet sheet1 = null;
CellStyle cellStyle = null;
String[] headerName = {
"순번","카드번호","결제금액"
};
String[] headerKey = {
"ROW","CARD_NO","PRICE"
};
int indexRow = 1;
int indexCell = 0;
int index = 0;
try {
ArrayList<Map<String,Object>> list = (ArrayList<Map<String,Object>>) ExcelMap.get("dataList");
String writeDate = (String) ExcelMap.get("date");
System.out.println("list check : " + list);
//워크북 생성
workbook = new SXSSFWorkbook(100);
workbook.setCompressTempFiles(true);
//워크시트 생성
Sheet sheet = workbook.createSheet("sheet1");
//셀스타일 생성
cellStyle = workbook.createCellStyle();
CreationHelper helper = workbook.getCreationHelper();
cellStyle.setDataFormat(helper.createDataFormat().getFormat("#,##0"));
cellStyle.setFillBackgroundColor(IndexedColors.YELLOW.getIndex());
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//행 생성
row = sheet.createRow(0);
//header create
for(String head : headerName){
cell = row.createCell(indexCell++);
cell.setCellValue(head);
cell.setCellStyle(cellStyle);
}
//body
for(int i=0; i<list.size(); i++){
row = sheet.createRow(indexRow++);
Map<String, Object> maps = (Map<String, Object>) list.get(i);
for(int j=0; j<headerKey.length;j++){
if(maps.containsKey(headerKey[j])){
Object value = maps.get(headerKey[j]);
cell = row.createCell(cellInt++);
cell.setCellValue(String.valueOf(value));
}else{
cell = row.createCell(cellInt++);
cell.setCellValue("-");
}
}
}
response.setCharacterEncoding("utf-8");
response.setContentType("application/ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + ".xlsx\";");
workbook.write(bos);
bos.flush();
bos.close();
ServletOutputStream out = response.getOutputStream();
out.write(bos.toByteArray());
out.flush();
out.close();
}catch (IOException e){
e.printStackTrace();
}finally {
workbook.close();
}
}
자료형이 HashMap > ArrayList > HashMap 형식으로 key&value , List, key&value 으로 데이터를 풀어내야했기때문에
여러 가지 알고리즘을 생각했지만, 내가 생각하기에 제일 간단한 방식으로 처리하게 되었다.
기존의 블로그나 구글링을 통한 자료는 우리회사처럼 여러번 감싸져있는 자료형이 아니어서 데이터를 처리하는 부분에서 많이 애를먹었다.
자료형과 알고리즘에 있어서 공부가 부족하다는 생각이 많이 들었고, 많은 공부가 되었다.