4. 모니터링

[K6] 대용량 엑셀 다운로드 API 부하 테스트 - XSSF vs SXSSF 성능 비교 - 2편 (feat. AWS 배포 환경 재검증)

lhk9311 2026. 5. 7. 19:05

[K6] 대용량 엑셀 다운로드 API 부하 테스트 - XSSF vs SXSSF 성능 비교 - 2편 (feat. AWS 배포 환경 재검증)

목차

1. 테스트 배경
2. 테스트 환경
3. 테스트 시나리오
4. 결과(로컬XSSF, 로컬 SXSSF, AWS XSSF, AWS SXSSF)
5. Grafana 시각화(API 부하테스트)
6. JVM 힙 제한 후 재테스트(AWS XSSF, AWS SXSSF)

1. 테스트 배경

이전 포스팅에서 로컬 환경 기준으로 XSSF vs SXSSF 부하 테스트를 진행했으나, 로컬 메모리(16GB+)가 충분하여 XSSF OOM을 재현하지 못했음. 또한 예상과 달리 SXSSF가 더 느린 결과가 나왔음.

이번 포스팅에서는 실제 운영 환경에 가까운 AWS EC2(t2.small, 2GB RAM)에 배포하여 동일 시나리오로 재검증함. 추가로 K6 결과를 InfluxDB에 저장하고 Grafana로 시각화하는 모니터링 환경도 구축함.

 

참고:

https://lhk9311.tistory.com/8

 

[K6] 대용량 엑셀 다운로드 API 부하 테스트 - XSSF vs SXSSF 성능 비교 - 1편

[K6] 대용량 엑셀 다운로드 API 부하 테스트 - XSSF vs SXSSF 성능 비교 - 1편목차1. 테스트 배경2. 테스트 환경3. 테스트 시나리오4. 결과 — XSSF (레거시)5. 결과 — SXSSF (개선)6. 비교 분석7. 한계 및 다음

lhk9311.tistory.com


2. 테스트 환경

항목 로컬 환경 AWS 환경
부하 도구 K6 v1.7.1 K6 v1.7.1 (로컬에서 EC2로 쏨)
서버 Spring Boot, 로컬 (port 9081) AWS EC2 t2.small (2GB RAM)
DB Oracle XE, 예약 데이터 10,112건 Oracle XE, 예약 데이터 10,112건
모니터링 없음 InfluxDB 1.8 + Grafana (Docker)
테스트 대상 API GET /admin/reservation/excel GET /final_hotel/admin/reservation/excel

 

테스트 구성도


3. 테스트 시나리오

export const options = {
  stages: [
    { duration: '1m', target: 3 },   // 3명까지 ramp-up
    { duration: '3m', target: 3 },   // 3명 유지
    { duration: '1m', target: 10 },  // 10명까지 올리기
    { duration: '2m', target: 10 },  // 10명 유지
    { duration: '1m', target: 0 },   // 종료
  ],
  thresholds: {
    http_req_duration: ['p(95)<10000'],  // p95 10초 이내
    http_req_failed: ['rate<0.05'],      // 실패율 5% 미만
  },
};

 

VU(가상 사용자)를 3명 → 10명으로 단계적으로 올려서 동시 접속 상황을 재현함.

※ VU(Virtual User) = 동시에 요청을 날리는 가상 사용자 수  -------> 동일한 상황 가정


4. 결과(로컬 XSSF, 로컬 SXSSF, AWS XSSF, AWS SXSSF)

■ AWS XSSF (응답시간 단축됨)

로컬 -> aws 236ms에서 89ms로 단축

 

※ 로컬(236ms)보다 AWS(89ms)가 더 빠른 이유 — 로컬은 IDE, Docker 등 다른 프로세스가 함께 돌아가서 리소스를 나눠 쓰기 때문으로 추정됨. AWS는 Spring Boot만 전담으로 실행 중. (정확한 원인은  cpu 사용량 측정 등 추가 모니터링이 필요함.)

 

■ AWS SXSSF(응답시간 마찬가지로 단축됨)

 

■ 비교

항목 로컬 XSSF 로컬 SXSSF AWS XSSF AWS SXSSF
평균 응답시간 236ms ✅ 38.79s ❌ 89ms ✅ 16.63s ❌
p95 응답시간 501ms ✅ 46.29s ❌ 131ms ✅ 33.31s ❌
에러율 0% ✅ 0% ✅ 0% ✅ 0% ✅
총 요청 처리수 10,615건 68건 2,317건 149건
메모리 안정성 oom 위험 ( ⚠️ ) 안전 oom 위험 ( ⚠️ ) 안전
임계값 통과

 

