일전에 영양사님의 요청으로 식단 관련 엑셀 기능을 개발했었다
만든지 6개월밖에 안된게 좀 놀랍긴하다
얼마전 다시보니 응답시간이 636ms나 걸린다는 점이 거슬렸고 이에 개선을 진행하게 되었다
우선 시작에 앞서 하드웨어 스펙이다
칩 : Apple M2
OS : MacOS
메모리 : 16gb
CPU 코어 : 8
위의 환경에서 진행했다
기존 로직은 apache poi 의 XSSF 라이브러리를 사용해서 구현을 했다.
XSSF 라이브러리에 대해서 조금 알아보고 가자
- 파일의 모든 데이터를 메모리에 업로드
→ 데이터에 빠르게 접근 가능 → 파일 크기가 클 경우 메모리 사용량 급격히 증가
→ 내가 구현한 최대 1년치의 식단을 엑셀로 만드는 작업에는 적합하지 않음
위에 쓰여 있는 것처럼 모든 데이터를 메모리에 한번에 올려두고 작업을 진행하다보니 문제가 생길 수 있다
하루에 식단이 아침, 점심, 저녁, 능수관, 2캠퍼스까지 하면 평균적으로 5~6개가 나온다고 가정해도 5 X 365 = 1825 건의 식단 데이터가 있는데 이 모든 식단 정보들(가격, 코너, 시간, 메뉴 등등) 을 메모리에 올리게 되면 문제가 메모리에 문제가 생길 것이다
그래서 기존에 사용하던 XSSF 라이브러리는 대규모 엑셀 처리에는 적합하지 않다
이 라이브러리로 인해 635ms 라는 수치가 나오지 않았을까 싶다. 그리고 예전 QA에서 발생했던 OOM 도...
너무 까기만 한것 같아서 특징들을 보자
특징을 보니 더더욱 사용할 이유가 없는 것 같다
그냥 메모리에 모든 데이터를 올린다는 이유에서 나온 특징들인듯 하다
XSSF 라이브러리가 엑셀을 만드는 순서까지 보고 가자
데이터를 DB에서 가져오거나 하는 과정들은 생략
XSSFWorkbook 객체 생성 sheet 생성, 옵션 설정CellStyle, Font, Data format 등 캐시 등록Cell 생성OutputStream 으로 workbook에 writeclose() 후 종료이런식으로 흘러간다
이제 다른 라이브러리들을 살펴보자
라이브러리의 문제가 가장 크다고 생각하여 다른 라이브러리를 좀 찾아봤다
그렇게 찾은 라이브러리가 SXSSF 와 알리바바의 EasyExcel 이라는 라이브러리들이다
하나씩 살펴보자
지정한 row 만큼 메모리에 올려놓고 기록 후 메모리 비우는 작업 반복 : 스트리밍 방식
→ 슬라이딩 윈도우 기법 → 메모리 사용량 감소
이름만 봐도 XSSF 와 관련이 있어보인다
앞에 붙은 S 는 아마 Streaming 에서 따오지 않았을까 싶다 → 찾아보니 맞음
특징부터 보자
-XSSF 의 확장
SXSSF 의 특징들도 스트리밍 방식으로 생긴 특징인것 같다
XSSF 와의 가장 큰 차이는 데이터들을 메모리에 전부 올리지 않고 스트리밍 방식으로 끊어서 처리한다는 점이다
SXSSF 의 순서도 한번 보자
SXSSFWorkbook 객체 생성sheet 생성windowSize 행만 메모리에 저장OutputStream 으로 workbook에 writeclose() 후 종료데이터를 스트리밍 방식으로 가져오면서 끊어서 처리를 하는것을 볼 수 있다
중간중간 임시 파일에 저장해주는것까지
마지막으로 알리바바에서 만든 엑셀 라이브러리다
특징으로는
이정도가 있다
SXSSF와 EasyExcel 둘 다 스트리밍 방식을 사용하는데 이 방식에 차이가 있다
그 차이는 아래에서 보자
sheet 지정SAX 기반 스트리밍 방식으로 가져오기doWrite() 로 엑셀 그리기close() 후 종료 7번에 있는 SAX 기반 스트리밍이 궁금해서 찾아봤다
Simple API for XML : XML 읽는 방식 중 하나
읽는 방식 중 하나라고 한다
이 방식은 한줄씩 읽으면서 이벤트를 발생시킨다
각 셀 1행이 나올때마다 Read Listener invoke 를 호출 → 처리 후 메모리에서 버림 → OOM X → 윈도우 크기 대신 한줄씩 처리
SXSSF 의 스트리밍 방식과 다르게 한줄씩 처리를 하는 방식이다
스트리밍인건 같지만 범위의 차이가 있다
또한 한줄씩 처리하기 때문에 위에서 말한것처럼 객체 생성이 거의 없다는 것이 특징이다
위에서 말한 3개의 라이브러리에 대한 성능 비교를 해봤다
하드웨어 스펙은 위에 나와있고, 1달과 1년 범위의 식단을 생성하는 시간을 측정하고 비교했다
각 라이브러리별로 첫번째 실행(콜드스타트) 는 건너뛰고 그 이후 4번의 시간을 측정하여 평균을 구한 결과다
코드는 생략
1달을 측정할때는 스크린샷을 못찍어놧다
XSSF : 평균 55ms → 기존 방식SXSSF : 평균 48msEasyExcel : 평균 34ms| 1달 | XSSF | SXSSF | EasyExcel |
|---|---|---|---|
| 시간 | 55ms | 48ms | 34ms |
| 평균 CPU | 42% | 38% | 31% |
| 엑셀 쓰는 시간 | 44ms | 30ms | 17ms |
| 이유 | 오버헤드로 인해 느림 | 임시 기록 → flush로 인해 빠르지만 약간 느림 | 이벤트기반 (객체 최소화) 으로 가장 짧음 |
XSSF : 평균 429ms → 기존 방식

SXSSF : 평균 149ms

EasyExcel : 평균 192ms

| 1년 | XSSF | SXSSF | EasyExcel |
|---|---|---|---|
| 시간 | 429ms | 149ms | 192ms |
| 평균 CPU | 68% | 52% | 59% |
| 엑셀 쓰는 시간 | 334ms | 54ms | 100ms |
| 이유 | 대량시 객체/GC 오버헤드로 느림 | 임시파일로 지속 flush → 힙 압박 적음 | 셀 핸들러, 스타일, 머지 비용 누적으로 느려짐 |
결과는 위와 같다
큰 차이가 나는 기존 방식인 XSSF 라이브러리는 제외한다
EasyExcel 우세 SXSSF 우세작은 기간의 엑셀 생성시의 차이(14ms) 보다 최대 기간(1년)의 차이가 더 중요하다고 생각함
→SXSSF적용으로 큰 기간에서의 차이 최소화

순서도로 마무리!