7 분 소요

🧪 홈페이지 스트레스 테스트 완벽 가이드 7부작 — 새로 만든 홈페이지가 얼마나 단단한지 확인하려면 무엇을, 어떻게, 어떤 지표로 재야 할까요. 목표 지표 설계부터 부하·프론트엔드·백엔드·복원력·보안, 그리고 실제 리포트 해석까지, 홈페이지를 무너뜨려 보며 한계를 재는 테스트를 편마다 하나씩. 전체 7편.
  1. 무엇을·왜·어떤 지표로 재는가 — 테스트 종류와 목표 지표
  2. k6 로 부하·스파이크·소크 돌리기 — 실전 부하 시나리오
  3. 프론트엔드 성능 — 코어 웹 바이탈과 렌더링 스트레스
  4. 백엔드·DB 병목 찾기 — 모니터링과 프로파일링
  5. 복원력 테스트 — 카오스 엔지니어링과 장애 주입
  6. 보안·가용성 스트레스 — Rate Limit·DDoS·종합 체크리스트
  7. 샘플 리포트로 읽는 결과 해석 — 숫자를 판정과 액션으로지금 글

Summary

여섯 편에 걸쳐 부하(2편)·프론트엔드(3편)·백엔드(4편)·복원력(5편)·보안(6편)을 각각 어떻게 테스트하는지 배웠어요. 그런데 실전에서 진짜 어려운 건 테스트를 돌리는 것보다 나온 숫자를 해석하는 것입니다. p95 가 780ms 인데 이게 통과인가? 메모리가 조금 늘었는데 이게 누수인가 정상인가? 어느 문제부터 고쳐야 하나?

이번 마지막 편은 그 해석을 연습합니다. 가상의 쇼핑몰 홈페이지 shop.example.com 에 지금까지 배운 테스트를 전부 돌렸다고 가정하고, 그 결과를 담은 샘플 스트레스 테스트 리포트를 처음부터 끝까지 함께 읽어요. 숫자가 어떻게 나왔고, 무엇을 통과/실패로 판정했고, 발견한 문제를 어떤 우선순위로 액션으로 바꿨는지까지. 실제 리포트를 쓸 때 그대로 참고할 수 있는 한 장 템플릿으로 마무리합니다.

💡 이 글에서 다루는 것

  • 스트레스 테스트 리포트의 표준 구조
  • 테스트 대상·조건과 판정 기준(SLO) 명시
  • 시나리오별 결과 읽기 — 부하·스파이크·소크·프론트·백엔드
  • 발견 문제를 심각도로 분류하고 우선순위 액션으로
  • 리포트 한 장 요약 템플릿



1. 리포트의 구조

숫자를 잔뜩 던져놓은 문서는 리포트가 아니에요. 좋은 스트레스 테스트 리포트는 읽는 사람이 5분 안에 “오픈해도 되나?”에 답할 수 있게 짜여 있어야 합니다. 표준 구조는 이래요.

섹션 담는 내용 읽는 사람
총평 (Executive Summary) 한 문단 결론
+ 오픈 가부
모두
테스트 조건 대상·환경·시나리오·기준 재현하려는 사람
시나리오별 결과 지표 표 + 판정 엔지니어
발견 사항 문제 + 원인 + 근거 엔지니어
권고·액션 우선순위별 할 일 의사결정자

핵심은 결론을 맨 위에 두는 거예요. 바쁜 사람은 총평만 읽고, 궁금한 사람만 아래로 내려갑니다. 그럼 이 구조 그대로 샘플 리포트를 채워볼게요.



2. 총평 — 리포트 맨 위

리포트의 첫 화면입니다. 여기만 읽어도 상황이 잡혀야 해요.

📋 총평. shop.example.com 오픈 전 스트레스 테스트를 수행했다. 결론: 조건부 오픈 가능. 예상 피크(동시 500명)의 부하는 목표 지표를 만족하며 견딘다. 다만 소크 테스트에서 DB 커넥션 누수가, 백엔드에서 상품 리뷰의 N+1 쿼리가 발견됐다. 둘 다 오픈 후 트래픽이 누적되면 장애로 이어질 수 있어 오픈 전 수정을 권고한다. 프론트엔드 LCP 는 기준을 소폭 초과(2.8s)하나 심각하지 않아 오픈 후 개선 대상으로 둔다.

