API가 단일 사용자에게는 잘 동작하지만 트래픽이 몰릴 때 느려지거나 중단된다면 부하 테스트가 필요합니다. k6는 API에 반복 가능한 부하를 걸고, 지연 시간·처리량·오류율을 코드로 검증하기 좋은 도구입니다. 이 글에서는 k6 설치, 첫 스크립트 작성, VU/단계/임계값 설정, 결과 해석, 그리고 API 성능 테스트 루틴과 CI에 통합하는 방법을 다룹니다. 세부 옵션은 공식 k6 문서도 함께 참고하세요.
k6란 무엇인가요?
k6는 Grafana에서 관리하는 오픈 소스 부하 테스트 도구입니다. 테스트는 JavaScript로 작성하고, k6는 Go 기반 엔진으로 이를 실행해 API 엔드포인트에 시뮬레이션된 트래픽을 보냅니다.
개발자는 익숙한 JavaScript로 시나리오를 작성하고, 부하 생성은 컴파일된 Go 엔진이 담당합니다. 덕분에 단일 머신에서도 많은 가상 사용자를 비교적 효율적으로 실행할 수 있습니다.
k6의 역할은 명확합니다.
- 지속적이고 반복 가능한 부하 생성
- 응답 시간, 요청 수, 오류율 측정
- 성능 기준을 임계값으로 정의
- CI에서 성능 회귀 감지
k6는 API 클라이언트, 문서화 도구, 기능 테스트 프레임워크가 아닙니다. 핵심 용도는 부하 테스트 엔진입니다.
자주 사용하는 용어는 다음과 같습니다.
| 용어 | 의미 |
|---|---|
| 가상 사용자, VU | 스크립트를 반복 실행하는 시뮬레이션된 사용자 |
| 반복, Iteration | 테스트 함수가 한 번 완전히 실행되는 단위 |
| 단계, Stage | 시간에 따라 VU 수를 늘리거나 줄이는 부하 구간 |
| 임계값, Threshold |
p(95)<500 같은 합격/불합격 성능 조건 |
| 확인, Check |
status is 200 같은 응답 검증. 실패해도 테스트는 계속 실행됨 |
k6 설치
k6는 단일 바이너리로 제공되므로 설치가 간단합니다.
macOS에서 Homebrew를 사용한다면 다음 명령을 실행합니다.
brew install k6
Windows에서 Chocolatey를 사용한다면 다음과 같습니다.
choco install k6
Debian 또는 Ubuntu에서는 Grafana apt 저장소를 추가한 뒤 설치합니다.
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg \
--keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" \
| sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
설치가 완료되면 버전을 확인합니다.
k6 version
로컬에 설치하지 않으려면 Docker 이미지도 사용할 수 있습니다. 패키지 설치 명령은 시간이 지나면서 바뀔 수 있으므로, 실제 환경에 적용하기 전에는 공식 문서의 설치 페이지를 확인하세요.
첫 번째 k6 스크립트 작성
k6 테스트는 기본 함수를 내보내는 JavaScript 모듈입니다. k6는 각 VU가 반복할 때마다 default function을 실행합니다.
다음은 하나의 API 엔드포인트를 호출하고 응답을 검증하는 최소 예제입니다.
import http from 'k6/http';
import { check, sleep } from 'k6';
export default function () {
const res = http.get('https://test-api.example.com/users');
check(res, {
'status is 200': (r) => r.status === 200,
'body is not empty': (r) => r.body.length > 0,
});
sleep(1);
}
파일을 script.js로 저장한 뒤 실행합니다.
k6 run script.js
기본 실행에서는 1명의 VU가 1번의 반복을 수행합니다.
여기서 sleep(1)은 반복 사이에 1초 대기를 추가합니다. 실제 사용자는 요청을 연속으로 무한히 보내지 않고 작업 사이에 멈추기 때문에, 사용자 행동을 시뮬레이션하려면 sleep()을 넣는 것이 좋습니다.
반대로 순수 처리량 한계를 보고 싶다면 sleep()을 제거해 VU가 가능한 한 빠르게 반복하도록 만들 수 있습니다.
check()는 소프트 어설션입니다. 실패한 확인은 결과 요약에 표시되지만 테스트 자체를 중단하지 않습니다. 부하가 높은 상황에서는 일부 실패가 발생할 수 있으므로, 테스트를 계속 실행해 실패율과 지연 시간이 어떻게 변하는지 측정하는 것이 중요합니다.
VU, 단계, 임계값 설정
단일 요청 테스트 다음 단계는 부하 프로필을 정의하는 것입니다. k6에서는 options 객체로 VU 수, 실행 시간, 단계, 임계값을 설정합니다.
가장 단순한 설정은 고정 VU와 실행 시간입니다.
export const options = {
vus: 50,
duration: '30s',
};
이 설정은 50명의 가상 사용자를 30초 동안 실행합니다.
실제 서비스 트래픽을 더 잘 표현하려면 단계 기반 램핑 프로필을 사용합니다.
export const options = {
stages: [
{ duration: '1m', target: 100 }, // 1분 동안 100 VU까지 증가
{ duration: '3m', target: 100 }, // 3분 동안 100 VU 유지
{ duration: '1m', target: 0 }, // 1분 동안 0 VU까지 감소
],
thresholds: {
http_req_duration: ['p(95)<500'], // 요청의 95%가 500ms 미만
http_req_failed: ['rate<0.01'], // 오류율 1% 미만
},
};
임계값은 CI에서 특히 중요합니다. 임계값이 실패하면 k6는 0이 아닌 종료 코드를 반환합니다. 따라서 GitHub Actions, GitLab CI, Jenkins 같은 파이프라인에서 성능 기준을 넘은 빌드를 실패 처리할 수 있습니다.
즉, 기능 테스트에서 어설션을 코드로 관리하듯이 성능 예산도 코드로 관리할 수 있습니다.
자주 사용하는 부하 테스트 프로필
모든 테스트를 처음부터 구현할 필요는 없습니다. 먼저 Smoke와 Load 테스트부터 시작하고, 기준 수치가 쌓이면 Stress, Spike, Soak 테스트를 추가하는 방식이 현실적입니다.
| 프로필 | 목표 | 확인할 수 있는 것 |
|---|---|---|
| Smoke | 아주 적은 부하로 스크립트 실행 확인 | 테스트 스크립트와 환경이 올바른지 |
| Load | 예상되는 일반 트래픽 적용 | API가 평상시 트래픽에서 안정적인지 |
| Stress | 예상 최대치를 넘는 부하 적용 | 어느 지점부터 문제가 발생하는지 |
| Spike | VU를 갑자기 급증 | 트래픽 급증을 견딜 수 있는지 |
| Soak | 몇 시간 동안 중간 부하 유지 | 메모리 누수, 점진적 성능 저하 |
부하 테스트 접근 방식과 메트릭을 더 넓게 정리하려면 성능 테스트의 기본 원리를 참고할 수 있습니다.
k6 결과 읽기
실행이 끝나면 k6는 터미널에 요약 결과를 출력합니다. 우선 다음 지표를 확인하세요.
| 메트릭 | 의미 |
|---|---|
http_req_duration |
전체 요청 시간. 평균, 최소, 최대, 중앙값, p90, p95 등으로 표시 |
http_req_failed |
실패한 요청 비율 |
http_reqs |
총 요청 수와 초당 요청 수. 처리량을 의미 |
iterations |
완료된 전체 반복 횟수와 비율 |
vus, vus_max
|
현재 및 최대 가상 사용자 수 |
checks |
check() 어설션 통과율 |
특히 평균보다 백분위수를 우선해서 보세요.
예를 들어 평균 응답 시간이 200ms라도 p99가 4초라면, 100명 중 1명은 4초를 기다린다는 의미입니다. 사용자 경험 문제는 평균보다 이런 꼬리 지연 시간에서 자주 발생합니다.
테스트 결과를 볼 때는 다음 순서로 확인하는 것이 좋습니다.
-
http_req_failed가 임계값 안에 있는지 확인 -
http_req_duration의 p95 또는 p99가 성능 목표를 넘는지 확인 - VU 증가 구간에서 지연 시간이 급격히 증가하는 지점 확인
-
http_reqs로 처리량이 기대치에 도달했는지 확인 -
checks실패율로 응답 자체가 깨졌는지 확인
터미널 요약 외에도 k6는 JSON 또는 CSV로 결과를 내보낼 수 있고, Grafana 대시보드 및 Prometheus와도 통합할 수 있습니다. 일회성 테스트에는 터미널 요약으로 충분하지만, 지속적인 성능 관찰이 필요하다면 메트릭을 시각화 가능한 저장소로 보내는 구성이 더 적합합니다.
CI에서 k6 실행하기
k6는 임계값 실패 시 0이 아닌 종료 코드를 반환하므로 CI/CD에 넣기 쉽습니다.
예를 들어 파이프라인에서는 다음처럼 실행할 수 있습니다.
k6 run script.js
스테이징 환경 URL을 환경 변수로 넘기고 싶다면 스크립트를 다음처럼 작성할 수 있습니다.
import http from 'k6/http';
import { check, sleep } from 'k6';
const BASE_URL = __ENV.BASE_URL || 'https://test-api.example.com';
export const options = {
stages: [
{ duration: '1m', target: 50 },
{ duration: '2m', target: 50 },
{ duration: '1m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const res = http.get(`${BASE_URL}/users`);
check(res, {
'status is 200': (r) => r.status === 200,
});
sleep(1);
}
실행 시 환경 변수를 지정합니다.
BASE_URL=https://staging.example.com k6 run script.js
CI에서는 일반적으로 다음 기준을 둡니다.
- 모든 커밋: 기능 테스트와 계약 테스트 실행
- 주요 PR 또는 릴리스 전: k6 부하 테스트 실행
- 야간 또는 예약 작업: Stress, Soak 테스트 실행
- 임계값 실패: 빌드 실패 또는 알림 발송
k6가 적합한 곳, Apidog가 적합한 곳
k6는 “트래픽이 많을 때 API가 얼마나 빠르고 안정적인가?”에 답합니다.
반면 다음 질문은 k6의 핵심 목적이 아닙니다.
- API가 올바른 데이터를 반환하는가?
- 응답이 계약과 일치하는가?
- 인증, 권한, 상태 코드가 올바른가?
- 모든 엔드포인트가 기능적으로 깨지지 않았는가?
이 영역은 기능 테스트와 계약 테스트의 영역입니다. 따라서 파이프라인에서는 k6와 API 테스트 도구를 함께 사용하는 것이 좋습니다.
| 관심사 | 적합한 도구 | 답변하는 질문 |
|---|---|---|
| 지속적인 고부하, 대규모 백분위수 | k6 | 트래픽이 많을 때도 빠른가 |
| 기능적 정확성, 계약, 인증 | Apidog | 올바른 것을 반환하는가 |
| 모든 커밋에 대한 CI 회귀 테스트 | Apidog (apidog run) |
이 변경이 엔드포인트를 깨뜨렸는가 |
| CI의 성능 예산 | k6 임계값 | 지연 시간 또는 오류율이 기준을 넘었는가 |
Apidog는 정확성 측면을 처리합니다. API를 설계하거나 가져오고, 시각적 어설션으로 테스트 시나리오를 만들고, k6 스크립트를 실행하듯 apidog run으로 CI에서 실행할 수 있습니다.
Apidog CLI 가이드는 기능 테스트를 파이프라인에 연결하는 방법을 설명합니다. 또한 Apidog는 빠른 확인을 위한 경량 성능 테스트 기능도 제공하며, 자세한 내용은 Apidog의 API 성능 테스트에서 확인할 수 있습니다. 다만 k6 수준의 전용 부하 생성기 역할을 목표로 하지는 않습니다.
실제 워크플로는 다음처럼 구성할 수 있습니다.
- 모든 커밋에서 Apidog로 기능 및 계약 테스트 실행
- 릴리스 전 또는 예약 작업에서 k6로 스테이징 환경 부하 테스트 실행
- Apidog 테스트 실패 시 정확성 문제로 처리
- k6 임계값 실패 시 성능 회귀로 처리
- 두 결과를 모두 CI 상태 또는 알림으로 연결
부하 테스트 도구를 비교 중이라면 k6는 JMeter, Gatling, Locust와 같은 범주에 있습니다. 도구 선택 기준은 부하 테스트 도구 총정리와 Locust 대안 비교를 참고하세요.
자주 묻는 질문
k6는 무료인가요?
네. k6는 AGPL 라이선스의 오픈 소스 도구이며, 로컬에서는 사용자의 하드웨어 용량 범위 내에서 무료로 실행할 수 있습니다.
Grafana는 대규모 분산 테스트 실행과 결과 저장을 위한 유료 서비스인 k6 Cloud도 제공하지만, 필수는 아닙니다. 대부분의 팀은 먼저 로컬 k6와 CI 실행만으로도 충분히 시작할 수 있습니다.
다른 무료 옵션도 비교하고 싶다면 부하 테스트 도구 개요를 확인하세요.
k6를 사용하려면 JavaScript를 깊게 알아야 하나요?
깊은 전문 지식은 필요하지 않습니다. 대부분의 k6 스크립트는 다음 요소로 구성됩니다.
default function-
http.get()또는http.post() check()sleep()options
이 글의 예제를 이해할 수 있다면 기본적인 부하 테스트 스크립트는 작성할 수 있습니다. 별도의 빌드 단계나 복잡한 프레임워크 학습도 필요하지 않습니다.
성능 테스트에서 k6와 Apidog의 차이점은 무엇인가요?
k6는 지속적인 부하를 발생시키고 지연 시간 백분위수, 처리량, 오류율을 측정하기 위한 전용 부하 생성기입니다.
Apidog는 API 설계, 기능 테스트, 계약 테스트에 중점을 둔 API 플랫폼이며, 빠른 확인을 위한 경량 성능 테스트 기능도 제공합니다.
정리하면 다음과 같습니다.
- 실제 부하, VU, 단계, p95/p99, CI 성능 예산이 필요하면 k6
- 정확성, 계약 검증, 인증 흐름, 모든 커밋의 기능 테스트가 필요하면 Apidog
두 도구는 경쟁 관계라기보다 서로 다른 문제를 해결합니다.
CI/CD에서 k6를 실행할 수 있나요?
네. k6는 CI/CD에 잘 맞습니다. 임계값이 실패하면 0이 아닌 종료 코드를 반환하므로, 성능 회귀가 발생했을 때 빌드를 실패 처리할 수 있습니다.
기본 흐름은 다음과 같습니다.
k6 run script.js
실무에서는 스테이징 환경을 대상으로 실행하고, p95 지연 시간과 오류율에 대한 임계값을 설정합니다. apidog run 기반 기능 테스트와 함께 사용하면 각 변경 사항에 대해 정확성과 성능을 모두 검증할 수 있습니다.
결론
k6는 API에 실제 부하를 걸고 결과를 코드로 검증하는 간단하고 실용적인 방법을 제공합니다.
기본 흐름은 다음과 같습니다.
- k6 설치
- JavaScript 테스트 스크립트 작성
- VU와 단계 설정
- 임계값으로 성능 예산 정의
- p95, p99, 오류율, 처리량 확인
- CI에서 성능 회귀 감지
부하 테스트와 기능 테스트는 서로 다른 질문에 답합니다. k6로 성능을 검증하고, Apidog로 정확성과 계약을 검증하는 식으로 역할을 분리하면 API 품질을 더 안정적으로 관리할 수 있습니다.
정확성 측면에서는 Apidog를 사용해 API를 설계, 테스트, 모의, 문서화하고, apidog run으로 CI에서 기능 테스트를 실행할 수 있습니다. k6 부하 테스트와 함께 사용하면 계약 수준의 신뢰도와 성능 기준을 모두 파이프라인에 포함할 수 있습니다.

Top comments (0)