From 7c649f8e9112ef2951991a254488743673fbc4e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B1=EC=98=81?= Date: Mon, 20 Oct 2025 14:15:05 +0900 Subject: [PATCH] =?UTF-8?q?=EC=97=91=EC=85=80=20=EB=8B=A4=EC=9A=B4?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EC=A0=95=EB=A0=AC=20=EC=98=B5=EC=85=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/excel/BaseSxssfExcelFile.java | 77 +++++++++++++++++-- .../egovframework/util/excel/ExcelColumn.java | 13 ++++ .../main/model/CrdnRegistAndViewExcelVO.java | 40 +++++----- 3 files changed, 103 insertions(+), 27 deletions(-) diff --git a/src/main/java/egovframework/util/excel/BaseSxssfExcelFile.java b/src/main/java/egovframework/util/excel/BaseSxssfExcelFile.java index 73f8209..28246cf 100644 --- a/src/main/java/egovframework/util/excel/BaseSxssfExcelFile.java +++ b/src/main/java/egovframework/util/excel/BaseSxssfExcelFile.java @@ -220,6 +220,37 @@ public abstract class BaseSxssfExcelFile implements ExcelFile { short decDf = workbook.createDataFormat().getFormat("#,##0.##"); decimalNumberStyle.setDataFormat(decDf); + // 한글 중요 주석: 정렬 옵션 지원을 위해 기본/숫자 스타일에 대해 좌/중앙/우 정렬 변형을 미리 생성해 재사용한다. + CellStyle baseLeft = workbook.createCellStyle(); + baseLeft.cloneStyleFrom(dataStyle); + baseLeft.setAlignment(HorizontalAlignment.LEFT); + CellStyle baseCenter = workbook.createCellStyle(); + baseCenter.cloneStyleFrom(dataStyle); + baseCenter.setAlignment(HorizontalAlignment.CENTER); + CellStyle baseRight = workbook.createCellStyle(); + baseRight.cloneStyleFrom(dataStyle); + baseRight.setAlignment(HorizontalAlignment.RIGHT); + + CellStyle intLeft = workbook.createCellStyle(); + intLeft.cloneStyleFrom(integerNumberStyle); + intLeft.setAlignment(HorizontalAlignment.LEFT); + CellStyle intCenter = workbook.createCellStyle(); + intCenter.cloneStyleFrom(integerNumberStyle); + intCenter.setAlignment(HorizontalAlignment.CENTER); + CellStyle intRight = workbook.createCellStyle(); + intRight.cloneStyleFrom(integerNumberStyle); + intRight.setAlignment(HorizontalAlignment.RIGHT); + + CellStyle decLeft = workbook.createCellStyle(); + decLeft.cloneStyleFrom(decimalNumberStyle); + decLeft.setAlignment(HorizontalAlignment.LEFT); + CellStyle decCenter = workbook.createCellStyle(); + decCenter.cloneStyleFrom(decimalNumberStyle); + decCenter.setAlignment(HorizontalAlignment.CENTER); + CellStyle decRight = workbook.createCellStyle(); + decRight.cloneStyleFrom(decimalNumberStyle); + decRight.setAlignment(HorizontalAlignment.RIGHT); + // 데이터 시작 행 (제목이 있으면 row 2, 없으면 row 1) int rowIndex = ROW_START_INDEX + titleRowOffset + 1; List fields = getAllFieldsWithExcelColumn(data.getType()); @@ -227,7 +258,10 @@ public abstract class BaseSxssfExcelFile implements ExcelFile { // 각 데이터 객체를 행으로 변환 for (Object record : data.getDataList()) { Row row = sheet.createRow(rowIndex++); - renderDataRow(row, record, fields, dataStyle, integerNumberStyle, decimalNumberStyle); + renderDataRow(row, record, fields, dataStyle, integerNumberStyle, decimalNumberStyle, + baseLeft, baseCenter, baseRight, + intLeft, intCenter, intRight, + decLeft, decCenter, decRight); } // 컬럼 너비 조정 @@ -310,7 +344,14 @@ public abstract class BaseSxssfExcelFile implements ExcelFile { * @param decimalNumberStyle 소수형 숫자 서식 스타일(#,##0.##) * @throws RuntimeException 필드 접근 실패 시 */ - private void renderDataRow(Row row, Object record, List fields, CellStyle baseStyle, CellStyle integerNumberStyle, CellStyle decimalNumberStyle) { + private void renderDataRow( + Row row, + Object record, + List fields, + CellStyle baseStyle, CellStyle integerNumberStyle, CellStyle decimalNumberStyle, + CellStyle baseLeft, CellStyle baseCenter, CellStyle baseRight, + CellStyle intLeft, CellStyle intCenter, CellStyle intRight, + CellStyle decLeft, CellStyle decCenter, CellStyle decRight) { int columnIndex = COLUMN_START_INDEX; try { for (Field field : fields) { @@ -320,6 +361,7 @@ public abstract class BaseSxssfExcelFile implements ExcelFile { // 수식 처리: ExcelColumn에 formula 설정이 있는 경우 수식을 생성하여 설정 ExcelColumn excelColumn = field.getAnnotation(ExcelColumn.class); + ExcelColumn.Align align = (excelColumn != null) ? excelColumn.align() : ExcelColumn.Align.AUTO; if (excelColumn != null && excelColumn.formula() && !excelColumn.formulaRefField().isEmpty()) { String refFieldName = excelColumn.formulaRefField(); int refColumnIndex = findFieldColumnIndex(fields, refFieldName); @@ -329,10 +371,14 @@ public abstract class BaseSxssfExcelFile implements ExcelFile { refField.setAccessible(true); Object refValue = refField.get(record); boolean isBlankRef = (refValue == null) || (refValue instanceof String && ((String) refValue).trim().isEmpty()); + CellStyle baseAligned = baseStyle; + if (align == ExcelColumn.Align.LEFT) baseAligned = baseLeft; + else if (align == ExcelColumn.Align.CENTER) baseAligned = baseCenter; + else if (align == ExcelColumn.Align.RIGHT) baseAligned = baseRight; if (isBlankRef) { Cell cell = row.createCell(columnIndex++); cell.setCellValue(""); - cell.setCellStyle(baseStyle); + cell.setCellStyle(baseAligned); continue; // 다음 필드 처리 } @@ -342,18 +388,35 @@ public abstract class BaseSxssfExcelFile implements ExcelFile { Cell cell = row.createCell(columnIndex++); cell.setCellFormula(formula); - cell.setCellStyle(baseStyle); + cell.setCellStyle(baseAligned); continue; // 다음 필드 처리 } } - // 기본 값 처리: 숫자 타입일 경우 천단위 콤마 서식 적용 + // 기본 값 처리: 숫자 타입일 경우 천단위 콤마 서식 적용 + 정렬 옵션 반영 + boolean isIntNum = (value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long); + boolean isDecNum = (value instanceof Float || value instanceof Double || value instanceof BigDecimal); CellStyle styleToUse = baseStyle; - if (value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long) { + if (isIntNum) { styleToUse = integerNumberStyle; - } else if (value instanceof Float || value instanceof Double || value instanceof BigDecimal) { + } else if (isDecNum) { styleToUse = decimalNumberStyle; } + if (align != ExcelColumn.Align.AUTO) { + if (isIntNum) { + if (align == ExcelColumn.Align.LEFT) styleToUse = intLeft; + else if (align == ExcelColumn.Align.CENTER) styleToUse = intCenter; + else if (align == ExcelColumn.Align.RIGHT) styleToUse = intRight; + } else if (isDecNum) { + if (align == ExcelColumn.Align.LEFT) styleToUse = decLeft; + else if (align == ExcelColumn.Align.CENTER) styleToUse = decCenter; + else if (align == ExcelColumn.Align.RIGHT) styleToUse = decRight; + } else { + if (align == ExcelColumn.Align.LEFT) styleToUse = baseLeft; + else if (align == ExcelColumn.Align.CENTER) styleToUse = baseCenter; + else if (align == ExcelColumn.Align.RIGHT) styleToUse = baseRight; + } + } createCell(row, columnIndex++, value, styleToUse); } } catch (IllegalAccessException e) { diff --git a/src/main/java/egovframework/util/excel/ExcelColumn.java b/src/main/java/egovframework/util/excel/ExcelColumn.java index f8ca912..a5b72aa 100644 --- a/src/main/java/egovframework/util/excel/ExcelColumn.java +++ b/src/main/java/egovframework/util/excel/ExcelColumn.java @@ -29,4 +29,17 @@ public @interface ExcelColumn { * 한글 중요 주석: 기본값은 "=%s%d-TODAY()" 로, "참조셀 - 오늘" 형태의 남은일 계산을 의미. */ String formulaPattern() default "=%s%d-TODAY()"; + + // ==================== 정렬 옵션(선택) ==================== + /** + * 데이터 셀 정렬 옵션 (선택) + * 한글 중요 주석: 미지정(AUTO) 시 기존 기본 정렬을 유지. 필요 컬럼만 LEFT/CENTER/RIGHT 지정. + */ + Align align() default Align.AUTO; + + /** + * 정렬 열거형 + * 한글 중요 주석: AUTO는 어노테이션에서 정렬을 지정하지 않음(기본 유지)을 의미. + */ + enum Align { AUTO, LEFT, CENTER, RIGHT } } diff --git a/src/main/java/go/kr/project/crdn/crndRegistAndView/main/model/CrdnRegistAndViewExcelVO.java b/src/main/java/go/kr/project/crdn/crndRegistAndView/main/model/CrdnRegistAndViewExcelVO.java index 576c49a..d7e3512 100644 --- a/src/main/java/go/kr/project/crdn/crndRegistAndView/main/model/CrdnRegistAndViewExcelVO.java +++ b/src/main/java/go/kr/project/crdn/crndRegistAndView/main/model/CrdnRegistAndViewExcelVO.java @@ -28,7 +28,7 @@ public class CrdnRegistAndViewExcelVO { // ==================== 엑셀 샘플 순서대로 필드 정렬 ==================== /** 관리번호 (단속연도-단속번호) */ - @ExcelColumn(headerName = "관리번호", headerWidth = 15) + @ExcelColumn(headerName = "관리번호", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String mngNo; /** 세움터 관리번호 (공백 처리) */ @@ -36,19 +36,19 @@ public class CrdnRegistAndViewExcelVO { private String sewmtrMngNo; /** 적발일자 */ - @ExcelColumn(headerName = "적발일자", headerWidth = 15) + @ExcelColumn(headerName = "적발일자", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String dsclYmd; /** 행위일자 (현재 DB에 없음 - 필요시 추가) */ - @ExcelColumn(headerName = "행위일자", headerWidth = 15) + @ExcelColumn(headerName = "행위일자", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String actYmd; /** 최초 시정명령 일자 */ - @ExcelColumn(headerName = "최초 시정명령", headerWidth = 15) + @ExcelColumn(headerName = "최초 시정명령", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String crcCmdBgngYmd; /** 최근 부과일 */ - @ExcelColumn(headerName = "최근 부과일", headerWidth = 15) + @ExcelColumn(headerName = "최근 부과일", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String levyBgngYmd; /** 부과금액 */ @@ -56,7 +56,7 @@ public class CrdnRegistAndViewExcelVO { private Long levyAmt; /** 납부일자 (공백 처리) */ - @ExcelColumn(headerName = "납부일자", headerWidth = 15) + @ExcelColumn(headerName = "납부일자", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String payYmd; /** 번지 (지번 전체 주소) */ @@ -69,59 +69,59 @@ public class CrdnRegistAndViewExcelVO { private String dtlAddr; /** 소유자(건축주) */ - @ExcelColumn(headerName = "소유자(건축주)", headerWidth = 20) + @ExcelColumn(headerName = "소유자(건축주)", headerWidth = 20, align = ExcelColumn.Align.CENTER) private String ownrNams; /** 행위자(상호) */ - @ExcelColumn(headerName = "행위자(상호)", headerWidth = 20) + @ExcelColumn(headerName = "행위자(상호)", headerWidth = 20, align = ExcelColumn.Align.CENTER) private String actrNams; /** 가중부과 대상 여부 */ - @ExcelColumn(headerName = "가중부과 대상", headerWidth = 15) + @ExcelColumn(headerName = "가중부과 대상", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String agrvtnLevyTrgtYn; /** 불법행위 (행위 유형) */ - @ExcelColumn(headerName = "불법행위", headerWidth = 25) + @ExcelColumn(headerName = "불법행위", headerWidth = 25, align = ExcelColumn.Align.CENTER) private String actTypeCdNm; /** 세부내용 (현재 DB에 없음 - 필요시 추가) */ - @ExcelColumn(headerName = "세부내용", headerWidth = 25) + @ExcelColumn(headerName = "세부내용(DB X)", headerWidth = 25, align = ExcelColumn.Align.CENTER) private String actDtlCn; /** 용도 */ - @ExcelColumn(headerName = "용도", headerWidth = 20) + @ExcelColumn(headerName = "용도", headerWidth = 20, align = ExcelColumn.Align.CENTER) private String usgIdxCdNm; /** 세부용도 (현재 DB에 없음 - 필요시 추가) */ - @ExcelColumn(headerName = "세부용도", headerWidth = 20) + @ExcelColumn(headerName = "세부용도(DB X)", headerWidth = 20, align = ExcelColumn.Align.CENTER) private String usgDtlCn; /** 불법면적 (AREA - ACTN_WHOL_AREA 계산값) */ - @ExcelColumn(headerName = "불법면적", headerWidth = 15) + @ExcelColumn(headerName = "불법면적", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String illegalArea; /** 구조 (구조지수명) */ - @ExcelColumn(headerName = "구조", headerWidth = 20) + @ExcelColumn(headerName = "구조", headerWidth = 20, align = ExcelColumn.Align.CENTER) private String strctNm; /** 남은일 (서식에서 today - 처분내용별 종료일자, =X3-TODAY(), X3 은 deadline 열과 행 ) */ - @ExcelColumn(headerName = "남은일", headerWidth = 10, formula = true, formulaRefField = "deadline") + @ExcelColumn(headerName = "남은일", headerWidth = 10, formula = true, formulaRefField = "deadline", align = ExcelColumn.Align.RIGHT) private String remainDays; /** 처분내용 (진행단계) */ - @ExcelColumn(headerName = "처분내용", headerWidth = 15) + @ExcelColumn(headerName = "처분내용", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String crdnPrcsSttsCdNm; /** 처분일 */ - @ExcelColumn(headerName = "처분일", headerWidth = 15) + @ExcelColumn(headerName = "처분일", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String crdnPrcsYmd; /** 향후절차 (다음 진행코드명) */ - @ExcelColumn(headerName = "향후절차", headerWidth = 15) + @ExcelColumn(headerName = "향후절차", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String nextPrcsNm; /** 기한 */ - @ExcelColumn(headerName = "기한", headerWidth = 15) + @ExcelColumn(headerName = "기한", headerWidth = 15, align = ExcelColumn.Align.CENTER) private String deadline; /** 특이사항 (비고) */