부하 테스트 결과 Redis와 RDS는 비교적 여유가 있었지만, EC2 CPU 사용률은 지속적으로 높은 상태를 유지했다.

특히 Socket 연결 유지, Polling 요청 처리, 주문 API 처리 등이 모두 애플리케이션 서버에 집중되면서 CPU 사용률이 병목 구간에 진입하는 현상을 확인할 수 있었다.

초기에는 동일한 범용 인스턴스 계열(m5.xlarge)로의 증설도 고려했지만, 병목 원인이 CPU 성능이라고 판단하여 Compute Optimized 계열인 C7i를 우선 검토하였다.

먼저 c7i.xlarge 환경에서 동일한 부하 테스트를 수행하였다.

모든 환경은 동일

EC2 m5.large → c7i.xlarge 변경

시나리오

  • Polling User 90%
  • Ordering User 10%

동시 접속

  • 1000명
  • 2000명
  • 3000명
  • 4000명

C7i.xlarge 신버전 1000명 테스트

  • artillery 소켓연결 1000명 이후 k6 실행 후 7분 30초 경과

1000명 규모에서 충분히 안정적인 성능을 보였다.


C7i.xlarge 신버전 2000명 테스트

  • artillery 소켓연결 2000명 이후 k6 실행 후 7분 30초 경과

2000명 규모에서 충분히 안정적인 성능을 보였다.


C7i.xlarge 신버전 3000명 테스트

  • artillery 소켓연결 3000명 이후 k6 실행 후 7분 30초 경과

3000명 규모에서 충분히 안정적인 성능을 보였다.


C7i.xlarge 신버전 4000명 테스트

  • artillery 소켓연결 4000명 이후 k6 실행 후 7분 30초 경과

 

4000명 규모에서도 충분히 안정적인 성능 보였다 .


신버전 5000명 테스트

  • artillery 소켓연결 5000명 이후 k6 실행 후 7분 30초 경과

구조 개선 후 c7i.xlarge 환경에서 5000명 규모까지 안정적으로 처리할 수 있었다.

 

-결과-



테스팅 목표 4000명 동시 시청 방송중 극한의 상황 에서도 3초 이내에 응답하는 환경 만들기


오픈런 테스트 (극한상황에서 집중 테스트)

4000명이 방송을 보는 도중 갑자기 1200명이 30초 안에 동시에 주문 버튼 연속으로 누르면 서버가 버티는가?

(Redis Lua / Bull Queue / DB 한계 확인)

테스트 시나리오

00:00 ~ 01:00 - 시청자 4,000명 접속 - 상품 상태 주기 조회

01:00 - 오픈런 시작 - 구매자 1,200명 동시 진입

01:00 ~ 01:30 - 상품 조회 - 주문 요청 집중 발생

01:35 - 오픈런 종료

04:00 - 테스트 종료

  • 시청자 그룹 : 상품 목록 및 상태를 10~15초 간격으로 반복 조회하며, 오픈런이 발생하는 순간에도 지속적으로 API를 호출하도록 구성하였다.
  • 구매자 그룹: 각 사용자는 로그인 후 상품 목록을 조회하고, 별도의 대기 시간 없이 즉시 주문을 시도하였다. 또한 1~3초 간격으로 주문을 재시도하여 실제 사용자가 반복적으로 구매 버튼을 누르는 상황을 재현하였다. 특히 sleep 시간을 최소화하여 서버가 감당할 수 있는 최대 처리량을 확인하는 데 초점을 맞추었다.

서버가 장애 없이 모든 요청을 처리했다

Redis Lua Script 기반 재고 선점 로직 역시 정상적으로 동작하였으며, 과매도(Oversell) 현상 없이 모든 주문 요청이 처리되었다.

다만 주문 응답 시간(P95)은 4.77초로 측정되어 목표 기준인 3초 이내를 만족하지 못하였다.



솔드 아웃 테스트

품절(Sold Out) 테스트는 동시성 환경에서 재고 정합성이 올바르게 유지되는지를 검증하기 위한 시나리오이다.

2,800명의 시청자와 1,200명의 구매자가 동시에 접속하는 상황을 구성하였다.

