OOM과 타임아웃을 극복하고 대용량 엑셀 다운로드 기능 구현하기
Apache-POI를 활용한 대용량 엑셀 다운로드 구현
OOM과 타임아웃을 극복하고 대용량 엑셀 다운로드 기능 구현하기
Apache-POI를 활용한 대용량 엑셀 다운로드 구현

대용량 데이터를 다룰 때는 Out Of Memory(OOM)와 타임아웃이 언제나 말썽입니다.
신입 시절 사내 정산팀 업무를 지원하면서 Apache POI를 활용해 대용량 엑셀 다운로드 기능을 구현했습니다. 당시 OOM과 타임아웃으로 골치아팠던 기억이 있는데요. 이를 해결한 방법을 공유해보려고 합니다.
왜 대용량 엑셀 다운로드 기능이 필요했을까?
사내 정산 팀에서 포인트 정산 내역 대용량 엑셀 다운로드 기능을 추가해달라고 요청했습니다.
기존에는 사내 관리자 시스템에서 적은 양의 데이터를 다운로드하는 것만 가능했습니다. 분기 단위의 전체 정산 내역 같은 것은 파일로 받을 수 없었습니다. 정산팀은 대용량 데이터 가공이 필요할 때마다 개발팀에 엑셀 파일을 요청해야 했습니다.
이러한 번거로운 과정을 없애고 원하는 때마다 실시간으로 데이터를 활용할 수 있도록 엑셀 다운로드 기능을 추가해달라는 요청이었습니다.
해당 프로젝트에는 엑셀 다운로드를 위한 공통 함수가 이미 존재했습니다. 하지만 대용량 데이터를 처리하는 데에는 적합하지 않았습니다. OOM과 타임아웃의 벽에 파일 쓰기가 번번히 좌절됐습니다.
기존의 형식과 유사한 새로운 함수를 개발해야 했습니다. 1. 유지보수의 편리함과 2. 기능 구현. 두마리 토끼를 잡기 위해서는 기존 코드와의 일관성을 유지하면서, 최소한의 수정으로 개발하는 것이 목표였습니다.
기존 XSSF 기반의 엑셀 다운로드 방식의 문제점
기존 엑셀 다운로드 기능은 Apache POI의 XSSF를 활용했습니다. 하지만 XSSF는 대용량 데이터를 처리하기에는 여러 한계가 있었습니다.
XSSF의 단점
- 템플릿을 읽는 데 시간이 오래 걸림
- 힙 메모리 사용량이 많아 OOM 발생 가능
- 메모리를 지속적으로 점유하여 성능 저하 초래
라이브러리 외에도 문제가 있었습니다.
엑셀 파일에 써야할 데이터를 가져오는 쿼리의 속도가 느렸습니다. 그런데 이 데이터를 분리된 서버에서 가져오고 있어서 타임아웃으로 연결이 끊어지는 문제까지 발생했습니다. 메모리 문제와 네트워크 응답 속도 문제까지 겹쳐서 다운로드 기능이 정상 동작하기 어려운 상황이었습니다.
해결 방법 1: SXSSF로 전환하여 메모리 최적화
기존의 XSSF 대신 SXSSF(Streaming Usermodel API for Excel)를 활용하여 OOM 문제를 해결했습니다.
SXSSF란?
SXSSF는 XSSF와 다르게 일정한 양의 데이터만 메모리에 유지하고, 나머지는 디스크에 저장하는 방식으로 메모리 사용량이 상대적으로 적다.
SXSSF 적용 방식
- 엑셀 데이터를 일정한 단위(예: 100개씩)로 나눠 저장
- 100줄 단위로 데이터를 쓰고 난 후 메모리에서 해제
한번씩 메모리를 날려주기 때문에 XSSF보다 적은 메모리로 대용량 데이터를 처리할 수 있습니다.
단, SXSSF는 read 기능을 지원하지 않아 템플릿 적용이 어렵다는 단점도 있습니다. 그렇지만 정산 팀의 요구사항을 고려했을 때, 템플릿대로 예쁘게 엑셀 파일이 만들어지는 것보다 데이터 정확성이 중요하다고 판단했습니다. 특히 돈과 관련된 건 예민한 문제기 때문에 템플릿을 포기하고 SXSSF를 활용하는 방향으로 결정했습니다.
해결 방법 2: 데이터 조회 방식 최적화
엑셀 다운로드 속도를 개선하려면 데이터를 불러오는 방식도 최적화해야 했습니다.
기존 방식: OFFSET 기반 페이징
PostgreSQL의 OFFSET을 활용해서 데이터를 페이징하고 있었습니다.
SELECT * FROM orders ORDER BY id LIMIT 100 OFFSET 500;
이 방식은 단순하고 구현이 쉽지만 데이터가 많아질수록 성능이 급격히 저하되는 단점이 있습니다. DB가 불필요한 row까지 스캔하고 버리는 비용이 증가하기 때문입니다.

