๊ธฐ์กด ๋ฐฉ์
๊ธฐ์กด์ ์์ ํ์ผ ๋ค์ด๋ก๋ ํ๋ ๋ฐฉ์์ list๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์์ modelMap ์ ๋ด์์ฃผ๊ณ return ํ ๋ new ModelAndView("fileroot/testExcelDownload", modelMap); ์ด๋ฐ์์ผ๋ก ์์ ํ์ผ์ ๋ง๋ค์ด์ฃผ๊ณ ์์๋ค.
modelMap.put("alist",alist);
๋ฌธ์ ๋ฐ์ ์์ธ
๊ทผ๋ฐ <c:forEach itmes ="${alist}" var = "row" varStatus = "i"> ์ด๋ ๊ฒ ์ฐ๊ณ ์์๋๋ฐ ๋ฐ์ดํฐ์ ์์ด N ๋ง๊ฑด ์ ๋๊ฐ ๋๋ค ๋ณด๋ ์ง๋์ง๋์น forEach ํ๊ทธ์ ๋ฐ๋ณต์ผ๋ก OOM ์ด ๋ฐ์ํ๋ค.
์ ๊ธ์์ ๋งํ๋๊ฒ ์ฒ๋ผ ์์ธํ ์ด์ ๋ ์๋์ ๊ฐ์๋ค.
Java WAS์ Heap ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ถ์กฑํด์ ธ Full GC๊ฐ ์ง์์ ์ผ๋ก ๋ฐ์ํ๊ณ , ๊ฒฐ๊ตญ Heap ๋ฉ๋ชจ๋ฆฌ2GB ์ค 1.6GB๋ฅผ ํน์ ํด๋์ค๊ฐ ์ ์ ํ๋ ๋ฌธ์ ์๋ค.
๋๋ต์ ์ธ ๊ณผ์ ์ ์ด๋ฌ๋ค.
- ์ธํ๋ผ ๋ฆฌ์์ค ํน์ด์ฌํญ์ 17์ 19๋ถ ๋ถํฐ cpu ์ฌ์ฉ๋ฅ ์ด ๊ธ์ฆํ๊ธฐ ์์ํ์ฌ was ํ๋ก์ธ์ค ์ฌ๊ธฐ๋ ์ ๊น์ง cpu ์ฌ์ฉ๋ฅ 90% ๊ณผ์ ์
- cpu ๊ณผ์ ์ ๋ jboss heap ๋ฉ๋ชจ๋ฆฌ์์ java.lang.outofMemoryError(OOM) ์ด ๋ฐ์ํ๋ฉฐ ๋ฉ๋ชจ๋ฆฌ ํ๋ณด๋ฅผ ์ํ full GC ์ํ ์ํฅ์ผ๋ก ํ์ธ
- Full GC ์ํ์ผ๋ก๋ java heap ๋ฉ๋ชจ๋ฆฌ๊ฐ ํ๋ณด๊ฐ ๋์ง ์์๊ณ 17:32 ์ Interneal Exception ์ด ๋ฐ์ํ๋ฉฐ java crash ๋ฐ์
(java crash log ์ดํด๋ณด๋ฉด heap ๋คํ ์์ฑ ์ค jdk ๋ด๋ถ jplisagent.c ์ฝ๋ ์ํ ์ ์๋ฌ ๋ฉ์์ง ์์ฑ ์คํจํ internal exception ๋ก ํน์ด์ฌํญ ์์)
- ๋ฐ๋ผ์ ์์ธ๋ถ์์ ์ํด heap ๋คํ ๋ถ์ํ ๊ฒฐ๊ณผ , ๋๋ฉ์ธ ํตํฉ ๊ด๋ฆฌ ์๋น์ค์ heap ๋ฉ๋ชจ๋ฆฌ๋ ํ์ค๊ฐ์ธ 2GB ๋ก ํ ๋น๋์ด ์์ผ๋ฉฐ ์ด์์งํ ๋น์ ๋ค๋์ org.apache.tagilbs.standard.tag.rt.core.ForEachTag ํด๋์ค์์ ๋ฉ๋ชจ๋ฆฌ 2GB ์ค 1.6GB ๊ณผ์ ์ ํ๋ฉฐ ๋ฐ์
๋ฌธ์ ํด๊ฒฐ ๋ฐฉ์
๊ทธ๋์ '์์ ํ์ผ ๋ค์ด๋ก๋ ๋์ฉ๋'์ผ๋ก ๊ฒ์ํด๋ณด๋ Java์์ Microsoft Office ๋ฌธ์๋ฅผ ์ฝ๊ณ , ์ฐ๊ณ , ๊ด๋ฆฌํ ์ ์๊ฒ ํด์ฃผ๋ ์คํ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ apache ์ POI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฐ๊ฒฌํ๋ค.
ํนํ **์์ (Excel)**์ ๋ค๋ฃจ๋ ์ฉ๋๋ก ๊ฐ์ฅ ๋ง์ด ํ์ฉ๋๋ค๊ณ ํจ.
1) ๊ธฐ์กด ๋ฐฉ์๊ณผ ์ฐจ์ด์
์๋๋ ๊ธฐ์กด์ ๋ฐฉ์๊ณผ ์ด๋ค ์ฐจ์ด๊ฐ ์๋์ง ์ฑ์งํผํฐํํ ๋ฌผ์ด๋ณธ ๊ฒฐ๊ณผ์ด๋ค.
apache POI ๊ธฐ๋ฐ ์ฒ๋ฆฌ ๋ฐฉ์์ JSP ์์ด ์ปจํธ๋กค๋ฌ๊ฐ ์ง์ ํ์ผ์ ์์ฑํ์ฌ ์ ๊ณตํ๋ค๊ณ ํ๋ค.
JSP View ์ฒ๋ฆฌ ๋ฐฉ์ | JSP๊ฐ View๋ก HTML์ ์์ฑํ์ฌ ์ฌ์ฉ์ ๋ธ๋ผ์ฐ์ ์ ๋ณด์ฌ์ค | HTML ํ์ด์ง ์กฐํ |
Apache POI ๊ธฐ๋ฐ ์ฒ๋ฆฌ ๋ฐฉ์ | JSP ์์ด ์ปจํธ๋กค๋ฌ๊ฐ ์ง์ ํ์ผ์ ์์ฑํ์ฌ ๋ค์ด๋ก๋ ์ ๊ณต | ์์ , PDF ๋ฑ ํ์ผ ๋ค์ด๋ก๋ |
๊ตฌ๋ถ | ModelAndView (JSP) | Apache POI (Excel) |
ํ๋ฉด ์ถ๋ ฅ | HTML๋ก ํ๋ฉด์ ์ง์ ์ถ๋ ฅ | ํ๋ฉด ์ถ๋ ฅ ์์ด ํ์ผ ๋ค์ด๋ก๋ |
๋ฐ์ดํฐ ํํ | HTML๋ก ๊ฐ๊ณต (View์์ ๊ฐ๊ณต์ฒ๋ฆฌ) | Excel๋ก ์ง์ ๊ฐ๊ณต (Java ์ฝ๋๋ก ๊ฐ๊ณต) |
ํ ํ๋ฆฟ ๋ฐฉ์ | JSP, Thymeleaf ๋ฑ์ ํ ํ๋ฆฟ ์ฌ์ฉ | ํ ํ๋ฆฟ ์์ด Java ์ฝ๋๋ง์ผ๋ก ์์ฑ |
โ๏ธ์์ ํ์ผ๋ค์ด๋ก๋ ํ๋ฆ
Client(๋ธ๋ผ์ฐ์ )
โ ์์ฒญ (GET /excel/download)
โผ
Controller โโโโโถ Service โโโโโถ DAO/Repository
โ (DB์์ ๋ฐ์ดํฐ ์กฐํ)
โผ ๋ฐ์ดํฐ๋ฅผ POI๋ก ์์
๋ก ๋ณํ
Excel ํ์ผ ์์ฑ (POI Workbook)
โ ํ์ผ์ Response OutputStream์ผ๋ก ์ ๋ฌ
โผ
Client(๋ธ๋ผ์ฐ์ )๋ก ๋ค์ด๋ก๋
+ ์ถ๊ฐ๋ก
๋์ฉ๋ ์์
ํ์ผ์ ๋ํด ์
๋ก๋ ํ๋ ๋ฐฉ์์ ๊ตฌํํ ๋, ๊ธฐ์กด์๋ DOM ๋ฐฉ์์ ๋ง์ด ์ฌ์ฉํ์ ๊ฒ์ด๋ค.
์ฌ๊ธฐ์ DOM๋ฐฉ์์? Document Object Model์ ์ฝ์์ด๋ฉฐ, ์๋์ ๊ฐ์ ํน์ง์ด ์๋ค.
- XML Parser๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๋ฉด์ Object๋ก ๊ตฌ์ฑํ์ฌ DOM Tree๋ฅผ ์์ฑ
- DOM ๊ตฌ์กฐ๋ ๋ชจ๋ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฌ
- ํฅํ ๋ฉ๋ชจ๋ฆฌ๋ด ๊ณ์ธต๊ฐ์ Random Access ๊ฐ๋ฅ
- ๋ฐ์ดํฐ ๋ชจ๋ธ์ด ์์ฑ ํ ์ฌ์ฉ์์ ์์ฒญ์ ๋ฐ๋ผ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๋ฐฉ์(Pull ๋ชจ๋ธ)
๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๊ฐ๊ฐ์ Cell, Row Type, Row, Sheet ๋ฑ ์์
์ ๋ชจ๋ ๋ถ๋ถ์ ๋ํด DOM ๊ตฌ์กฐ๋ก ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฌํ๊ณ ์์ผ๋...
์์ ํ์ผ์ Row์ Cell์ด ์ ๋ง์ ๋ง์ ๋ง ๋ง์์ง๊ฒ ๋๋ค๋ฉด...? (๊ทธ๋ฌ๋ฉด ์์ฒญ...๋๋ ค์ง๊ตฌ...๋ฌด๊ฑฐ์์ง๊ฒ ์ฃ ...?)
์ด๋ฌํ ๋ฌธ์ ๋ก ์ธํด DOM๋ฐฉ์์ด ์๋ Apache POI ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณตํด์ฃผ๋ SAX๋ฐฉ์์ด ๋์ค๊ฒ ๋ ๊ฒ์ด๋ค!!!
SAX๋ฐฉ์์ด๋? Simple API for XML์ ์ฝ์์ด๋ฉฐ, ์๋์ ๊ฐ์ ํน์ง์ ๊ฐ์ง๊ณ ์์ต๋๋ค.
- DOM ๊ตฌ์กฐ๋ฅผ ๋ง๋ค์ง ์์
- ์ค์บํ๋ ์์๋๋ก ์ด๋ฒคํธ๊ฐ ๋ฐ์๋๋ฉฐ, Random Access ๋ถ๊ฐ
- XML ๋ฌธ์๋ฅผ ์ค์บํ๋ฉด์ ์ถ์ถ๋ ๋ฐ์ดํฐ์ ๋ํด ์ง์์ ์ผ๋ก ์ด๋ฒคํธ๋ฅผ ๋ฐ์์์ผ, ์ฌ์ฉ์์ธก์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ํต๋ณดํ๋ ๋ฐฉ์(Push ๋ชจ๋ธ)
์์ ํ์์ ๋ณด๋ฉด ๊ฐ์ฅ ํฐ ๋ชฉ์ ์ ๊ฐ์ง๊ณ ์ฌ์ฉํ๊ฒ ๋ ์ด์ ๋ ์ฒ๋ฆฌ์๋์ ๋์๋ฌธ์ ๋๋ฌธ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
dom ๋ฐฉ์์ ๋นํด sax ๋ฐฉ์์ด ์ฒ๋ฆฌ์๋๊ฐ ๋น ๋ฅด๊ณ , ๋์ฉ๋ ๋ฌธ์์ ์ ํฉํ๊ธฐ ๋๋ฌธ์ sax ๋ฐฉ์์ผ๋ก ์ ์ฉํ๊ธฐ๋ก ํ๊ณ
apache POI ๊ฐ ์ง์ํ๋ ์ฃผ์ ๋ฌธ์ ํ์์ผ๋ก ์ฌ๋ฌ๊ฐ์ง๊ฐ ์๋๋ฐ ๊ทธ์ค SXSSF ์ ์ ํํ๋ค.
HSSF | Excel 97~2003 ํ์ (.xls) | HSSFWorkbook |
XSSF | Excel 2007 ์ดํ (.xlsx) | XSSFWorkbook |
SXSSF | ๋์ฉ๋ ์์ ๋ฌธ์(.xlsx) ์์ฑ (์คํธ๋ฆฌ๋ฐ ๋ฐฉ์) | SXSSFWorkbook |
HWPF | Word (.doc) | HWPFDocument |
XWPF | Word (.docx) | XWPFDocument |
HSLF | PowerPoint (.ppt) | HSLFSlideShow |
XSLF | PowerPoint (.pptx) | XMLSlideShow |
<-- poi ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉ์ ์ํ pom.xml ์ ์์กด์ฑ ์ฃผ์ -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.12</version>
<scope>system</scope>
<systemPath>{project.basedir}/lib/poi-3.12.jar</systemPath>
</dependency>
2) SXSSFWorkbook ์ฌ์ฉ
: SXSSFWorkbook์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ค์ฌ ๋์ฉ๋ ์์ ํ์ผ ์์ฑ์ ์ ํฉํ๋ค.
- ์๋ง~์์ญ๋ง ๊ฑด ์ด์์ ๋๋ ๋ฐ์ดํฐ๋ฅผ ์์ ๋ก ์์ฑํ ๋
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ์ ์ต์ํํ์ฌ OOM(OutOfMemory) ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ ๋
โ SXSSFWorkbook ์ฌ์ฉํ ์์ ํ์ผ ์์ฑ ์์
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class ExcelWriteSXSSFExample {
public static void main(String[] args) {
// SXSSFWorkbook ์ธ์คํด์ค ์์ฑ (flush ๋จ์ 100 row)
SXSSFWorkbook workbook = new SXSSFWorkbook(100);
Sheet sheet = workbook.createSheet("๋์ฉ๋ ์ํธ");
// ํ ์์ฑ
Row headerRow = sheet.createRow(0); // ํค๋ ํ ์์ฑ
headerRow.createCell(0).setCellValue("๋ฒํธ");
headerRow.createCell(1).setCellValue("์ด๋ฆ");
headerRow.createCell(2).setCellValue("์ ์");
// ๋์ฉ๋ ๋ฐ์ดํฐ ์์ (10๋ง๊ฑด) Cell ์์ฑ
for (int i = 1; i <= 100000; i++) {
Row row = sheet.createRow(i);
row.createCell(0).setCellValue(i);
row.createCell(1).setCellValue("์ด๋ฆ-" + i);
row.createCell(2).setCellValue(Math.random() * 100);
}
String filename = "test"+currentDate + ".xlsx";
Stirng encodedFilename = URLEncoder.encode(filename , "UTF-8").replaceAll("\\u002B","%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Dispostion", "attachment; filename*= UTF-8""+encoderFilename);
workbook.write(bufferedOutputStream);
bufferedOutputStream.flush();
}
}
์๋ ์ฝ๋๊ฐ ํ์ํ ์ด์
Stirng encodedFilename = URLEncoder.encode(filename , "UTF-8").replaceAll("\\u002B","%20");
ํ์ผ๋ช ์ธ์ฝ๋ฉ ๋ถ๋ถ
- ๋ธ๋ผ์ฐ์ ๋ HTTP ์๋ต ํค๋์ ํ์ผ๋ช ์ ๋ฐ์ ๋ ํน์ ๋ฌธ์๋ ํ๊ธ ๋ฑ์ ์ ๋๋ก ์ฒ๋ฆฌํ์ง ๋ชปํ ์ ์๋ค.
- ํ์ผ๋ช ์ ํ๊ธ์ด๋ ๊ณต๋ฐฑ์ด ์์ผ๋ฉด ๊นจ์ง ์ ์์ด์ ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ํ์ผ๋ช ์ URL ์ธ์ฝ๋ฉํ์ฌ ์ฒ๋ฆฌํ๋ ๊ฒ
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
์ ์ฝ๋์ ๊ฒฝ์ฐ HTTP ์๋ต์ ๋ฐ์ ๋ธ๋ผ์ฐ์ ์๊ฒ ์ ๋ฌ๋๋ ๋ฐ์ดํฐ๊ฐ ์์ ํ์ผ์์ ๋ช ์์ ์ผ๋ก ์๋ ค์ฃผ๋ ์ญํ ์ ํจ
response.setHeader("Content-Disposition", "attachment; filename*= UTF-8""+encoderFilename);
- ๋ธ๋ผ์ฐ์ ์๊ฒ ๋ค์ด๋ก๋ํ ํ์ผ์ ์ด๋ฆ๊ณผ ์ธ์ฝ๋ฉ ๋ฐฉ์์ ์ง์ ํด์ค๋๋ค.
- ํนํ ๊ตญ์ ํ์ค(RFC 5987)์ ๋ฐ๋ฅธ filename* ๋ฐฉ์์ ๋ค์ํ ๋ธ๋ผ์ฐ์ ์์ UTF-8 ์ธ์ฝ๋ฉ๋ ํ์ผ๋ช ์ ์ง์
- attachment: ๋ธ๋ผ์ฐ์ ์์ ์ด ํ์ผ์ ์ฒจ๋ถํ์ผ(๋ค์ด๋ก๋)๋ก ์ฒ๋ฆฌํ๋๋ก ์ค์ .
- filename*: ์ธ์ฝ๋ฉ ๋ฐฉ์์ UTF-8๋ก ๋ช ์ํ๋ฉฐ ์ธ์ฝ๋ฉ๋ ํ์ผ๋ช ์ ์ง์ .
workbook.write(bufferedOutputStream);
Workbook ๊ฐ์ฒด๋ ๋ฉ๋ชจ๋ฆฌ์๋ง ์กด์ฌํ๋ ์ํ์ด๋ฉฐ,
์ด๋ฅผ ์ค์ ๋ธ๋ผ์ฐ์ ๋ ํ์ผ๋ก ๋ด๋ ค์ฃผ๊ธฐ ์ํด ์ถ๋ ฅ ์คํธ๋ฆผ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋กํ๋ ๊ณผ์ ์
๋๋ค.
๋์ ๊ณผ์
- Apache POI๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ๋ด์๋ Workbook ๋ฐ์ดํฐ๋ฅผ OutputStream์ผ๋ก ์ ๋ฌํฉ๋๋ค.
- ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ฉด ๋ธ๋ผ์ฐ์ ๋ ์ด ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์์ ํ์ผ๋ก ๋ค์ด๋ก๋ํฉ๋๋ค.
bufferedOutputStream.flush();
- Java์ BufferedOutputStream์ ๋ฐ์ดํฐ๋ฅผ ์ฑ๋ฅ ํฅ์์ ์ํด ๋ฒํผ์ ์์๋ก ์ ์ฅํฉ๋๋ค.
- ๋ฒํผ์ ๋ด๊ธด ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ํด๋ผ์ด์ธํธ(๋ธ๋ผ์ฐ์ )๋ก ๋ด๋ณด๋ด๊ธฐ ์ํด ๋ช ์์ ์ผ๋ก ํธ์ถํฉ๋๋ค.
์ด ์ฝ๋๋ฅผ ํธ์ถํ์ง ์์ผ๋ฉด?
- ๋ฐ์ดํฐ๋ฅผ ์ถ๋ ฅ ์คํธ๋ฆผ์ผ๋ก ์ ๋ฌํด๋, ๋ฒํผ๊ฐ ๊ฐ๋ ์ฐจ๊ธฐ ์ ๊น์ง ๋ฐ์ดํฐ๊ฐ ์ฆ์ ์ ๋ฌ๋์ง ์์ ์ ์์
- ํ์ผ์ ๋๋ถ๋ถ์ด ์ ์ก๋์ง ์์ ์์ ํ์ผ์ด ๊นจ์ง๊ฑฐ๋ ์์๋ ์ ์์
Apache POI™ - the Java API for Microsoft Documents
<!--+ |breadtrail +--> <!--+ |start Menu, mainarea +--> <!--+ |start Menu +--> <!--+ |end Menu +--> <!--+ |start content +--> Apache POI™ - the Java API for Microsoft Documents Project News 8 January 2025 - POI 5.4.0 available The Apache POI team is plea
poi.apache.org
๋์ฉ๋ ์์ ๋ค์ด๋ก๋ OOM(Out Of Memory) ํด๊ฒฐ ๊ณผ์
์๋ฒ๊ฐ ๊ฐ์๊ธฐ ๋จนํต์ด ๋ ๊ฒฝํ์ ํ๋ค.์ด์ ์ค์ธ ์๋ฒ์ CPU ์ฌ์ฉ๋ฅ ์ด ๊ธ๊ฒฉํ ์์นํ๋ฉฐ ์๋ฒ๊ฐ ์ฃฝ์ด๋ฒ๋ ธ๋ค.์๋ฒ๋ฅผ ์ฆ์ ์ฌ๊ฐ๋ํ๊ณ ์์ธ์ ํ์ธํ ๊ฒฐ๊ณผ, OOM(Out Of Memory)์ผ๋ก ์ธํด ์๋ฒ๊ฐ ์ค๋จ๋
dev-coco.tistory.com
https://devinn.dev/blog/detail/237
Blog | Dev.Inn | Apache POI ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ํด ํํด์ณ๋ณด์!!!
Apache POI๋ ์ํ์น ์ํํธ์จ์ด ์ฌ๋จ์์ ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก์ ๋ง์ดํฌ๋ก์ํํธ ์คํผ์ค ํ์ผ ํฌ๋งท์ ์์ ์๋ฐ ์ธ์ด๋ก์ ์ฝ๊ณ ์ฐ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. ์ฌ๊ธฐ์๋ ์์ ๋ค์ด๋ก๋ ๋ฐ ์์ ํ์ผ์ ์ฝ์
devinn.dev