1. 문제점 파악
기존 구글링을 통한 엑셀 다운로드 로직들을 살펴보면서 스터디 하던 도중,
전부 컨트롤러 단에서 구현해야하는 모든 로우를 작성한 다음, 반환하는 수고를 하는 케이스가 대부분인 것을 알게 되었다.
2. 구현 목표
그래서 이를 서비스로 분리해 클래스의 필드 또는 맵으로 작성한 필드의 모음을 파라미터로 보내면 DB에서 해당 필드와 맞는 데이터를 매핑해 로우 단위로 작성한 로직을 개발해 봤다.
3. 코드
public void writeExcelSheetToWorkbook(Workbook workbook, String sheetName, Map<String, String> cellValues, int minWidth, List<Map<String, Object>> dataList) {
try {
Sheet sheet = workbook.createSheet(sheetName);
Row headerRow = sheet.createRow(0);
int cellIndex = 0;
for (String header : cellValues.values()) {
Cell cell = headerRow.createCell(cellIndex++);
cell.setCellValue(header);
}
CellStyle numberStyle = null;
// 데이터 행 작성
for (int rowIndex = 0; rowIndex < dataList.size(); rowIndex++) {
Row dataRow = sheet.createRow(rowIndex + 1);
Map<String, Object> dataMap = dataList.get(rowIndex);
cellIndex = 0;
// cellValues의 key를 사용해 dataList의 값 매핑
for (String key : cellValues.keySet()) {
Cell cell = dataRow.createCell(cellIndex++);
Object value = dataMap.get(key);
if (value != null) {
if (value instanceof String) {
cell.setCellValue((String) value);
} else if (value instanceof Integer) {
cell.setCellValue((Integer) value);
numberStyle = configCellStyleNumeric(workbook, false);
cell.setCellStyle(numberStyle);
} else if (value instanceof Long) {
cell.setCellValue((Long) value);
numberStyle = configCellStyleNumeric(workbook, false);
cell.setCellStyle(numberStyle);
} else if (value instanceof Double) {
cell.setCellValue((Double) value);
numberStyle = configCellStyleNumeric(workbook, true);
cell.setCellStyle(numberStyle);
} else if (value instanceof BigDecimal) {
cell.setCellValue(((BigDecimal) value).doubleValue());
numberStyle = configCellStyleNumeric(workbook, true);
cell.setCellStyle(numberStyle);
} else {
cell.setCellValue(value.toString());
}
} else {
cell.setCellValue("");
}
}
}
adjustColumnWidth(cellValues, minWidth, sheet);
} catch (Exception e) {
e.printStackTrace();
}
}
1) Header Row 설정
파라미터로 받은 CellMap에서 데이터 리스트의 헤더를 추출한다
2) Data Map 활용한 매핑
Data Row를 시트에 생성한 후, 데이터 맵(조회 후 반환받은 결과)을 CellMap에 따라 각 Data Row에 저장하면서, 각 데이터 타입에 따른 서식 설정등을 진행한다
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(Constants.APPLICATION_XLSX));
try {
Workbook workbook = new XSSFWorkbook();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// 데이터 조회 로직
// 리스트에 따라 헤더 로우 매핑 로직
excelService.writeExcelSheetToWorkbook(workbook, "Trade_Data_List_HsCode", cellValueMap, 16, tradeDataListHsCode);
headers.setContentDispositionFormData("attachment", fileName.xlsx);
// 시트 개수에 따른 유효성 로직
workbook.write(outputStream);
byte[] excelBytes = outputStream.toByteArray();
return ResponseEntity.ok()
.headers(headers)
.body(excelBytes);
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(null);
}
}
1) 데이터 조회 후 해당 데이터 리스트를 공통으로 작성한 엑셀 워크북 생성 메서드 로직을 태운다
2) 데이터가 채워진 workBook 워크북 데이터를 파일로 출력하기 위해 write() 메서드 호출 및 ByteArrayOutputStream 사용
3) ByteArrayOutputStream를 byte[]로 변환 후 ResponseEntity에 설정한 header와 body에 값 할당
4. 구현하며 느낀 점
매번 모든 작업을 각 컨트롤러에서 처리하는 것은 매우 비효율적이었다. 그래서 서비스단으로 로직을 분리하고 해당 서비스에 필요한 Row를 컨트롤러에서 설정하고 서식에 필요한 파라미터를 추가해 데이터를 매핑하는 로직을 구현했다.
이를 통해 다양한 엑셀 데이터를 하나의 공통된 메서드를 통해 확인할 수 있게 되었다
'자바☕' 카테고리의 다른 글
엑셀 다운로드를 위한 엑셀 생성 로직 리팩토링 : 속도 개선 (1) | 2024.10.25 |
---|---|
자바 옵셔널(Optional<T>) (0) | 2022.12.11 |
스트림 API (0) | 2022.12.11 |
함수형 인터페이스와 람다 표현식 (0) | 2022.12.11 |
Lombok (0) | 2022.12.06 |