초반에 쿼리 조회에 걸리는 시간

페이지가 커질 수록 급격하게 늘어나는 조회 시간
개선된 방식: PK 기반 페이징
OFFSET 대신 PK(Primary Key)를 기준으로 데이터를 조회하는 방식으로 변경했습니다.
SELECT * FROM orders WHERE id > last_id ORDER BY id LIMIT 100;
PK를 기준으로 페이징 처리할 때의 장점은 인덱스를 활용해 빠른 조회 가능하다는 점입니다. OFFSET을 사용할 때보다 조회 성능도 일정하게 유지됩니다. 많은 양의 데이터를 반복적으로 가져오는 대량 데이터 처리 시 성능 개선 효과가 컸습니다.

일정하게 유지되는 조회 속도
대용량 엑셀 다운로드 속도 개선
SXSSF 적용과 쿼리 최적화 이후, 기존 대비 OOM 발생 없이 안정적으로 다운로드 기능이 동작했습니다.
- 다운로드 성공률 100%
- OOM 발생 없음
- 다운로드 속도 향상(총 15분 소요 → 3.4분 소요로 개선)
대용량 데이터 처리 시 고려할 점
대용량 데이터를 처리할 때는 늘 OOM과 시간초과를 유의해야 합니다.
개발 시에는 이전의 코드와 어우러지게 하기 위해서 기존 방식의 한계를 먼저 분석하는 것이 중요하다고 느꼈습니다. 거창하게 수정하면서 기존 코드와 지나치게 괴리가 생긴다면, 그만큼 유지보수에 비용이 들 수 있기 때문입니다.
그 결과 아래와 같은 방식으로 기능을 개선했습니다.
- 메모리 최적화: SXSSF 활용하여 XSSF의 메모리 사용 문제 해결
- 쿼리 최적화: OFFSET 대신 PK 기반 페이징으로 성능 개선
- 주요 요구사항 우선 반영: 템플릿 적용보다 정확한 데이터 전달이 중요
이처럼 기존 코드의 장점을 유지하면서도 필요한 부분을 개선하는 방향으로 접근해서 실제 서비스에서 안정적이고 효율적인 기능을 구현할 수 있었습니다.
메타데이터
- post_id
- 8b83a9db1dc4
- slug
- oom과-타임아웃을-극복하고-대용량-엑셀-다운로드-기능-구현하기-8b83a9db1dc4
- url
- https://medium.com/@sh940311/oom%EA%B3%BC-%ED%83%80%EC%9E%84%EC%95%84%EC%9B%83%EC%9D%84-%EA%B7%B9%EB%B3%B5%ED%95%98%EA%B3%A0-%EB%8C%80%EC%9A%A9%EB%9F%89-%EC%97%91%EC%85%80-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-8b83a9db1dc4
- canonical_url
- https://medium.com/@sh940311/oom%EA%B3%BC-%ED%83%80%EC%9E%84%EC%95%84%EC%9B%83%EC%9D%84-%EA%B7%B9%EB%B3%B5%ED%95%98%EA%B3%A0-%EB%8C%80%EC%9A%A9%EB%9F%89-%EC%97%91%EC%85%80-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-8b83a9db1dc4
- author_url
- https://medium.com/@sh940311
- status
- ok
- fetched_at
- 2026-07-20 15:47:05