2026년 4월 2일 작성
Apache POI
Apache POI는 Apache Software Foundation이 개발한 Java library로, Microsoft Office file format(Excel, Word, PowerPoint)을 순수 Java로 읽고 쓸 수 있으며, HSSF/XSSF/SXSSF 등 format별 전용 component를 제공합니다.
Apache POI
- Apache POI는 Microsoft Office 문서를 Java program에서 읽고 쓸 수 있게 해주는 open source library입니다.
- Word, Excel, PowerPoint 등 다양한 Microsoft Office format을 지원합니다.
- 100% 순수 Java로 구현되어 있어 platform에 관계없이 사용합니다.
- Apache Software Foundation에서 개발하고 유지 관리합니다.
역사 및 배경
- POI project는 1999년에 시작되어 20년 이상의 역사를 가지고 있습니다.
- 초기에는 Excel file 처리만을 목표로 했지만, 현재는 Microsoft Office 전반을 지원합니다.
- POI라는 이름은 “Poor Obfuscation Implementation”의 줄임말로, Microsoft의 복잡한 file format을 해석한다는 의미입니다.
지원하는 File Format
- POI가 지원하는 Microsoft Office file format입니다.
| Format | 확장자 |
|---|---|
| Excel | .xls (Excel 97-2003), .xlsx (Excel 2007+) |
| Word | .doc (Word 97-2003), .docx (Word 2007+) |
| PowerPoint | .ppt (PowerPoint 97-2003), .pptx (PowerPoint 2007+) |
| Visio | .vsd (Visio 2003-2010) |
| Publisher | .pub (Publisher 98+) |
| Outlook | .msg (Outlook message file) |
POI 구성 요소
- POI는 format별로 전용 component를 분리하여 구성합니다.
Core Component
- Microsoft Office file을 처리하는 핵심 component입니다.
| Component | 설명 |
|---|---|
| POIFS (Poor Obfuscation Implementation File System) | Microsoft의 OLE2 compound document format 처리 |
| HSSF (Horrible Spreadsheet Format) | Excel 97-2003 format인 .xls file 처리 |
| XSSF (XML Spreadsheet Format) | Excel 2007+ format인 .xlsx file 처리 |
| SXSSF (Streaming XML Spreadsheet Format) | 대용량 Excel file을 memory 효율적으로 처리 |
Document Processing Component
- Word와 PowerPoint 문서를 처리하는 component입니다.
| Component | 설명 |
|---|---|
| HWPF (Horrible Word Processor Format) | Word 97-2003 format인 .doc file 처리 |
| XWPF (XML Word Processor Format) | Word 2007+ format인 .docx file 처리 |
| HSLF (Horrible Slide Layout Format) | PowerPoint 97-2003 format인 .ppt file 처리 |
| XSLF (XML Slide Layout Format) | PowerPoint 2007+ format인 .pptx file 처리 |
Utility Component
- 공통 기능과 실험적 기능을 포함하는 component입니다.
| Component | 설명 |
|---|---|
| SS (SpreadSheet) | Excel format 간 공통 interface |
| Common | 모든 POI component에서 공통으로 사용하는 utility class |
| Scratchpad | 개발 중이거나 실험적인 기능 |
기본 설정 및 Dependency
- POI를 사용하려면 Maven이나 Gradle로 dependency를 추가합니다.
Maven Dependency 설정
- 기본 POI core library와 필요한 추가 module을 선택적으로 추가합니다.
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.4</version>
</dependency>
Gradle Dependency 설정
build.gradle에 dependency를 추가합니다.
implementation 'org.apache.poi:poi:5.2.4'
Excel 처리 기능
- POI는
XSSFWorkbook으로.xlsxfile을 생성하고,WorkbookFactory로 기존 file을 읽어Sheet,Row,Cell단위로 data를 처리합니다.
Excel File 생성 및 조작
XSSFWorkbook으로 Excel workbook을 생성하고 data를 입력합니다.
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ExcelProcessor {
public void createExcelFile() throws IOException {
// 새 workbook 생성
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Data Sheet");
// Header row 생성
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("이름");
headerRow.createCell(1).setCellValue("나이");
headerRow.createCell(2).setCellValue("부서");
// Data row 생성
Row dataRow = sheet.createRow(1);
dataRow.createCell(0).setCellValue("김철수");
dataRow.createCell(1).setCellValue(30);
dataRow.createCell(2).setCellValue("개발팀");
// File 저장
FileOutputStream fileOut = new FileOutputStream("sample.xlsx");
workbook.write(fileOut);
fileOut.close();
workbook.close();
}
}
Excel File 읽기
WorkbookFactory.create()로 기존 Excel file을 읽어서 data를 추출합니다.
public void readExcelFile(String filePath) throws IOException {
FileInputStream file = new FileInputStream(filePath);
Workbook workbook = WorkbookFactory.create(file);
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for (Cell cell : row) {
switch (cell.getCellType()) {
case STRING:
System.out.print(cell.getStringCellValue() + "\t");
break;
case NUMERIC:
System.out.print(cell.getNumericCellValue() + "\t");
break;
case BOOLEAN:
System.out.print(cell.getBooleanCellValue() + "\t");
break;
default:
System.out.print("UNKNOWN\t");
}
}
System.out.println();
}
workbook.close();
file.close();
}
Word 문서 처리 기능
- POI는
XWPFDocument를 통해.docx문서를 생성하고, 단락(XWPFParagraph)과 표(XWPFTable) 단위로 내용을 읽고 씁니다.
Word 문서 생성
XWPFDocument로 Word 문서를 생성하고 내용을 추가합니다.
import org.apache.poi.xwpf.usermodel.*;
public class WordProcessor {
public void createWordDocument() throws IOException {
XWPFDocument document = new XWPFDocument();
// 제목 추가
XWPFParagraph titleParagraph = document.createParagraph();
titleParagraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun titleRun = titleParagraph.createRun();
titleRun.setText("문서 제목");
titleRun.setBold(true);
titleRun.setFontSize(18);
// 본문 추가
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText("이것은 Apache POI를 사용하여 생성된 Word 문서입니다.");
// Table 추가
XWPFTable table = document.createTable(3, 3);
table.getRow(0).getCell(0).setText("이름");
table.getRow(0).getCell(1).setText("나이");
table.getRow(0).getCell(2).setText("부서");
// File 저장
FileOutputStream out = new FileOutputStream("sample.docx");
document.write(out);
out.close();
document.close();
}
}
Word 문서 읽기
XWPFDocument로 기존 Word 문서의 단락과 표를 읽어옵니다.
public void readWordDocument(String filePath) throws IOException {
FileInputStream fis = new FileInputStream(filePath);
XWPFDocument document = new XWPFDocument(fis);
// 모든 단락 읽기
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
System.out.println(paragraph.getText());
}
// 모든 표 읽기
List<XWPFTable> tables = document.getTables();
for (XWPFTable table : tables) {
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
System.out.print(cell.getText() + "\t");
}
System.out.println();
}
}
document.close();
fis.close();
}
PowerPoint 처리 기능
- POI는
XMLSlideShow를 통해.pptxfile을 생성하고, slide 단위로 text와 shape를 읽고 씁니다.
PowerPoint 생성
XMLSlideShow로 PowerPoint presentation을 생성하고 slide를 추가합니다.
import org.apache.poi.xslf.usermodel.*;
public class PowerPointProcessor {
public void createPresentation() throws IOException {
XMLSlideShow ppt = new XMLSlideShow();
// 첫 번째 슬라이드 추가
XSLFSlide slide1 = ppt.createSlide();
XSLFTextBox title = slide1.createTextBox();
title.setAnchor(new Rectangle(50, 50, 400, 100));
XSLFTextParagraph titleParagraph = title.addNewTextParagraph();
XSLFTextRun titleRun = titleParagraph.addNewTextRun();
titleRun.setText("프레젠테이션 제목");
titleRun.setFontSize(24.0);
titleRun.setBold(true);
// 두 번째 슬라이드 추가
XSLFSlide slide2 = ppt.createSlide();
XSLFTextBox content = slide2.createTextBox();
content.setAnchor(new Rectangle(50, 150, 500, 200));
XSLFTextParagraph contentParagraph = content.addNewTextParagraph();
XSLFTextRun contentRun = contentParagraph.addNewTextRun();
contentRun.setText("슬라이드 내용입니다.");
// File 저장
FileOutputStream out = new FileOutputStream("sample.pptx");
ppt.write(out);
out.close();
ppt.close();
}
}
PowerPoint 읽기
XMLSlideShow로 기존 PowerPoint file의 slide와 text를 읽어옵니다.
public void readPresentation(String filePath) throws IOException {
FileInputStream fis = new FileInputStream(filePath);
XMLSlideShow ppt = new XMLSlideShow(fis);
XSLFSlide[] slides = ppt.getSlides().toArray(new XSLFSlide[0]);
for (int i = 0; i < slides.length; i++) {
System.out.println("슬라이드 " + (i + 1) + ":");
List<XSLFShape> shapes = slides[i].getShapes();
for (XSLFShape shape : shapes) {
if (shape instanceof XSLFTextShape) {
XSLFTextShape textShape = (XSLFTextShape) shape;
System.out.println(textShape.getText());
}
}
}
ppt.close();
fis.close();
}
Error 처리 및 Best Practice
- POI 사용 시 resource 관리와 exception 처리가 중요합니다.
Exception 처리 Pattern
- POI 사용 시 발생하는 주요 exception을 처리하고,
finallyblock에서 resource를 정리합니다.
public class SafePoiProcessor {
public void safeFileProcessing(String filePath) {
FileInputStream fis = null;
Workbook workbook = null;
try {
fis = new FileInputStream(filePath);
workbook = WorkbookFactory.create(fis);
// 파일 처리 로직
processWorkbook(workbook);
} catch (FileNotFoundException e) {
logger.error("파일을 찾을 수 없습니다: " + filePath, e);
} catch (IOException e) {
logger.error("파일 읽기/쓰기 오류", e);
} catch (InvalidFormatException e) {
logger.error("지원하지 않는 파일 형식", e);
} catch (Exception e) {
logger.error("예상치 못한 오류 발생", e);
} finally {
closeQuietly(workbook);
closeQuietly(fis);
}
}
private void closeQuietly(Closeable resource) {
if (resource != null) {
try {
resource.close();
} catch (IOException e) {
logger.warn("Resource 종료 중 오류", e);
}
}
}
}
Performance 최적화
CellStyle을 매번 생성하지 않고 cache하여 재사용하면 성능이 크게 향상됩니다.
public class PerformanceOptimizedProcessor {
// Cell style 재사용을 위한 cache
private final Map<String, CellStyle> styleCache = new HashMap<>();
public void optimizedExcelProcessing(Workbook workbook) {
Sheet sheet = workbook.createSheet("최적화된 시트");
// Style 재사용
CellStyle headerStyle = getCachedStyle(workbook, "header");
CellStyle dataStyle = getCachedStyle(workbook, "data");
// Bulk operation 사용
for (int i = 0; i < 10000; i++) {
Row row = sheet.createRow(i);
Cell cell = row.createCell(0);
cell.setCellValue("Data " + i);
cell.setCellStyle(i == 0 ? headerStyle : dataStyle);
}
// Formula evaluation 최적화
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateAll();
}
private CellStyle getCachedStyle(Workbook workbook, String styleType) {
return styleCache.computeIfAbsent(styleType,
type -> createStyle(workbook, type));
}
}