이 한 문단에 세 가지가 다 들어 있어요. 결론(조건부 오픈), 잘 된 것(예상 부하는 견딤), 막는 것(커넥션 누수·N+1 은 오픈 전 수정). 나머지 섹션은 이 문단의 근거일 뿐입니다.



3. 테스트 조건 — 무엇을, 어떤 기준으로

결과를 믿으려면 조건을 알아야 해요. 1편에서 강조한 대로, 판정 기준(SLO)을 먼저 못 박아둡니다.

  • 대상: shop.example.com (스테이징, 운영과 동일 스펙 · 운영 규모 데이터 복제)
  • 환경: 앱 서버 2대(4vCPU/8GB), DB 1대(8vCPU/32GB), 앞단 CDN
  • 부하 생성: k6, 별도 리전에서 실행
  • 시나리오: 부하 / 스파이크 / 소크 / 브레이크포인트 + 프론트엔드 + 백엔드 모니터링

판정 기준은 이렇게 표로 못 박았어요. 이게 있어야 결과에 초록/빨강을 매길 수 있습니다.

지표 목표(SLO)
동시 접속 500 VU 에서 안정
p95 응답 시간 < 800 ms
에러율 < 0.1 %
서버 CPU < 80 % 유지
소크(3h) 자원 우상향 없음



4. 부하 테스트 결과 — 예상 부하는 견디나

먼저 예상 피크(500 VU)까지 서서히 올리는 부하 테스트. k6 요약을 발췌하면 이래요.

     http_reqs......................: 1,284,300  713.5/s
     http_req_duration..............: avg=190ms  p(90)=410ms  p(95)=680ms  p(99)=1.2s
     http_req_failed................: 0.04%   ✓ 514  ✗ 1,283,786
     vus............................: 500     max=500

     ✓ http_req_duration..........: p(95)=680ms  (기준 p95<800 통과)
     ✓ http_req_failed............: rate=0.0004  (기준 rate<0.001 통과)

이걸 2편에서 배운 순서(에러율 → p95 → 처리량)로 읽어볼게요.

지표 결과 기준 판정
에러율 0.04 % < 0.1 % ✅ 통과
p95 680 ms < 800 ms ✅ 통과
p99 1.2 s (참고) ⚠️ 주시
처리량 713 RPS ✅ 목표 충족
CPU(피크) 71 % < 80 % ✅ 통과

에러율·p95 모두 기준 안이라 부하 테스트는 통과예요. 한 가지 눈여겨볼 건 p99 가 1.2초로 튄다는 점입니다. 평균과 p95 는 좋은데 가장 느린 1%가 1.2초라는 건, 대부분은 빠르지만 일부 요청에서 뭔가 오래 걸린다는 신호예요. 이건 뒤의 백엔드 분석에서 N+1 의 꼬리로 다시 등장합니다.

✅ 여기서 배울 점: 평균이 좋다고 끝이 아니에요. 꼬리(p99) 를 봐야 숨은 문제의 실마리가 보입니다. p95 는 통과라도 p99 가 튀면 “왜 저 1% 만 느리지?”를 파고들 가치가 있어요.



5. 스파이크·소크 결과 — 시간이 드러낸 것

스파이크 (10초 만에 1500 VU)

이벤트 오픈 순간을 흉내 낸 스파이크 테스트예요.

  • 폭증 직후 30초간 에러율이 2.1%까지 상승했다가, 오토스케일이 3번째 서버를 띄운 뒤 정상화.
  • 부하가 빠진 뒤 90초 만에 p95 가 평상시(680ms)로 복귀 → 회복도 정상이에요.

판정은 조건부 통과예요. 폭증 순간의 짧은 에러(2.1%)는 오토스케일이 뜨기 전의 1~2분이라 불가피한 면이 있지만, 이벤트가 예정돼 있다면 미리 서버를 늘려두는(프리워밍) 걸 권고 사항에 넣습니다.

소크 (300 VU, 3시간) — 문제 발견

