fix: SeedParser 숫자 셀 절삭 및 수식 셀 공백 처리 버그 수정
- 연번1 등 NUMERIC 셀을 (long) 캐스팅해 절삭하던 것을 Math.round로 교체.
부동소수점 오차(예: 6.999999999999998)가 실제 값보다 한 자리 작은
기관번호("006")로 읽혀 채널명이 잘못 생성되는 문제를 막는다.
- FORMULA 셀은 default 분기로 빠져 항상 빈 문자열로 읽히던 것을 고쳐,
캐시된 수식 결과 타입(getCachedFormulaResultType)에 따라 STRING/NUMERIC
처리 로직을 재사용하도록 cellValue(Cell, CellType) 재귀 구조로 정리.
- 두 결함을 각각 재현하는 테스트 추가(부동소수점 절삭, 수식 셀 캐시값 읽기).
Co-Authored-By: Claude Opus 4.8 (1M context)
@ce307c115a5fcdbc64ac507342ed55ae2bf6ae7d
--- src/main/java/kr/itn/itnhub/seed/SeedParser.java
+++ src/main/java/kr/itn/itnhub/seed/SeedParser.java
... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 |
|
| 3 | 3 |
import kr.itn.itnhub.provision.ChannelNaming; |
| 4 | 4 |
import org.apache.poi.ss.usermodel.Cell; |
| 5 |
+import org.apache.poi.ss.usermodel.CellType; |
|
| 5 | 6 |
import org.apache.poi.ss.usermodel.Row; |
| 6 | 7 |
import org.apache.poi.ss.usermodel.Sheet; |
| 7 | 8 |
import org.apache.poi.ss.usermodel.Workbook; |
... | ... | @@ -108,10 +109,22 @@ |
| 108 | 109 |
if (cell == null) {
|
| 109 | 110 |
return ""; |
| 110 | 111 |
} |
| 111 |
- return switch (cell.getCellType()) {
|
|
| 112 |
+ return cellValue(cell, cell.getCellType()); |
|
| 113 |
+ } |
|
| 114 |
+ |
|
| 115 |
+ /** |
|
| 116 |
+ * cellType으로 셀 값을 문자열로 변환한다. FORMULA는 수식을 평가하지 않고 |
|
| 117 |
+ * 파일에 저장된 캐시 결과 타입({@link Cell#getCachedFormulaResultType()})으로 재귀 위임해
|
|
| 118 |
+ * STRING/NUMERIC/BOOLEAN 처리 로직을 그대로 재사용한다. |
|
| 119 |
+ */ |
|
| 120 |
+ private String cellValue(Cell cell, CellType cellType) {
|
|
| 121 |
+ return switch (cellType) {
|
|
| 112 | 122 |
case STRING -> cell.getStringCellValue().trim(); |
| 113 |
- case NUMERIC -> String.valueOf((long) cell.getNumericCellValue()); |
|
| 123 |
+ // 반올림: 부동소수점 오차로 6.999999999999998처럼 저장된 값이 절삭되어 |
|
| 124 |
+ // 한 자리 작은 값("006")으로 읽히는 것을 방지한다.
|
|
| 125 |
+ case NUMERIC -> String.valueOf(Math.round(cell.getNumericCellValue())); |
|
| 114 | 126 |
case BOOLEAN -> String.valueOf(cell.getBooleanCellValue()); |
| 127 |
+ case FORMULA -> cellValue(cell, cell.getCachedFormulaResultType()); |
|
| 115 | 128 |
default -> ""; |
| 116 | 129 |
}; |
| 117 | 130 |
} |
--- src/test/java/kr/itn/itnhub/seed/SeedParserTest.java
+++ src/test/java/kr/itn/itnhub/seed/SeedParserTest.java
... | ... | @@ -149,6 +149,51 @@ |
| 149 | 149 |
} |
| 150 | 150 |
|
| 151 | 151 |
@Test |
| 152 |
+ void 연번1이_부동소수점_오차로_정수보다_살짝_작아도_반올림한다() throws Exception {
|
|
| 153 |
+ try (XSSFWorkbook wb = new XSSFWorkbook(); |
|
| 154 |
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
|
| 155 |
+ |
|
| 156 |
+ Sheet sheet = wb.createSheet(SeedParser.SHEET_NAME); |
|
| 157 |
+ sheet.createRow(0); |
|
| 158 |
+ sheet.createRow(1); |
|
| 159 |
+ Row row = sheet.createRow(2); |
|
| 160 |
+ // 부동소수점 연산 결과로 흔히 나타나는 오차: 7이 아니라 6.999999999999998로 저장됨 |
|
| 161 |
+ row.createCell(1).setCellValue(6.999999999999998); |
|
| 162 |
+ row.createCell(2).setCellValue(0); |
|
| 163 |
+ row.createCell(5).setCellValue("기후에너지환경부");
|
|
| 164 |
+ wb.write(out); |
|
| 165 |
+ |
|
| 166 |
+ List<SeedRow> rows = parser.parse(new ByteArrayInputStream(out.toByteArray())); |
|
| 167 |
+ |
|
| 168 |
+ assertThat(rows.get(0).orgNo()).isEqualTo("007");
|
|
| 169 |
+ } |
|
| 170 |
+ } |
|
| 171 |
+ |
|
| 172 |
+ @Test |
|
| 173 |
+ void 기관명이_수식_셀이어도_캐시된_문자열_결과를_읽는다() throws Exception {
|
|
| 174 |
+ try (XSSFWorkbook wb = new XSSFWorkbook(); |
|
| 175 |
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
|
| 176 |
+ |
|
| 177 |
+ Sheet sheet = wb.createSheet(SeedParser.SHEET_NAME); |
|
| 178 |
+ sheet.createRow(0); |
|
| 179 |
+ sheet.createRow(1); |
|
| 180 |
+ Row row = sheet.createRow(2); |
|
| 181 |
+ row.createCell(1).setCellValue("001");
|
|
| 182 |
+ row.createCell(2).setCellValue("00");
|
|
| 183 |
+ Cell orgNameCell = row.createCell(5); |
|
| 184 |
+ orgNameCell.setCellFormula("\"국제방송교류재단\"");
|
|
| 185 |
+ // 수식을 평가하지 않고 캐시된 결과 문자열을 직접 심어 둔다(파일에 저장된 캐시값 재현). |
|
| 186 |
+ orgNameCell.setCellValue("국제방송교류재단");
|
|
| 187 |
+ wb.write(out); |
|
| 188 |
+ |
|
| 189 |
+ List<SeedRow> rows = parser.parse(new ByteArrayInputStream(out.toByteArray())); |
|
| 190 |
+ |
|
| 191 |
+ assertThat(rows).hasSize(1); |
|
| 192 |
+ assertThat(rows.get(0).orgName()).isEqualTo("국제방송교류재단");
|
|
| 193 |
+ } |
|
| 194 |
+ } |
|
| 195 |
+ |
|
| 196 |
+ @Test |
|
| 152 | 197 |
void 시트가_없으면_설명이_있는_예외를_던진다() throws Exception {
|
| 153 | 198 |
try (XSSFWorkbook wb = new XSSFWorkbook(); |
| 154 | 199 |
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?