구매자는 상품 조회 과정을 생략하고 즉시 주문을 시도하도록 설정하였으며, 재고가 소진된 이후에도 1~3초 간격으로 지속적으로 주문을 재시도하였다.

0:00 - 폴러 2,800명 진입 - 쇼퍼 1,200명 진입

00:10 - 총 4,000명 부하 유지 - 주문 집중 발생

00:10 ~ 03:10 - 구매자 지속 주문 시도 - 재고 소진 후에도 반복 요청

03:10 - 테스트 종료

재고 수량만큼만 정확하게 판매하고 나머지는 정상적으로 거절하는 것

 

█ TOTAL RESULTS

checks_total.......: 268639  1253.502114/s
checks_succeeded...: 100.00% 268639 out of 268639
checks_failed......: 0.00%   0 out of 268639

✓ login 200
✓ login has token
✓ [S4] 성공/품절/중복차단
✓ [S4] 서버 에러 없음
✓ [S4 poller] 200

CUSTOM
error_count....................: 0       0/s
order_duration_ms..............: avg=54.41ms min=2ms    med=16ms    max=1.15s    p(90)=130ms    p(95)=180ms
order_success_rate.............: 0.26%   300 out of 111268
oversell_detected..............: 0       0/s
poller_api_success.............: 100.00% 43703 out of 43703
soldout_409_count..............: 53870   251.363945/s
success_count..................: 300     1.399836/s

HTTP
http_req_duration..............: avg=53.2ms  min=2.19ms med=14.83ms max=979.02ms p(90)=129.24ms p(95)=180.06ms
  { expected_response:true }...: avg=50.95ms min=2.19ms med=13.23ms max=979.02ms p(90)=128.4ms  p(95)=180.5ms
http_req_failed................: 71.05%  110968 out of 156172
http_reqs......................: 156172  728.717469/s

EXECUTION
iteration_duration.............: avg=5.02s   min=1s     med=2.45s   max=16.83s   p(90)=13.3s    p(95)=14.19s
iterations.....................: 154971  723.113457/s
vus............................: 3       min=0                max=4000
vus_max........................: 4000    min=1068             max=4000

NETWORK
data_received..................: 72 MB   338 kB/s
data_sent......................: 30 MB   138 kB/s

HTTP 실패율은 71.05%로 높게 보이지만, 이는 재고 소진 이후 발생한 409 품절 응답이 k6 기준에서 실패로 집계되었기 때문이다.

결과적으로 주문 성공 건수는 300건으로 제한되었고, 과매도 감지 건수는 0건, 서버 오류 역시 0건으로 측정되었다.

이는 Redis Lua Script 기반 재고 선점 로직이 대량 동시 주문 상황에서도 재고를 정확하게 보호했음을 의미한다.

또한 주문 응답 시간 P95는 180ms로 측정되어, 품절 상황에서도 응답 지연 없이 빠르게 정상 거절이 이루어졌음을 확인할 수 있었다.



High Stock 테스트

오픈런 테스트가 서버의 생존성을 검증하고, 품절 테스트가 재고 정합성을 검증하는 시나리오였다면, High Stock 테스트는 서버가 실제로 초당 몇 건의 주문을 처리할 수 있는지 측정하기 위한 시나리오이다.

이번 테스트에서는 품절로 인한 409 응답이 발생하지 않도록 충분한 재고를 준비한 상태에서 부하를 발생시켰다.

즉, 재고 부족이라는 변수를 제거하고 순수하게 주문 처리 성능(TPS)을 측정하는 데 목적이 있다.

00:00 ~ 01:00 - 폴러 2,800명 접속 - 쇼퍼 1,200명 접속

01:00 ~ 09:00 - 총 4,000명 유지 - 지속적인 조회 및 주문 발생

09:00 - 테스트 종료

  • 시청자 그룹: 상품 상태 API를 10~15초 간격으로 반복 호출하여 실제 서비스에서 발생하는 조회 트래픽을 재현하였다.
  • 구매자 그룹:

오픈런 테스트와 달리 주문 직후 즉시 재시도하지 않고 2~5초의 대기 시간을 두었다.

상품 목록 조회
↓
상품 선택
↓
옵션 조회 (옵션 상품인 경우)
↓
주문
↓
2~5초 대기
↓
반복