부하는 3시간 내내 일정했는데, 서버 자원 그래프가 이렇게 나왔어요.

[소크 3시간 — 부하 일정(300 VU)]
메모리 사용률     :  42% → 44% → 43%   (안정, 정상)
DB 커넥션(사용중) :  18 → 27 → 39 → 48  (계속 우상향 ⚠️)
p95 응답 시간     : 620ms → 700ms → 890ms (서서히 악화 ⚠️)

메모리는 안정적인데 DB 커넥션이 3시간 내내 우상향했고, 그에 따라 p95 도 서서히 나빠졌어요. 4편에서 다룬 전형적인 커넥션 누수 패턴입니다. 부하가 일정한데 사용 중 커넥션이 계속 는다는 건, 요청 처리 후 커넥션을 반납하지 않는 코드가 있다는 뜻이에요.

🚨 이게 이번 테스트의 가장 중요한 발견입니다. 짧은 부하 테스트(4번 결과)에서는 전혀 안 보였어요. 소크 테스트가 아니었으면 오픈 후 며칠 뒤 새벽에 커넥션 풀이 고갈되며 서버가 통째로 멈췄을 문제입니다. 이래서 소크 테스트를 하는 거예요.



6. 프론트엔드 결과 — 코어 웹 바이탈

3편의 Lighthouse 를 모바일·느린 네트워크 조건으로 3회 측정한 중앙값이에요.

지표 결과 기준 판정
LCP 2.8 s < 2.5 s ⚠️ 초과
INP 140 ms < 200 ms ✅ 통과
CLS 0.03 < 0.1 ✅ 통과

INP·CLS 는 통과인데 LCP 가 2.8초로 기준(2.5초)을 소폭 초과했어요. Lighthouse 개선 제안을 보니 메인 히어로 이미지가 최적화 안 된 큰 PNG(1.8MB)라, WebP 로 바꾸고 지연 로딩만 걸어도 기준 안에 들 것으로 보입니다. 심각도는 낮음 — 사용성을 크게 해치는 수준은 아니라, 오픈을 막지는 않고 개선 대상으로 둡니다.



7. 백엔드 병목 — p99 꼬리의 정체

4번에서 본 p99 1.2초의 정체를, 4편의 APM 으로 파고들었어요. 가장 느린 요청 하나를 시간 분해하니 이렇게 나왔습니다.

요청: GET /products/{id}          총 1,180 ms
├─ 라우팅·미들웨어                    25 ms
├─ DB 쿼리 (상품 조회)               140 ms
├─ DB 쿼리 (리뷰 N+1 ×48)            870 ms   ← 범인
├─ 외부 API (배송비)                 100 ms
└─ 렌더링                            45 ms

상품 상세 페이지가 리뷰를 가져올 때 리뷰 개수만큼 쿼리를 반복하는 N+1 쿼리였어요. 리뷰가 많은 인기 상품일수록 느려지니, 4번의 p99 꼬리(가장 느린 1%)가 바로 리뷰 많은 상품 페이지였던 거예요. 하나의 쿼리로 묶으면(eager loading) 해결됩니다.

💡 리포트의 힘이 여기서 나와요. “부하 테스트에서 p99 가 튄다”(4번)와 “상품 페이지가 느리다”(APM)와 “리뷰 N+1”(원인)이 하나의 이야기로 연결됩니다. 각 층의 결과를 따로 보지 않고 엮어야, 증상이 아니라 원인을 고칠 수 있어요.



8. 브레이크포인트 — 우리 한계는 몇 RPS

마지막으로 죽을 때까지 부하를 올려 최대 용량을 쟀어요.

  • ~850 RPS 까지는 SLO 유지.
  • 850 RPS 를 넘자 DB CPU 가 95%에 닿으며 p95 가 급격히 악화, 920 RPS 에서 에러율 급증.
  • 즉 현재 구성의 안전 한계는 약 850 RPS, 예상 피크(713 RPS)와의 여유는 약 19%.

이 여유가 충분한지는 판단의 영역이에요. 평상시엔 괜찮지만, 마케팅으로 트래픽이 30% 뛰면 한계를 넘습니다. 그래서 권고에 DB 읽기 부하 분산(리드 레플리카) 을 중기 과제로 넣었어요.