GitHub main 브랜치는 XSSF 방식, 개인 브랜치(hankyung)는 SXSSF 방식으로 관리되어 있음. 각 브랜치를 AWS EC2에 배포한 후 동일한 K6 시나리오로 부하 테스트를 진행했음.

SXSSF로 리팩토링한 이유는 XSSF 방식의 경우 동시 사용자가 몰릴 때 전체 데이터를 힙에 올리는 구조상 OOM이 발생할 수 있기 때문이었음. 즉, 속도보다 서버 안정성을 우선한 선택이었음.

그러나 t2.small + 동시 10명 조건에서는 XSSF도 OOM 없이 정상 동작했음. 이는 테스트 조건이 OOM을 재현하기에 충분하지 않았기 때문으로 판단됨.

이를 검증하기 위해 JVM 힙을 -Xmx64m으로 강제 제한하고 VU를 50명으로 늘린 조건에서 재테스트를 진행했으며, 그 결과는 6번 섹션에서 확인할 수 있음.

 

+ 로컬이나 AWS환경이나 둘 다 리팩토링 전 인 XSSF 방식이 응답시간이 빠름.

 

+ 추가적으로 sxssf의 경우 응답시간이 느린 이유를 이전 포스팅에 적어뒀으나 한번 더 정리하자면

 ■ 왜 SXSSF가 느렸는가?
SXSSF는 메모리 사용량을 줄이기 위해 데이터를 스트리밍 방식으로 처리한다.

이 과정에서:
1. 페이지 단위 DB 조회가 반복 발생
2. 임시 파일 flush I/O 발생
3. 단일 조회 기반 XSSF보다 DB 왕복 횟수가 증가

결과적으로 메모리 안정성은 확보했지만 응답시간은 크게 증가했다.

5. Grafana 시각화 (k6 부하테스트)

K6 실행 시 --out influxdb 옵션으로 결과를 InfluxDB에 저장하고, Grafana Explore에서 시각화함.

k6 run --out influxdb=http://[EC2_IP]:8086/k6 excel_test.js

 

■ vus 그래프

 

vu가 3명 -> 10명 단계적으로 증가함.(k6 시나리오 대로 가상사용자가 단계적으로 증가했음을 확인)


■ http_req_duration 그래프

 

XSSF의 경우 응답시간이 거의 0에 가깝고 SXSSF의 경우 35K(35초)로 나타남.

 

※ 시각화를 통해 확인한 것

- vu가 3명 -> 10명으로 늘어날수록 sxssf의 응답시간이 급격히 증가함. (db 왕복 11회 누적이 영향인 것으로 보임.)

- xssf는 동시 사용자가 늘어도 응답시간이 거의 일정하게 유지됨. (단일 db 조회라 영향 적음)

- 즉, 동시 사용자가 늘어날수록 sxssf 응답시간은 느려짐 ...

 

상기 시각화 결과는 HTTP 응답시간 기반 부하 테스트 결과임. 서버 내부 메트릭(CPU, JVM 힙 메모리 사용량 등)은 별도 모니터링 도구(Actuator + Prometheus + Grafana)가 필요하며, 이번 테스트에서는 구축하지 않음. 따라서 XSSF의 실제 메모리 사용량 증가는 이번 테스트에서 수치로 확인하지 못함.

 

일단 하기부터는 테스트 조건 변경 후 다시 aws 배포한 후에 k6부하테스트 한번 더 진행해보겠음.


6. JVM 힙 제한 후 재테스트(AWS XSSF, AWS SXSSF)

■ 테스트 조건

항목
JVM 힙 제한 -Xmx64m (docker-compose.yml 환경변수로 설정)
서버 구성 spring-app-1 단독 (로드밸런싱 제거)
VU 50명
테스트 시간 7분

 

※ JVM(Java Virtual Machine) 힙 제한은 docker-compose.yml의 environment 항목에 JAVA_TOOL_OPTIONS=-Xmx64m을 추가하는 방식으로 설정함. 이 환경변수는 JVM 시작 시 자동으로 읽혀 힙 메모리 상한을 64MB로 제한함.

 

Spring Boot가 JVM 위에서 실행되어 있고, 힙(Heap)메모리는 JVM이 객체를 저장하는 공간. XSSF의 경우 10,112건 조회시 데이터가 전부 힙에 올라감.

 

※ -Xmx64m :

Xmx = 힙 최대 크기 설정 64m = 64MB, 즉 힙을 최대 64MB까지만 쓰도록 제한함. 64MB 꽉 차면 OOM 발생. 

 