테스트 7분 30초 후 서버 상태

 

█ TOTAL RESULTS

checks_total.......: 280572 457.801329/s
checks_succeeded...: 94.24% 264425 out of 280572
checks_failed......: 5.75%  16147 out of 280572

✓ [S5 poller] 200
✓ login 200
✓ login has token
✗ [S5] 주문 성공
  ↳  99% — ✓ 84315 / ✗ 3
✗ [S5] 주문 3초 이내
  ↳  80% — ✓ 68174 / ✗ 16144

CUSTOM
order_duration_ms..............: avg=1.57s min=5ms    med=1.17s    max=8.68s  p(90)=3.73s  p(95)=4.2s
order_fast_count...............: 68174   111.237571/s
order_fast_ms..................: avg=1.03s min=5ms    med=779ms    max=2.99s  p(90)=2.44s  p(95)=2.71s
order_fast_rate................: 80.85%  68174 out of 84318
order_slow_count...............: 16144   26.341704/s
order_slow_ms..................: avg=3.88s min=3s     med=3.76s    max=8.68s  p(90)=4.76s  p(95)=5.08s
order_slow_rate................: 19.14%  16144 out of 84318
order_success_rate.............: 99.99%  84315 out of 84318
order_tps......................: 84315   137.57438/s
poller_api_success.............: 100.00% 109536 out of 109536

HTTP
http_req_duration..............: avg=1.5s  min=2.27ms med=984.93ms max=9.17s  p(90)=3.76s  p(95)=4.17s
  { endpoint:live_status }.....: avg=1.23s min=2.27ms med=626.33ms max=7.5s   p(90)=3.47s  p(95)=3.99s
  { endpoint:order }...........: avg=1.51s min=5.67ms med=1.11s    max=8.68s  p(90)=3.66s  p(95)=4.15s
  { expected_response:true }...: avg=1.5s  min=2.27ms med=984.91ms max=9.17s  p(90)=3.76s  p(95)=4.17s
http_req_failed................: 0.00%   3 out of 288077
http_reqs......................: 288077  470.047024/s

EXECUTION
iteration_duration.............: avg=11.2s min=2.01s  med=11.7s    max=23.51s p(90)=15.85s p(95)=16.92s
iterations.....................: 193854  316.306042/s
vus............................: 2       min=0                max=4000
vus_max........................: 4000    min=920              max=4000

NETWORK
data_received..................: 2.8 GB  4.5 MB/s
data_sent......................: 35 MB   57 kB/s

이번 테스트를 통해 현재 주문 시스템은 약 138 TPS 수준의 처리량을 안정적으로 처리할 수 있음을 확인하였다.

주문 성공률은 99.99%, 서버 오류율은 0%로 측정되었으며, 약 84,000건 이상의 주문이 정상 처리되었다.

반면 응답시간 측면에서는 개선이 필요한 것으로 확인되었다.

주문 응답시간 P95는 약 4.2초로 측정되었으며, 전체 주문의 약 19%는 3초를 초과하였다.

흥미로운 점은 서버 CPU, Redis CPU 사용률이 포화 상태가 아니었음에도 응답시간이 증가했다는 점이다.

이는 단순한 CPU 부족 문제가 아니라 Queue 처리 대기, DB 처리 대기, 네트워크 지연 등 시스템 내부의 대기시간이 누적된 결과로 판단된다.

따라서 본 테스트는 현재 구조가 높은 처리량을 확보했음을 확인하는 동시에, 향후 응답시간 개선을 위해 추가적인 병목 분석이 필요함을 보여주었다.


 


종합 현실 시나리오

앞선 테스트들은 각각 하나의 목적에 집중하였다.

하지만 실제 라이브커머스 방송은 단일 상황만 발생하지 않는다.

시청자가 방송을 시청하고, 일반 구매가 발생하고, 특정 시점에는 오픈런 이벤트가 열리며, 방송 종료 직전에는 마감 특가 구매가 몰리는 등 다양한 트래픽 패턴이 연속적으로 발생한다.

