목차

K6 소개

부하·성능 테스트 도구 K6 소개

1. k6란?

k6는 서버·API·시스템의 부하 테스트, 스트레스 테스트, 성능 검증을 수행하기 위한 오픈소스 도구입니다. 트래픽을 코드 기반으로 시뮬레이션할 수 있어 이해하고 유지하기 쉬운 테스트 환경을 제공합니다.

특징 요약

  • JavaScript로 테스트 시나리오 작성
  • 테스트를 코드처럼 관리(Git PR 리뷰·버전 관리 가능)
  • 요청량, 가중치, 세션 흐름 등 실제 사용 패턴 시뮬레이션 가능
  • 운영 환경에서 확인하는 지표(지연 시간, 에러율, 처리량 등)를 테스트 단계에서 확인 가능

2. K6 장점

테스트를 코드로 정의하는 방식이기 때문에 다음과 같은 장점이 있습니다.

장점 설명
명확성 테스트 의도가 코드에 그대로 표현됨
재현성 같은 코드라면 항상 동일한 테스트 결과
수정 용이성 API 스펙 변경 시 코드만 고치면 반영
기록·이력 테스트 변경 이력을 Git으로 추적 가능
확장성 테스트가 많아져도 구조 유지 가능

즉, 테스트를 개발 프로세스 안으로 자연스럽게 가져올 수 있는 도구입니다.

3. 핵심 개념

용어 설명
VU (Virtual User) 동시에 실행되는 가상 사용자
iteration VU가 default()를 1회 수행한 횟수
scenario 부하 패턴 정의 (예: TPS 유지, 단계적 증가 등)
threshold 목표 성능 기준 (예: p95<1500ms, 실패율<1%)
tags 요청 라벨링 (엔드포인트별 통계 분석 목적)

4. 스크립트 구조 예제

import http from 'k6/http';
import { check, sleep } from 'k6';

/**
 * 기본 설정
 *  - BASE_URL       : 요청을 보낼 대상 서버 (환경 변수로 덮어쓰기 가능)
 *  - RATE           : 초당 요청 수(TPS). 미설정 시 100 TPS
 *  - TEST_DURATION  : 테스트 수행 시간. 미설정 시 24h
 */
//const BASE_URL = __ENV.BASE_URL || 'http://10.0.1.8:24002';
const BASE_URL = __ENV.BASE_URL || 'http://127.0.0.1:24002';  // ← 기본값
const RATE = __ENV.RATE ? Number(__ENV.RATE) : 30;
const TEST_DURATION = __ENV.TEST_DURATION || '10m';
//const TEST_DURATION = __ENV.TEST_DURATION || '24h';

// 불규칙 트래픽을 만들기 위한 API 경로와 가중치
// 예: orders 호출이 가장 빈번, shipments는 가장 드묾
const API_WEIGHTS = [
  { path: '/api/spans/orders',   weight: 4 },
  { path: '/api/spans/payments', weight: 4 },
  { path: '/api/spans/shipments', weight: 2 },
];

/**
 * 가중치 기반 랜덤 API 선택
 *  - weight 합을 기준으로 0 ~ totalWeight 사이 난수를 뽑아서
 *    해당 구간에 걸리는 path를 선택
 */
function pickRandomApi() {
  const totalWeight = API_WEIGHTS.reduce((sum, item) => sum + item.weight, 0);
  let rand = Math.random() * totalWeight;

  for (const item of API_WEIGHTS) {
    if (rand < item.weight) {
      return item.path;
    }
    rand -= item.weight;
  }

  // 방어적 코드: 논리상 이 지점은 오지 않아야 하지만,
  // 예외 상황 대비하여 첫 번째 항목을 반환
  return API_WEIGHTS[0].path;
}

/**
 * VU별로 유지되는 traceId
 *  - 각 VU마다 별도 값
 *  - default() 안에서 확률적으로 갱신
 */
let currentTraceId = `${__VU}-${Date.now()}`;

/**
 * k6 옵션
 *  - executor: constant-arrival-rate
 *    → 시나리오 전체 기준으로 RATE 값만큼 초당 iteration 실행 시도
 *  - rate     : 목표 TPS (초당 요청 수)
 *  - timeUnit : rate 기준 단위 (1초)
 *  - duration : 전체 테스트 시간
 *
 *  preAllocatedVUs / maxVUs
 *   - 응답 시간에 따라 필요한 동시 VU 수가 달라질 수 있으므로
 *     여유 있게 설정
 */