9. 종합 판정과 액션 아이템

이제 발견을 모아 심각도로 분류하고 우선순위 액션으로 바꿉니다. 이게 리포트에서 의사결정자가 실제로 보는 부분이에요.

발견 심각도 액션 시점
DB 커넥션 누수
(소크)
🔴 높음 커넥션 반납 누락
수정
오픈 전
리뷰 N+1 쿼리 🔴 높음 eager loading
으로 묶기
오픈 전
스파이크 초기
에러 2.1%
🟡 중간 이벤트 전
서버 프리워밍
오픈 전
(이벤트 시)
LCP 2.8s 🟢 낮음 히어로 이미지
WebP·지연로딩
오픈 후
한계 여유 19% 🟡 중간 리드 레플리카
도입
중기

심각도를 나누는 기준은 “오픈 후 장애로 이어지는가” 예요. 커넥션 누수와 N+1 은 시간이 지나면 반드시 터지므로 🔴 높음, LCP 는 불편하지만 장애는 아니라 🟢 낮음. 이렇게 나눠야 “무엇을 오픈 전에 꼭 고치고, 무엇을 나중에 해도 되는가”가 명확해집니다.

✅ 좋은 리포트는 문제를 나열하는 데서 끝나지 않아요. 각 문제에 심각도·액션·시점을 붙여 받는 사람이 바로 우선순위를 잡을 수 있게 합니다. “느립니다”가 아니라 “리뷰 N+1 을 오픈 전에 eager loading 으로 고치세요”까지 써야 리포트예요.



10. 리포트 한 장 템플릿

지금까지의 구조를 그대로 복사해 쓸 수 있게 템플릿으로 정리했어요. 새 홈페이지를 테스트할 때 이 틀에 숫자만 채우면 됩니다.

# [사이트명] 스트레스 테스트 리포트 (YYYY-MM-DD)

## 총평
- 결론: [오픈 가능 / 조건부 / 불가]
- 견딘 것: [한 줄]
- 막는 것: [오픈 전 필수 수정 항목]

## 테스트 조건
- 대상 / 환경 / 데이터:
- 시나리오: 부하 / 스파이크 / 소크 / 브레이크포인트
- 판정 기준(SLO): p95 < __ms, 에러율 < __%, CPU < __%

## 시나리오별 결과  (지표 | 결과 | 기준 | 판정)
- 부하 / 스파이크 / 소크 / 프론트(CWV) / 백엔드
- 최대 용량(브레이크포인트): __ RPS, 피크 대비 여유 __%

## 발견 사항  (발견 | 원인 | 근거)

## 권고·액션  (발견 | 심각도 | 액션 | 시점)



11. 정리 — 시리즈를 마치며

  • 리포트는 결론을 맨 위에 — 총평만 읽어도 오픈 가부가 잡히게
  • 판정 기준(SLO)을 먼저 못 박아야 결과에 초록/빨강을 매긴다
  • 각 층 결과를 하나의 이야기로 엮기 — p99 꼬리 → 상품 페이지 → 리뷰 N+1
  • 소크 테스트가 짧은 부하에선 안 보이는 커넥션 누수를 잡았다
  • 발견을 심각도·액션·시점으로 바꿔야 비로소 리포트가 된다

일곱 편에 걸쳐 홈페이지를 계획하고, 부수고, 들여다보고, 흔들고, 방어를 시험하고, 마지막으로 그 결과를 리포트로 해석하는 데까지 왔어요. 스트레스 테스트의 진짜 결과물은 예쁜 그래프가 아니라 “이건 오픈 전에 고치고, 저건 나중에 해도 된다”는 명확한 판단이에요. 그 판단을 숫자로 뒷받침할 수 있게 되었다면, 이 시리즈는 제 몫을 다한 겁니다.

일단 오늘은 여기까지….. 긴 시리즈 따라와 주셔서 고맙습니다. 이제 자신 있게, 단단한 홈페이지를 오픈하세요.


← 이전 글: (6/7) 홈페이지 스트레스 테스트 — 보안·가용성과 종합 체크리스트