이 글에서는 터미널에서 서버 성능 테스트를 아주 간편하게 할 수 있는 방안을 소개한다.
작성자는 "k6 부하테스트"라는 단어를 처음 보고 나서 5분 내에 실제 부하테스트를 완료하고, 결과를 도출했다.
Grafana k6
k6는 Grafana Labs에서 운영하는 오픈소스 성능테스트 도구이다. 2017년에 처음 출시했고, 그라파나에서 지속적으로 업데이트 중인 프로젝트이다. 빠르게 적용해본 소감으로는 너무 쉽고 편하다.
https://grafana.com/products/cloud/k6
Grafana Cloud k6 | Performance testing tool
Minimum commit of $25,000 per year. Premium supportObservability ArchitectDeployment flexibility (Public Cloud, Federal Cloud, or Bring Your Own Cloud)Scale up to 1 million concurrent virtual users
grafana.com
GitHub - grafana/k6: A modern load testing tool, using Go and JavaScript
A modern load testing tool, using Go and JavaScript - grafana/k6
github.com
서버 성능 테스트 도구
JMeter
Java 기반의 가장 오래된 오픈소스 도구로, GUI를 통해 복잡한 테스트 시나리오를 만들 수 있다. 하지만 XML 기반의 설정 파일이 매우 무겁고, 메모리 점유율이 높아 고사양의 리소스가 필요하다.
nGrinder
네이버에서 개발한 도구로, Jython(Python)이나 Groovy 스크립트로 테스트를 짤 수 있고 대규모 분산 테스트에 강력하다. 다만, 컨트롤러와 에이전트라는 별도의 서버 환경을 구축해야 해서 시작하는 데 공수가 많이 든다.
Grafana k6
Grafana Labs에서 개발한 오픈소스 성능 테스트 도구로, javascript로 테스트 시나리오를 작성하고, CLI 환경에서 실행한다. Java 설치나 복잡한 설정 등이 전혀 필요없고, 명령어 하나로 설치 후 javascript 파일 하나로 모든 테스트를 완료할 수 있다.
- 간편한 설치: macOS 기준 brew install 하나로 모든 준비가 끝난다.
- 리소스 효율성: Go 언어로 작성된 엔진 덕분에, 단일 머신에서도 적은 메모리로 수천명의 가상 사용자를 시뮬레이션 할 수 있다.
- 학습 곡선: javascript로 작성하므로, 친숙한 언어로 빠르게 학습하여 적용할 수 있다.
- 가벼운 환경: 정리하자면 설치 - 코드 작성 - 실행 까지의 모든 과정이 쉽고 가볍다.
실습
1. k6 설치
mac 기준 homebrew 명령어 한줄로 즉시 설치할 수 있다. 별도의 설치파일이나 환경을 구축하지 않아도 된다.
brew install k6
2. 테스트 스크립트 작성
option에 테스트 환경을 설정하고,
export default function() 함수 내에 테스트 시나리오를 작성한다.
아래 코드의 경우 100명의 사용자가 10초동안 초당 1번 간격으로 요청한다.
import http from 'k6/http';
import { sleep, check } from 'k6';
// 1. 테스트 설정
export const options = {
vus: 100, // 가상 사용자 100명
duration: '10s', // 10초 동안 실행
};
// 2. 실제 테스트 시나리오
export default function () {
const res = http.get('http://localhost:8080/api/menus?service=PUBLIC'); // 테스트 대상 URL
// 응답이 200인지 확인
check(res, { 'status is 200': (r) => r.status === 200 });
sleep(1); // 사용자 행동 간격 (1초 대기)
}
3. 실행
k6 run test.js
실행 결과
~ k6 run test.js
/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/
execution: local
script: test.js
output: -
scenarios: (100.00%) 1 scenario, 100 max VUs, 40s max duration (incl. graceful stop):
* default: 100 looping VUs for 10s (gracefulStop: 30s)
█ TOTAL RESULTS
checks_total.......: 1000 95.346509/s
checks_succeeded...: 100.00% 1000 out of 1000
checks_failed......: 0.00% 0 out of 1000
✓ status is 200
HTTP
http_req_duration..............: avg=29.17ms min=6.01ms med=17.97ms max=175.1ms p(90)=65.66ms p(95)=119.94ms
{ expected_response:true }...: avg=29.17ms min=6.01ms med=17.97ms max=175.1ms p(90)=65.66ms p(95)=119.94ms
http_req_failed................: 0.00% 0 out of 1000
http_reqs......................: 1000 95.346509/s
EXECUTION
iteration_duration.............: avg=1.02s min=1s med=1.01s max=1.17s p(90)=1.06s p(95)=1.12s
iterations.....................: 1000 95.346509/s
vus............................: 100 min=100 max=100
vus_max........................: 100 min=100 max=100
NETWORK
data_received..................: 17 MB 1.6 MB/s
data_sent......................: 94 kB 9.0 kB/s
running (10.5s), 000/100 VUs, 1000 complete and 0 interrupted iterations
default ✓ [======================================] 100 VUs 10s
4. 성능 지표 분석
위의 결과 로그를 보면 아래 정보를 알 수 있다.
응답속도 관련
- avg=29.17ms - 평균 응답속도는 29.17ms이다.
- min=6.01ms - 가장 빨랐던 응답 속도는 6.01ms이다.
- med=17.97ms - 절반 이상의 요청이 17.97ms내에 처리되었다.
- p(95)=119.94ms - 하위 5%의 요청은 119.94ms 가 소요되었다.
- max=175.1ms - 가장 느렸던 요청은 175.1ms이다.
처리량 관련
- check_succeeded=100.00% - 모든 응답이 성공했다.
- http_reqs=1000 95.346509/s - 초당 약 95번의 요청을 처리했다.
- iterations=1000 - 전체 1000번의 테스트 루프가 돌았다.
네트워크 관련
- data_received=1.7MB/s - 초당 약 1.7MB의 데이터를 서버로부터 내려받았다.
- 전체 수신량=17MB - 응답 데이터가 한번에 약 17KB 정도 되고, 총 17MB받았다.
활용 방안
비교 분석
어떤 기술을 도입하거나 코드, 쿼리를 개선했을 때 응답속도, 부하 차이를 측정할 수 있다.
아래 2가지 예시에서는 동일하게 100명이 10초동안 0.5초간격으로 요청보내는걸 가정했다.
테스트 스크립트
import http from 'k6/http';
import { sleep, check } from 'k6';
export const options = {
vus: 100, // 가상 사용자 10명
duration: '10s', // 10초 동안 실행
};
// 2. 실제 테스트 시나리오
export default function () {
const res = http.get('http://localhost:8080/api/menus?service=PUBLIC'); // 테스트 대상 URL
// 응답이 200인지 확인
check(res, { 'status is 200': (r) => r.status === 200 });
sleep(0.5); // 사용자 행동 간격 (1초 대기)
}
1. 캐싱 미적용 시
평균 응답 속도 = 110.66ms