export const options = {
  scenarios: {
    span_scenario: {
      executor: 'constant-arrival-rate',
      rate: RATE,               // 목표 TPS (기본 100)
      timeUnit: '1s',
      duration: TEST_DURATION,  // 기본 24h, 필요 시 환경 변수로 조정

      // 초기에 확보할 VU 수
      preAllocatedVUs: 50,
      // 최대 허용 VU 수 (TPS가 안 나오면 이 값을 더 늘려볼 수 있음)
      maxVUs: 200,
    },
  },
  thresholds: {
    // 95퍼센타일 응답 시간 1.5초 미만
    http_req_duration: ['p(95)<1500'],
    // 실패율 1% 미만
    http_req_failed: ['rate<0.01'],
  },
};

export default function () {
  // 30% 확률로 traceId 변경 → 일정 기간 동안 동일 traceId로 묶인 요청 유지
  if (Math.random() < 0.3) {
    currentTraceId = `${__VU}-${Date.now()}`;
  }

  const path = pickRandomApi();

  const res = http.get(`${BASE_URL}${path}`, {
    headers: {
      // 백엔드/APM에서 추적용으로 사용할 커스텀 헤더
      'x-trace-id': currentTraceId,
    },
    tags: {
      // k6 메트릭(예: http_reqs)에 endpoint 태그가 붙도록 설정
      endpoint: path,
    },
  });

  // 엔드포인트별 상태 코드 검증
  check(res, {
    [`${path} status is 200`]: (r) => r.status === 200,
  });

  /**
   * iteration 사이에 약간의 지터 추가 (0 ~ 300ms)
   *
   * executor가 constant-arrival-rate 이기 때문에
   * 전체 TPS는 rate에 의해 제어되고,
   * 이 sleep은 개별 VU들의 타이밍을 살짝 어긋나게 만들어
   * 완전히 규칙적인 패턴을 피하는 정도의 역할만 수행한다.
   */
  sleep(Math.random() * 0.3);
}

5. 실행 방법

기본 실행

k6 run span-load.js

결과 예시

Monitoring/Resources/3b5c399dfbcd54a27713f1a9f0edd147_MD5.jpeg

서버 주소 / TPS / 테스트 시간 조정

k6 run span-load.js \
  -e BASE_URL=http://10.0.1.8:24002 \
  -e RATE=50 \
  -e TEST_DURATION=30m

실행 결과로 확인 가능한 항목

  • p95 / p99 지연 시간
  • 성공/실패 요청 수
  • 초당 처리량(TPS)
  • 응답 시간 분포
  • VU 사용량 변화

6. 테스트 결과 활용

테스트 결과는 다음과 같이 활용할 수 있습니다.

  • 지연이 발생하는 API/엔드포인트 확인
  • TPS 증가 시 어느 시점에서 응답이 느려지는지 확인
  • 에러가 많이 발생하는 상황 파악
  • 병목 구간 추적(백엔드/DB/외부 연동 등)
  • 배포 후 성능 비교

7. 운영 환경에서의 활용 시나리오

상황 사용 예
신규 기능 출시 전 API 처리 시간/에러율 점검
성능 최적화 이후 개선 전후 응답 시간 비교
장애 재현 대량 트래픽 유입 조건에서 오류 발생 패턴 확인
확장성 검증 TPS 상승 시 서비스가 견딜 수 있는 선 파악
스케일링 테스트 부하 증가 시 인프라 반응 확인

8. JMeter vs k6 비교

우리 회사에서는 기존에 Apache JMeter를 사용하고 있습니다. k6는 JMeter를 대체한다기보다, 개발자가 테스트를 유지하고 협업하기 좋은 방식을 추가할 수 있습니다.

항목 JMeter k6
테스트 작성 GUI 기반 설정 JavaScript 기반 코드
협업 파일 공유 중심 코드 리뷰·PR 가능
재현성 설정 누락/버전 차이 발생 가능 코드 그대로 실행되면 동일 결과
유지 보수 테스트 많아지면 복잡해짐 코드 구조로 관리
트래픽 재현 반복 호출 중심 실제 사용 패턴 반영 용이
고부하 테스트 자원 사용량 많음 경량 실행

우리 회사 입장에서의 장점

  • 개발자가 테스트를 직접 버전 관리
  • 테스트 변경 이력이 명확
  • API 변경 시 코드만 수정하면 반영
  • 실제 서비스 흐름 기반 트래픽 시뮬레이션 가능

9. 참고 자료