■ XSSF

 

■ SXSSF

 

■ 결과 비교

지표 XSSF SXSSF
평균 응답시간 2.22s 39.05s
p95 응답시간 14.29s 59.99s
에러율 2.52% 89.33%
총 요청 수 3,365건 272건
200 OK 97% 10%
OOM 발생 ✅ 서버 다운 + 재시작 ✅ 간헐적 발생, 서버 생존

 

※ 200 OK : HTTP 응답코드. 서버가 요청을 정상적으로 처리하면 200 리턴함.

 

  • XSSF 97% = 3,365건 중 97%는 정상 응답
  • SXSSF 10% = 272건 중 10%만 정상 응답, 나머지 90%는 응답 못 받음

SXSSF 에러율이 89%인 이유

- 요청 1개 처리하는 데 평균 39초가 걸리는데, k6 스크립트에 timeout: '60s' 설정되어 있어서, 50명 동시에 요청하면 

50명 × 39초 = 서버가 동시에 처리해야 할 부하가 몰림

→ 대기 중인 요청들이 60초 넘기 전에 응답 못 받음 → timeout 발생 → 에러로 처리

 

메모리 부족이 아니라 응답시간이 느려서 에러가 났다고 보면 됨. (89% 요청이 엑셀 다운로드 실패한 것임....) 결국 xssf의 경우 에러율은 2.52%지만 서버가 죽었기 때문에 다운 -> 재시작 (10~15초 동안 서비스 중단) -> 트래픽 계속 오면 -> 다운 -> 재시작 -> 다운 -> 재시작 ....

 

반면 sxssf는 서버가 살아있기 때문에, 트래픽이 줄어들면 정상적으로 엑셀 다운로드가 되고 다른 api(예약 등)는 영향 없이 정상 동작하게 되는 것임...(서버가 죽지 않는 결과!) 

 


■ XSSF - 서버 다운 확인

 

동시 요청 50명이 각각 별도 스레드에서 10,112건 전체를 힙에 올리려다 연쇄적으로 OOM 발생. Started FinalProjectApplication 직후 OutOfMemoryError가 터지며 핵심 스레드(Acceptor, Catalina-utility)가 죽음. restart: unless-stopped 설정으로 자동 재시작됐지만 재시작 중 서비스 중단이 발생함.


■ SXSSF — 서버 생존 확인

 

OOM이 간헐적으로 발생하지만 페이징 처리가 계속 진행됨.

 

테스트 종료 후에도 spring-app-1 Up 20 minutes — 서버가 살아있음.

 

XSSF vs SXSSF 메모리 처리

※ 결론

SXSSF도 힙 64MB + VU 50명 극한 조건에서는 OOM이 발생함. 그러나 XSSF와 결정적인 차이가 있음.

  • XSSF = OOM 발생 시 서버 다운 → 재시작 (서비스 중단)
  • SXSSF = OOM 간헐적 발생해도 서버 생존, 페이징 처리 계속

이것으로 SXSSF 리팩토링의 목적 달성을 확인할 수 있었음. 속도는 느리지만 고부하 상황에서 서버가 죽지 않음.

 

- 추후 개선(보완) 계획

SXSSF로 안정성은 확보했으나, 속도를 개선할 필요가 있음.

 

1) DB 인덱스 추가

현재 페이징 쿼리는 created_at, reservation_status 컬럼에 인덱스가 없어 매 페이지 조회 시 Full Scan이 발생하는 것으로 추정됨. 두 컬럼에 복합 인덱스를 추가하면 페이징 쿼리 속도 개선이 가능하며, 11번의 db왕복 시간을 단축할 수 있음.

 

2) HikariCP 커넥션 풀 튜닝

현재 maximum-pool-size 기본값(10)으로 설정되어 있어 동시 요청 50명이 몰릴 경우 DB 커넥션 대기가 발생할 수 있음. maximum-pool-size를 적절히 늘려 DB 연결 대기시간을 단축할 예정임.

 

3) 비동기 처리 도입

엑셀 생성을 백그라운드에서 처리하고 완료 시 다운로드 링크를 제공하는 방식으로 전환하면, 사용자가 응답을 기다리지 않아도 되므로 체감 응답시간이 대폭 개선될 것으로 예상됨. @Async + 임시 파일 저장 방식으로 구현 예정.

 

위 세 가지 개선 후 동일 조건(힙 64MB + VU 50명)으로 재테스트하여 SXSSF 응답시간이 얼마나 개선되는지 검증할 예정임.