2. Redis 캐싱 적용 시
평균 응답 속도 = 21.02ms

부하테스트 결과
Redis 캐싱 처리 유무에 따른 차이를 비교하면 아래와 같다.
- 평균 응답속도는 110ms에서 21ms로 약 5배 빨라졌다.
- 하위 95%로 느린 응답은 747ms에서 37ms로 20배 빨라졌다.
-> 캐싱을 적용해서 모든 유저가 안정적으로 37ms 내로 응답을 받았다. - 캐싱 전에는 10초간 2000개의 요청 중 1685개의 요청만 처리할 수 있었지만, 캐싱 처리 후에는 2000개의 응답을 모두 처리했다.
요청 모니터링 대시보드
실행 시 옵션을 주면 브라우저에서 실시간 차트를 볼 수 있다.
다만, 실행중에만 볼 수 있다. InfluxDB를 이용해서 테스트 결과를 DB 에 저장하고, 영구적으로 저장할 수 있다.
하지만 간편하게 보고싶을땐 이정도도 괜찮을 것 같다.
(실험을 위해 duration을 600s으로 늘려줬다.)
K6_WEB_DASHBOARD=true k6 run test.js
아래와 같이 web url이 뜨고, 브라우저에서 접근해서 대시보드를 볼 수 있다.

Overview

시간대별

요약

리포트 출력
우측 상단의 REPORT 버튼 클릭 시 아래와 같은 html 정적 파일로 내려준다.
[접은글] 리포트 보기
'Infra & DevOps' 카테고리의 다른 글
| [Docker] 도커 볼륨 설정법 및 개념 정리 Docker Volume (0) | 2025.12.22 |
|---|---|
| [Docker] 도커 기본 명령어 정리 / docker CLI commands (0) | 2025.12.12 |
| [Docker] 도커 시작하기 / Docker 컨테이너 올리기 (2) | 2025.12.10 |
| [Network] Telnet으로 네트워크 연결 확인하는 방법 (0) | 2025.10.14 |
| [Kafka] 카프카 알아보기 (0) | 2025.09.26 |