따라서 마지막 단계에서는 실제 방송 15분을 그대로 재현한 종합 시나리오를 구성하여 테스트를 수행하였다.

  • 방송 중 지속적인 조회 트래픽 처리
  • 일반 구매자의 반복 주문 처리
  • 오픈런 이벤트 발생 시 재고 정합성 유지
  • 마감 특가 이벤트 처리
  • 전체 방송 구간 동안 서비스 안정성 유지

시나리오 타임라인

00:00 ~ 01:00
방송 시작
시청자 4,000명 점진 진입

01:00 ~ 03:00
일반 구매 시작
600명 구매자 활동

03:00 ~ 03:35
오픈런 이벤트
500명 동시 진입
한정 상품 300개 판매

03:35 ~ 11:00
일반 방송 지속

11:00 ~ 13:30
마감 특가
300명 추가 구매

13:30 ~ 15:00
방송 종료

시청자 (Poller)

시청자는 실제 방송을 시청하는 사용자로 가정하였다.

10~15초 간격으로 상품 상태 API를 조회하며 지속적으로 조회 트래픽을 발생시킨다.

구매자 수가 증가하는 구간에서는 전체 접속자 수가 4,000명을 유지하도록 시청자 수를 조정하였다.

일반 구매자

일반 구매자는 실제 쇼핑 행동과 유사하게 구성하였다.

상품 목록 조회 → 상품 선택 → 주문 과정을 수행하며,

30~60초 간격으로 반복 구매한다.

오픈런 상품은 제외하여 일반 방송 구매 행동만 측정하였다.

오픈런 구매자

오픈런 구매자는 특정 한정 상품에 집중하도록 구성하였다.

500명이 5초 이내에 동시에 진입하며,

30초 동안 집중적으로 구매를 시도한다.

재고 300개가 모두 소진되면 이후 요청은 409(품절) 응답을 반환한다.

이를 통해 Redis Lua Script 기반 재고 차감 로직이 실제 오픈런 상황에서도 과매도 없이 동작하는지 검증하였다.

마감 특가 구매자

마감 특가 구매자는 방송 종료 직전 구매를 서두르는 사용자를 가정하였다.

15~30초 간격으로 반복 구매하며 일반 구매자보다 높은 구매 빈도를 가진다.

이를 통해 방송 종료 직전 발생하는 트래픽 집중 현상을 재현하였다.

 



최종 결과

Redis Lua Script 기반 재고 선점과 Bull Queue 기반 비동기 주문 처리 구조를 적용한 후 다양한 부하 테스트를 수행하였다.

단순 TPS 측정이나 스트레스 테스트에 그치지 않고,

  • 오픈런 상황(S3)
  • 품절 및 재고 정합성 검증(S4)
  • 최대 처리량 측정(S5)
  • 실제 방송 시뮬레이션(S7)

을 각각 분리하여 검증하였다.

특히 S7 종합 현실 시나리오에서는 4,000명의 시청자와 1,400명의 구매자가 참여하는 15분 방송을 재현하였다.

테스트 결과

  • 일반 구매 8,300건 처리
  • 오픈런 상품 300건 판매
  • 품절 응답 4,131건 정상 처리
  • 마감 특가 2,140건 처리
  • 과매도 0건
  • 서버 오류 0건

을 기록하였다.

또한 Redis Lua Script 기반 재고 선점 구조를 통해 대량 동시 주문 환경에서도 재고 정합성을 유지할 수 있음을 확인하였고, Bull Queue 기반 비동기 처리 구조를 통해 높은 처리량에서도 안정적인 주문 처리가 가능함을 검증하였다.

다만 오픈런(S3) 및 High Stock(S5) 시나리오에서는 주문 응답시간 P95가 약 4~5초 수준으로 측정되어 목표로 설정한 3초 이내 응답시간에는 도달하지 못하였다.

이는 서버 자원 부족보다는 Queue 처리 대기, DB 처리 대기 등의 내부 병목 가능성을 시사하며, 향후 Worker Concurrency 조정, 주문 처리 로직 최적화, Queue 처리 구조 개선 등을 통해 추가적인 성능 개선이 가능할 것으로 판단된다.

이번 개선을 통해 단순히 성능을 높이는 것을 넘어, 대규모 동시 접속 환경에서도 정합성과 안정성을 유지할 수 있는 주문 처리 구조를 구축할 수 있었다.

+ Recent posts