서비스 출시 전이나 이벤트 오픈을 앞두고 "갑자기 사용자 수천 명이 동시 접속하면 우리 서버가 버틸 수 있을까?" 고민해 본 적이 있을 것이다.
다만 실제 트래픽 환경을 사전에 시뮬레이션하지 않고 그저 서버 스펙만 올렸다가, 예상치 못한 병목 현상이나 DB 커넥션 고갈로 서비스가 마비되는 상항을 맞닥뜨리는 경우가 많다.
이번에는 Go 기반의 가볍고 강력한 오픈소스 부하 테스트 도구인 **k6**가 정확히 무엇이고, 왜 사용해야 하며, 어떻게 실제 서비스 검증에 적용하는지 완벽하게 정리해서 소개하겠다.

 

1단계: 부하 테스트와 k6 이해하기

부하 테스트(Load Testing)는 시스템이 예상되는 사용자 요청(트래픽)을 한계 상황까지 잘 처리해낼 수 있는지 미리 검증하는 과정이다.
과거에는 JMeter나 ApacheBench(ab) 같은 도구를 많이 사용했지만, XML 기반의 복잡한 설정이나 높은 메모리 사용량 때문에 실무에서 운영하기 부담스러운 경우가 많았다.

k6는 이러한 단점을 보완하여 개발 친화적인 JavaScript 스크립트로 테스트 시나리오를 작성할 수 있고, 가벼운 실행 구조 덕분에 적은 자원으로도 고성능 부하를 생성할 수 있는 현대적인 도구다.

구분 JMeter ApacheBench (ab) k6
설정 방식 GUI 기반 / XML 설정 단순 CLI 명령어 JavaScript 코드 기반
자원 소모량 높음 (Java JVM 기반) 매우 낮음 낮음 (Go 엔진 기반)
시나리오 유연성 보통 (플러그인 필요) 단순 (단일 URL 반복) 매우 높음 (복잡한 흐름 가능)
CI/CD 연동 가능하나 설정 복잡 어려움 매우 용이 (CLI 및 JSON 출력)

 

2단계: k6 설치 및 기초 사용법

k6는 독립 실행형 바이너리로 제공되므로 OS별 패키지 매니저를 통해 매우 간단하게 설치할 수 있다.

OS별 설치 명령어

운영체제 환경에 맞춰 Terminal이나 PowerShell에서 아래 명령어를 실행하자.

# macOS (Homebrew)
brew install k6

# Linux (Debian/Ubuntu)
gpg --k6-keyring /usr/share/keyrings/k6-archive-keyring.gpg
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

# Windows (Chocolatey)
choco install k6

 

3단계: 실전 부하 테스트 예제

단순히 특정 URL을 때리는 것만으로는 실제 사용자 행동을 모사하기 어렵다.
점진적으로 가상 사용자(VU, Virtual Users)를 늘려가며 서버의 한계를 측정하는 스테이지(Stages) 설정을 활용해야 한다.

✗ 잘못된 코드 (단순 반복 부하로 실제 사용자 패턴 미반영)

단순히 일정 사용자 수로 무한 요청만 보낼 경우, 서비스의 급증 트래픽 스파이크나 복구력을 측정할 수 없다.

import http from 'k6/http';

export const options = {
  vus: 100, // 가상 사용자 100명
  duration: '30s', // 30초 동안 고정
};

export default function () {
  http.get('https://api.example.com/products');
}

✓ 올바른 코드 (단계별 사용자 증가 및 응답 검증 포함)

사용자를 10명에서 100명까지 늘렸다가 줄이는 계단식 단계(Ramp-up/Ramp-down)와 함께 응답 상태 코드 및 응답 시간 검증(Thresholds/Checks)을 포함한 코드다.

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

export const options = {
  // 트래픽 변화 시나리오 설정
  stages: [
    { duration: '10s', target: 20 },  // 10초 동안 VU를 20명까지 부드럽게 증가
    { duration: '30s', target: 100 }, // 30초 동안 100명 유지하며 부하 유지
    { duration: '10s', target: 0 },   // 10초 동안 0명으로 종료 (Ramp-down)
  ],
  // 품질 목표 (성능 기준선) 설정
  thresholds: {
    http_req_duration: ['p(95)<500'], // 상위 95% 요청은 500ms 이내에 응답해야 성공
    http_req_failed: ['rate<0.01'],   // 에러율이 1% 미만이어야 성공
  },
};

export default function () {
  const res = http.get('https://api.example.com/products');

  // 응답 검증 (Check)
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.json() !== null,
  });

  // 실제 사용자처럼 부하 사이에 일정 대기시간(Think time) 부여
  sleep(1);
}

터미널 실행 명령 및 결과 출력:

k6 run test-script.js
execution: local
   script: test-script.js
   output: -

  scenarios: (100.00%) 1 scenario, 100 max VUs, 50s max duration

     ✓ status is 200
     ✓ response time < 500ms

   ✓ http_req_duration..............: avg=120ms min=45ms med=98ms max=480ms p(95)=340ms
   ✓ http_req_failed................: 0.00%  ✓ 0        ✗ 3420
   ✓ http_reqs......................: 3420   68.4/s

 

4단계: 부하 테스트 시 흔한 실수와 주의사항

부하 테스트 도구를 다룰 때 의외로 많은 개발자들이 오해하거나 실수하는 부분이 있다.

  • 실제 운영 DB에 테스트 진행: 테스트 수행 시 대량의 더미 데이터가 쓰이거나 서버가 다운될 수 있으므로 반드시 Staging 환경에서 실시하자.
  • sleep() 생략: `sleep()` 없이 무한 루프 형태로 요청을 보내면 실제 사용자 트래픽 패턴이 아닌 DDOS 공격 형태가 되어 지표가 오염된다.
  • 클라이언트 하드웨어 한계 고려 미흡: 부하를 주는 내 로컬 PC의 CPU나 네트워크 대역폭이 먼저 100%에 도달하면 서버 성능이 나쁜 것으로 잘못 측정될 수 있다.

 

5단계: 정리 및 다음 단계

k6 부하 테스트는 단순한 백엔드 수치 검증을 넘어, 작성한 애플리케이션의 아키텍처 한계와 DB 쿼리 병목 지점을 미리 발견할 수 있는 가장 효과적인 방어선이다.
작은 최적화와 정교한 성능 검증 습관이 모여서 장애 없는 안정적인 서비스를 만든다는 점을 잊지 말자.
이 글의 **3단계 계단식 부하 설정 코드**를 참고해 현재 운영/개발 중인 API 서비스에 k6 테스트를 직접 적용해 보면, 예상되는 최대 트래픽 수치를 정확히 파악하는 최적의 결과를 얻을 수 있을 것이다.