문제 상황 Apache POI를 사용하여 Excel 파일(.xlsx)을 생성하거나 수정할 때 다음과 같은 오류가 발생할 수 있습니다.
java.lang.IllegalStateException: The maximum number of Cell Styles was exceeded. You can define up to 64000 style in a .xlsx Workbook
발생 원인 1. Cell Style 생성 방식의 문제 Excel의 .xlsx 파일 형식에서는 최대 64,000개의 Cell Style 만 생성할 수 있습니다. 이 제한은 Excel 파일 포맷의 스펙에 정의된 하드 리미트입니다.
일반적으로 이 오류는 다음과 같은 상황에서 발생합니다:
for (int i = 0 ; i < rows.size(); i++) { Row row = sheet.createRow(i); Cell cell = row.createCell(0 ); CellStyle style = workbook.createCellStyle(); Font font = workbook.createFont(); font.setBold(true ); style.setFont(font); cell.setCellStyle(style); cell.setCellValue(data.get(i)); }
위 코드에서 10,000개의 행을 처리한다면, 10,000개의 동일한 스타일이 생성되어 메모리를 낭비하고 결국 64,000개 제한에 도달하게 됩니다.
2. .xlsx vs .xls 차이점
.xls (HSSF): 최대 4,000개 의 Cell Style 제한
.xlsx (XSSF): 최대 64,000개 의 Cell Style 제한
해결 방법 방법 1: Cell Style 재사용 (권장) 가장 효율적인 방법은 필요한 스타일을 미리 생성하고 재사용하는 것입니다.
Workbook workbook = new XSSFWorkbook ();Sheet sheet = workbook.createSheet("Data" );CellStyle boldStyle = workbook.createCellStyle();Font boldFont = workbook.createFont();boldFont.setBold(true ); boldStyle.setFont(boldFont); CellStyle normalStyle = workbook.createCellStyle();Font normalFont = workbook.createFont();normalStyle.setFont(normalFont); for (int i = 0 ; i < rows.size(); i++) { Row row = sheet.createRow(i); Cell cell = row.createCell(0 ); if (i % 2 == 0 ) { cell.setCellStyle(boldStyle); } else { cell.setCellStyle(normalStyle); } cell.setCellValue(data.get(i)); }
방법 2: Style Map 활용 여러 종류의 스타일이 필요한 경우, Map을 사용하여 스타일을 관리합니다.
public class ExcelStyleManager { private final Workbook workbook; private final Map<String, CellStyle> styleCache = new HashMap <>(); public ExcelStyleManager (Workbook workbook) { this .workbook = workbook; } public CellStyle getStyle (String styleKey) { return styleCache.computeIfAbsent(styleKey, key -> createStyle(key)); } private CellStyle createStyle (String styleKey) { CellStyle style = workbook.createCellStyle(); Font font = workbook.createFont(); switch (styleKey) { case "BOLD" : font.setBold(true ); break ; case "ITALIC" : font.setItalic(true ); break ; case "HEADER" : font.setBold(true ); font.setFontHeightInPoints((short ) 14 ); style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); style.setFillPattern(FillPatternType.SOLID_FOREGROUND); break ; default : break ; } style.setFont(font); return style; } } ExcelStyleManager styleManager = new ExcelStyleManager (workbook);for (int i = 0 ; i < rows.size(); i++) { Row row = sheet.createRow(i); Cell cell = row.createCell(0 ); if (i == 0 ) { cell.setCellStyle(styleManager.getStyle("HEADER" )); } else { cell.setCellStyle(styleManager.getStyle("BOLD" )); } cell.setCellValue(data.get(i)); }
방법 3: 조건부 스타일 생성 최소화 날짜, 숫자 포맷 등 동적으로 스타일을 생성해야 하는 경우, 필요한 패턴들을 미리 정의합니다.
for (Cell cell : cells) { CellStyle dateStyle = workbook.createCellStyle(); dateStyle.setDataFormat( createHelper.createDataFormat().getFormat("yyyy-mm-dd" ) ); cell.setCellStyle(dateStyle); } CreationHelper createHelper = workbook.getCreationHelper();CellStyle dateStyle = workbook.createCellStyle();dateStyle.setDataFormat( createHelper.createDataFormat().getFormat("yyyy-mm-dd" ) ); for (Cell cell : cells) { cell.setCellStyle(dateStyle); }
Best Practices
스타일을 Workbook 생성 직후에 미리 정의
스타일 팩토리 패턴 사용
StyleManager 클래스를 만들어 중앙 집중식으로 관리
스타일 개수 모니터링
int numCellStyles = workbook.getNumCellStyles();System.out.println("현재 스타일 개수: " + numCellStyles);
클래스 레벨에서 스타일 관리
public class ExcelGenerator { private final CellStyle headerStyle; private final CellStyle dataStyle; private final CellStyle numberStyle; public ExcelGenerator (Workbook workbook) { this .headerStyle = createHeaderStyle(workbook); this .dataStyle = createDataStyle(workbook); this .numberStyle = createNumberStyle(workbook); } }
성능 비교
방식
10,000행 처리 시 스타일 개수
메모리 사용량
매번 생성
10,000개
높음
재사용
3-5개
낮음