🎯 k6 부하테스트 가이드

📑 목차


1. 설치

brew install k6

2. 기본 명령어

# 기본 실행
k6 run script.js
 
# VU(가상 유저) + 지속 시간 지정
k6 run --vus 10 --duration 30s script.js
 
# 결과를 JSON으로 저장
k6 run --out json=result.json script.js
 
# 상세 통계 출력
k6 run --summary-trend-stats="avg,p(90),p(95),p(99),max" script.js

3. 스크립트 작성

💡 기본 구조

import http from 'k6/http';
import { check, sleep } from 'k6';
import { Trend, Rate } from 'k6/metrics';
 
// 커스텀 메트릭
const orderDuration = new Trend('order_duration');
const orderSuccess  = new Rate('order_success');
 
// 부하 시나리오 설정
export const options = {
  stages: [
    { duration: '10s', target: 10  },  // 워밍업
    { duration: '30s', target: 50  },  // 부하 증가
    { duration: '20s', target: 100 },  // 피크
    { duration: '10s', target: 0   },  // 쿨다운
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95%ile 500ms 이하
    order_success:     ['rate>0.9'],   // 성공률 90% 이상
  },
};
 
// 각 VU가 반복 실행하는 함수
export default function () {
  const res = http.get('https://example.com/api/items');
 
  check(res, {
    'status 200': r => r.status === 200,
    'body ok':    r => r.body.length > 0,
  });
 
  sleep(1);
}

💡 타임딜 프로젝트 예시 (재고 경쟁 시뮬레이션)

import http from 'k6/http';
import { check, sleep } from 'k6';
import { Trend, Rate } from 'k6/metrics';
 
const BASE_URL = 'http://<NLB_URL>';
 
const orderDuration = new Trend('order_duration');
const orderSuccess  = new Rate('order_success');
 
export const options = {
  stages: [
    { duration: '10s', target: 10  },
    { duration: '30s', target: 50  },
    { duration: '20s', target: 100 },
    { duration: '10s', target: 0   },
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],
    order_success:     ['rate>0.9'],
  },
};
 
export default function () {
  // 1. 목록 조회
  const listRes = http.get(`${BASE_URL}/api/timedeals`);
  check(listRes, { 'list 200': r => r.status === 200 });
 
  sleep(0.5);
 
  // 2. 주문 (1~3번 딜 랜덤)
  const payload = JSON.stringify({
    timedealId: Math.ceil(Math.random() * 3),
    quantity: 1,
  });
 
  const orderRes = http.post(`${BASE_URL}/api/orders`, payload, {
    headers: { 'Content-Type': 'application/json' },
  });
 
  const ok = check(orderRes, {
    'order 200':       r => r.status === 200,
    'order completed': r => {
      try { return JSON.parse(r.body).status === 'COMPLETED'; }
      catch { return false; }
    },
  });
 
  orderDuration.add(orderRes.timings.duration);
  orderSuccess.add(ok);
 
  sleep(1);
}

4. 실행 패턴

목적명령어
기본 실행k6 run script.js
스파이크 테스트k6 run --vus 200 --duration 10s script.js
상세 통계k6 run --summary-trend-stats="avg,p(90),p(95),p(99),max" script.js
결과 저장k6 run --out json=result.json script.js
# 원라이너 (스크립트 파일 없이)
k6 run --vus 20 --duration 20s - <<'EOF'
import http from 'k6/http';
export default () => { http.get('https://example.com'); }
EOF

5. 결과 해석

checks.........................: 94.20%  ✓ 5652  ✗ 349
data_received..................: 1.2 MB  20 kB/s
http_req_duration..............: avg=142ms  p(90)=310ms  p(95)=450ms  p(99)=820ms
http_req_failed................: 5.80%   ✗ 349
order_success..................: 91.30%  ✓ 2743  ✗ 261
vus............................: 100     min=10   max=100
항목의미기준
p(95)95%의 요청이 이 시간 이하< 500ms 권장
http_req_failed서버 에러(5xx) 비율< 1% 권장
order_success rate비즈니스 성공률 (재고 부족 포함)목표에 따라 다름
checkscheck() 통과율> 99% 권장

thresholds 설정

thresholds에서 조건을 벗어나면 k6가 exit code 1로 종료됨 → CI/CD 파이프라인에서 게이트로 활용 가능

stages vs --vus/--duration

  • stages: 점진적 부하 증가/감소 시나리오 (스크립트에 정의)
  • --vus --duration: 고정 부하 (CLI 플래그, 스크립트 stages 무시)