Apache POI를 사용해 Java에서 Excel 작업할 때 핵심 요점들을 간단히 정리해 볼려고 합니다.
excel Apache POI jar 파일들은 다음과 같아야 한다.
이와 같은 버전이 아니면 오류가 발생할 수 있으니,
다음 사진과 같이 버전별로 맞출 필요가 있다.
하나하나 maven에서 다운로드하는 것이 귀찮기 때문에 알집으로 google 드라이브 링크를 공유해드리겠다. 귀찮은 분들은 다운로드 gogo!
springboot 프레임워크 gradle 빌드를 쓴다면 다음 라이브러리를 사용하면 됩니다!
build.gradle
// excel Apache POI
implementation 'org.apache.poi:poi:4.1.2'
implementation 'org.apache.poi:poi-ooxml:4.1.2'
XSSFWorkbook
(xlsx) 또는 HSSFWorkbook
(xls) 사용createSheet("Sheet1")
같은 방식createRow(int)
, createCell(int)
등으로 셀에 접근setCellValue(String)
등으로 값 설정FileOutputStream
통해 .xlsx
또는 .xls
저장java main문에서 example.xlsx 파일을 읽어와서 콘솔에 보여주는 예시입니다.
public static void main(String[] args) {
try {
FileInputStream file = new FileInputStream(new File("example.xlsx"));
Workbook workbook = WorkbookFactory.create(file);
Sheet sheet = workbook.getSheetAt(0);
DataFormatter formatter = new DataFormatter();
// sheet별 row들을 가져와서 dataFormatter로 cell 내용을 가져오기
for (Row row : sheet) {
for (Cell cell : row) {
System.out.print(formatter.formatCellValue(cell) + "\t"); // 탭으로 구분
}
System.out.println(); // 줄바꿈
}
file.close();
System.out.println("엑셀에서 데이터 읽어오기 끝");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
FileInputStream file = new FileInputStream(new File("example.xlsx"));
Workbook workbook = WorkbookFactory.create(file);
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for (Cell cell : row) {
switch (cell.getCellType()) {
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
Date dateValue = cell.getDateCellValue();
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String formattedDate = dateFormat.format(dateValue);
System.out.print(formattedDate);
} else {
double numericValue = cell.getNumericCellValue();
// 정수인지 실수인지 구분
// 정수
if (numericValue == Math.floor(numericValue)) {
int intValue = (int) numericValue;
System.out.print(intValue);
// 실수
} else {
System.out.print(numericValue + "\t");
}
}
break;
case STRING:
DataFormatter formatter = new DataFormatter();
String stringValue = formatter.formatCellValue(cell);
System.out.print(stringValue + "\t");
break;
case BOOLEAN:
boolean booleanValue = cell.getBooleanCellValue();
System.out.print(booleanValue + "\t");
break;
case FORMULA:
String formulaValue = cell.getCellFormula();
System.out.print(formulaValue + "\t");
break;
case BLANK:
System.out.print("\t");
break;
default:
System.out.print("\t");
break;
}
}
System.out.println(); // 줄바꿈
}
file.close();
System.out.println("엑셀에서 데이터 읽어오기 끝");
} catch (IOException e) {
e.printStackTrace();
}
}
public class ExcelWriter {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
List<MemberVO> members = new ArrayList<>();
while (true) {
System.out.print("이름을 입력하세요:");
String name = scanner.nextLine();
if (name.equals("quit")) {
//System.exit(-1);
break;
}
System.out.print("나이를 입력하세요:");
int age = scanner.nextInt();
scanner.nextLine(); // 개행문자 제거
System.out.print("생년월일을 입력하세요:");
String birthdate = scanner.nextLine();
System.out.print("전화번호를 입력하세요:");
String phone = scanner.nextLine();
System.out.print("주소를 입력하세요:");
String address = scanner.nextLine();
System.out.print("결혼여부를 입력하세요 (true/false):");
boolean isMarried = scanner.nextBoolean();
scanner.nextLine(); // 개행문자 제거
MemberVO member = new MemberVO(name, age, birthdate, phone, address, isMarried);
members.add(member);
}
scanner.close();
try {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("회원 정보");
// 헤더 생성
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("이름");
headerRow.createCell(1).setCellValue("나이");
headerRow.createCell(2).setCellValue("생년월일");
headerRow.createCell(3).setCellValue("전화번호");
headerRow.createCell(4).setCellValue("주소");
headerRow.createCell(5).setCellValue("결혼여부");
// 헤더 색깔 변경
CellStyle headerStyle = workbook.createCellStyle();
headerStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 헤더 가운데 정렬
headerStyle.setAlignment(HorizontalAlignment.CENTER);
// 글자 굵기 진하게 변경
Font font = workbook.createFont();
font.setBold(true);
headerStyle.setFont(font);
// 테두리 전체 적용
headerStyle.setBorderTop(BorderStyle.THIN);
headerStyle.setBorderBottom(BorderStyle.THIN);
headerStyle.setBorderLeft(BorderStyle.THIN);
headerStyle.setBorderRight(BorderStyle.THIN);
for (Cell cell : headerRow) {
cell.setCellStyle(headerStyle);
}
// 데이터 생성
for (int i = 0; i < members.size(); i++) {
MemberVO member = members.get(i);
Row row = sheet.createRow(i + 1);
row.createCell(0).setCellValue(member.getName());
row.createCell(1).setCellValue(member.getAge());
row.createCell(2).setCellValue(member.getBirthdate());
row.createCell(3).setCellValue(member.getPhone());
row.createCell(4).setCellValue(member.getAddress());
Cell marriedCell = row.createCell(5);
marriedCell.setCellValue(member.isMarried());
}
// 엑셀 파일 저장
String filename = "members.xlsx";
FileOutputStream outputStream = new FileOutputStream(new File(filename));
workbook.write(outputStream);
workbook.close();
System.out.println("엑셀 파일이 저장되었습니다: " + filename);
} catch (IOException e) {
System.out.println("엑셀 파일 저장 중 오류가 발생했습니다.");
e.printStackTrace();
}
}
}
...
try {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("도서 목록");
// 헤더 스타일 설정
CellStyle headerStyle = workbook.createCellStyle();
// 굵은 bold 처리
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
headerStyle.setFillForegroundColor(IndexedColors.AQUA.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Row headerRow = sheet.createRow(0);
List<String> headerList = List.of("ISBN", "카테고리", "제목", "저자", "출판사", "부연설명", "대여여부");
for (int i = 0; i < headerList.size(); i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headerList.get(i));
cell.setCellStyle(headerStyle);
}
for (int i = 0; i < bookList.size(); i++) {
Row row = sheet.createRow(i + 1);
row.createCell(0).setCellValue(bookList.get(i).getId());
row.createCell(1).setCellValue(bookList.get(i).getCategory());
row.createCell(2).setCellValue(bookList.get(i).getTitle());
row.createCell(3).setCellValue(bookList.get(i).getWriter());
row.createCell(4).setCellValue(bookList.get(i).getPublisher());
row.createCell(5).setCellValue(bookList.get(i).getDescription());
row.createCell(6).setCellValue(bookList.get(i).isRented() == true ? "대여중" : "대여가능");
}
...
XSSFWorkbook은 Apache POI 라이브러리에서 Excel 2007 이상(.xlsx) 파일을 다룰 때 사용하는 클래스입니다. 사용 목적에 따라 달라지는 excel workbook이 있습니다. 아래에 XSSFWorkbook과 다른 대안들을 비교해볼게요.
종류 | 지원 형식 | 특징 | 장점 | 단점 |
---|---|---|---|---|
HSSFWorkbook | .xls (Excel 97~2003) | 오래된 Excel 형식 지원 | 가볍고 빠름 | 65,536행 제한, 최신 기능 미지원 |
XSSFWorkbook | .xlsx (Excel 2007+) | 최신 Excel 형식 지원, XML 기반 | 다양한 기능 지원, 포맷 호환성 좋음 | 메모리 사용량 많음, 대용량 처리에 부담 |
SXSSFWorkbook | .xlsx (Streaming) | 대용량 Excel 파일 처리에 최적화 (POI 3.8+부터 지원) | 스트리밍 방식으로 메모리 절약, 수십만 행 처리 가능 | 일부 기능 제한 (예: 셀 복사, 수식 평가) |
출처: POI 라이브러리 비교 설명
반면, 수십만 행 이상의 대용량 데이터를 처리하거나 메모리 사용을 줄이고 싶을 때는 SXSSFWorkbook이 더 적합합니다.
리소스 관리 철저히
Workbook
, InputStream
, OutputStream
등은 close()
또는 try-with-resources
로 확실하게 종료
버전 호환 유의
.xls
(HSSF)는 2003 이하, .xlsx
(XSSF)는 2007 이상. 새로운 기능은 XSSF 기반에서만 가능
많은 데이터 처리 시 SXSSF 고려
대용량 엑셀 생성 시 SXSSFWorkbook
사용 → 메모리 효율 개선
셀 스타일 객체 재사용
CellStyle
은 많이 생성하면 성능 저하 가능 → 스타일 객체는 최대한 공유해서 사용
날짜·숫자 포맷 주의
단순 setCellValue()
로는 날짜 형식 적용 안 됨 → DataFormat
을 설정해줘야 함
행/셀 인덱스는 0부터 시작
실수로 인덱스를 잘못 쓰기 쉬움! 항상 0부터 시작임을 기억하기
Formula 적용 가능
setCellFormula("SUM(A1:A5)")
처럼 수식도 삽입 가능
자동 크기 조절
autoSizeColumn()
사용 시 성능 이슈 있을 수 있음. 꼭 필요한 열에만 사용