Artillery 시나리오 설계
7. 동적 데이터와 인증 처리
JWT 토큰 추출
로그인 응답이 다음과 같다고 가정한다.
{
"accessToken": "eyJ..."
}
Artillery에서는 capture로 토큰을 변수에 저장하고 이후 요청에서 사용할 수 있다.
- post:
url: "/api/auth/login"
json:
email: "{{ email }}"
password: "{{ password }}"
capture:
- json: "$.accessToken"
as: "token"
- get:
url: "/api/me"
headers:
authorization: "Bearer {{ token }}"
환경 변수 사용
config:
target: "{{ $processEnvironment.TARGET_URL }}"
TARGET_URL=https://staging.example.com npx artillery run tests/performance/api.yml
processor.js 예시
Artillery는 config.processor로 JS 파일을 로드해 hook이나 사용자 함수를 실행할 수 있다.
const crypto = require("crypto");
function setRandomUser(context, events, done) {
const id = crypto.randomUUID();
context.vars.email = `loadtest+${id}@example.com`;
context.vars.password = process.env.LOAD_TEST_PASSWORD || "password1234";
context.vars.itemTitle = `item-${id}`;
return done();
}
function addRequestId(requestParams, context, events, done) {
requestParams.headers = requestParams.headers || {};
requestParams.headers["x-request-id"] = crypto.randomUUID();
return done();
}
module.exports = {
setRandomUser,
addRequestId,
};
YAML에서 사용:
config:
processor: "./processor.js"
scenarios:
- beforeScenario: "setRandomUser"
flow:
- get:
url: "/api/items"
beforeRequest: "addRequestId"
CSV payload 사용
계정이 미리 준비되어 있다면 CSV를 사용한다.
email,password
user1@example.com,password
user2@example.com,password
config:
payload:
path: "./payloads/users.csv"
fields:
- "email"
- "password"
8. 테스트 강도 설계
arrivalRate와 동시 사용자 수
arrivalRate는 초당 새로 시작되는 가상 사용자 수다. 동시 사용자 수 자체가 아니다.
동시 사용자 수는 대략 다음 요인으로 결정된다.
동시 실행 VU ~= arrivalRate * 시나리오 평균 수행 시간
예를 들어 arrivalRate: 50이고 한 시나리오가 평균 4초 걸리면 동시에 약 200명의 VU가 실행될 수 있다.
maxVusers
서버가 느려져 시나리오가 오래 걸리면 동시 VU가 계속 증가할 수 있다. 이때 maxVusers로 상한을 둘 수 있다.
config:
phases:
- duration: 300
arrivalRate: 50
maxVusers: 300
단계별 설계 예시
phases:
- name: "warmup"
duration: 120
arrivalRate: 5
rampTo: 20
- name: "target-load"
duration: 600
arrivalRate: 50
- name: "stress-ramp"
duration: 600
arrivalRate: 50
rampTo: 200
- name: "recovery"
duration: 300
arrivalRate: 20
권장 순서:
- 로컬에서 작은 부하로 스크립트 검증
- staging에서 목표 부하의 10~20%로 검증
- 목표 부하 load test
- 목표 부하 초과 stress test
- 1~4시간 이상 soak test
9. 성능 지표 해석
Artillery 결과에서 주로 볼 지표:
| 지표 | 의미 | 해석 기준 |
|---|---|---|
| RPS | 초당 처리 요청 수 | 목표 트래픽을 안정적으로 처리하는지 확인 |
| latency median | 일반적인 응답 시간 | 평균 사용자 경험에 가까움 |
| latency p95 | 느린 5% 요청의 경계 | SLO 기준으로 많이 사용 |
| latency p99 | 매우 느린 요청의 경계 | tail latency와 장애 징후 확인 |
| error rate | 실패 요청 비율 | 4xx/5xx, timeout, connection error 포함 여부 확인 |
| HTTP status codes | 상태 코드 분포 | 429, 500, 502, 503 증가 여부 확인 |
| timeout | 응답 제한 시간 초과 | 서버 포화, 네트워크, LB 제한 가능성 |
해석할 때는 Artillery 결과만 보지 말고 서버 지표와 함께 본다.
- latency p95 상승 + CPU 90% 이상: CPU 병목 가능성
- latency 상승 + DB/외부 API 지표 악화: 앱 외부 의존성 가능성
- RPS 정체 + 5xx 증가: 서버 포화 또는 connection pool 고갈 가능성
- 429 증가: rate limit 또는 upstream 보호 정책 작동
- 메모리 지속 증가: leak 또는 GC pressure 가능성
10. WebSocket 테스트
Artillery의 WebSocket 엔진은 engine: ws를 사용한다.
config:
target: "ws://localhost:3000"
phases:
- duration: 120
arrivalRate: 20
scenarios:
- name: "websocket echo"
engine: ws
flow:
- connect: "{{ target }}/ws"
- send: '{"type":"ping"}'
- think: 1
- send: '{"type":"message","body":"hello"}'
인증 토큰을 쿼리 스트링으로 전달하는 경우:
config:
target: "ws://localhost:3000"
variables:
token: "test-token"
scenarios:
- engine: ws
flow:
- connect: "{{ target }}/ws?token={{ token }}"
- send: '{"type":"ping"}'
11. Socket.IO 테스트
Socket.IO는 engine: socketio를 사용한다.
config:
target: "http://localhost:3000"
phases:
- duration: 120
arrivalRate: 20
socketio:
transports:
- "websocket"
scenarios:
- name: "socket.io chat"
engine: socketio
flow:
- emit:
- "join"
- roomId: "load-test"
- think: 1
- emit:
- "message"
- roomId: "load-test"
text: "hello from artillery"
주의:
- Socket.IO 서버 버전과 Artillery 엔진 호환성을 확인한다.
- 브라우저와 달리 쿠키, 헤더, transport 설정이 자동으로 같지 않다.
- 인증이 handshake query/header에 묶여 있으면 실제 서버 인증 방식과 동일하게 구성해야 한다.