본문으로 건너뛰기
고급약 35분

워크로드 모델링 — 실사용 로그에서 시나리오 비율과 도착률을 뽑아 부하의 모양을 설계한다

부하 테스트가 실제 장애를 못 잡는 가장 흔한 이유는 도구가 아니라 부하가 실제와 다르기 때문이다. 운영 액세스 로그에서 시나리오 비율·시간대별 도착률·think time을 집계해 워크로드 모델을 만들고, 닫힌 모델과 열린 모델의 차이를 이해하며, 정상·피크·스파이크·내구 네 가지 부하 모양을 k6 시나리오로 구현하는 법을 익힌다.

  • #워크로드모델링
  • #도착률
  • #부하모양
  • #k6
  • #성능테스트

개념

도구가 아니라 부하가 틀린다

모듈 16 레슨 3에서 k6 스크립트를 처음 썼을 때, 대개 이런 모양이었다.

export const options = { vus: 50, duration: '30s' };
export default function () {
  http.get('https://test.k6.io/');
  sleep(1);
}

이 스크립트는 동작하는 부하 테스트 지만, 이것으로 "우리 서비스가 다음 주 프로모션을 견딘다"를 말할 수는 없다. 이유는 세 가지다.

  1. 실제 사용자는 한 가지 요청만 반복하지 않는다. 조회·검색·장바구니·결제가 섞여 있고, 그 비율은 시간대마다 다르다.
  2. 실제 사용자 수는 30초 동안 50명으로 고정되지 않는다. 늘어나고, 튀고, 오래 유지된다.
  3. 실제 사용자는 응답이 느려져도 기다려 주지 않는다 — 새 사용자는 앞 사용자의 응답과 무관하게 계속 들어온다.

워크로드 모델(workload model) 은 이 세 가지를 문서로 고정한 것이다. 성능 테스트의 첫 산출물은 스크립트가 아니라 이 한 장이다. 스크립트는 이 문서를 도구 언어로 옮긴 결과일 뿐이다.

워크로드 모델의 네 가지 구성요소

구성요소답해야 할 질문근거 데이터
사용자 유형누가 들어오는가 (신규·기존·봇·내부 배치)로그인 로그, User-Agent, 세션 분석
트랜잭션 믹스어떤 시나리오가 몇 퍼센트인가액세스 로그 경로별 집계
도착률·동시성단위 시간당 몇 건이 들어오는가시간대별 요청 수, 동시 세션 수
부하 모양시간에 따라 어떻게 변하는가일·주·이벤트 단위 트래픽 그래프

여기에 데이터 프로파일 이 하나 더 붙는다. 같은 GET /orders/{id} 라도 주문 1건짜리 계정과 3,000건짜리 계정은 다른 쿼리다. 이건 뒤에서 따로 다룬다.

로그에서 트랜잭션 믹스를 뽑는다

가장 먼저 할 일은 추측을 지우는 것이다. 기획자·개발자에게 "어떤 기능을 많이 쓰나요"라고 묻지 말고 로그를 센다. nginx 액세스 로그가 기본 포맷이라면 이 정도로 충분하다.

# 어제 피크 1시간(19:00~19:59)의 경로별 요청 수 상위 15개
awk '$4 ~ /24\/Sep\/2026:19:/ {print $6, $7}' access.log \
  | sed -E 's/\?.*//; s#/orders/[0-9]+#/orders/{id}#; s#/products/[0-9]+#/products/{id}#' \
  | sort | uniq -c | sort -rn | head -15

sed 로 경로의 숫자 ID를 {id} 로 치환하는 단계가 핵심이다. 이걸 빼면 /orders/10231 이 1건씩 수천 줄로 흩어져서 비율이 보이지 않는다. 결과는 이런 표가 된다.

  48210 GET /api/products
  13904 GET /api/products/{id}
  11022 GET /api/search
   6180 POST /api/cart/items
   2431 GET /api/cart
   1205 POST /api/orders
    980 GET /api/orders/{id}
    412 POST /api/auth/login

여기서 곧바로 퍼센트를 계산하면 안 된다. 요청(request) 비율과 시나리오(business transaction) 비율은 다르다. 사용자가 주문을 한 번 완료하려면 장바구니 조회 → 결제 페이지 → 주문 생성 → 주문 상세 확인이 함께 일어난다. 요청 단위로 세면 POST /api/orders 는 1.4%지만, 시나리오 단위로는 "주문 완료 흐름"이 전체의 5%일 수 있다.

그래서 요청을 시나리오로 묶은 뒤 각 시나리오의 시작 요청(entry point) 건수로 센다.

시나리오시작 요청피크 1시간 건수비율
상품 탐색GET /api/products48,21071.6%
검색GET /api/search11,02216.4%
장바구니 담기POST /api/cart/items6,1809.2%
주문 완료POST /api/orders1,2051.8%
로그인POST /api/auth/login4120.6%
합계67,029100%

이 표가 워크로드 모델의 절반이다. 대략 7 : 2 : 1 (탐색 : 검색 : 쓰기) 이라는 모양이 잡히면 스크립트의 시나리오 가중치가 정해진다.

도착률(arrival rate)을 시간대별로 본다

비율 다음은 절대량이다. 시간대별 요청 수를 뽑아 평균과 피크의 비율 을 본다.

# 하루치 시간대별 요청 수
awk -F'[:[]' '{print $3":00"}' access.log | sort | uniq -c
   1820 00:00      12410 12:00      31204 18:00
   1104 01:00      10880 13:00      67029 19:00   ← 피크
    902 02:00       9950 14:00      52180 20:00
   ...             11200 15:00      33900 21:00
   8930 09:00      13800 16:00      18420 22:00
  11450 10:00      19870 17:00       6210 23:00

여기서 두 숫자를 계산한다.

  • 피크 도착률 = 67,029 ÷ 3,600 = 초당 18.6건
  • 피크 대 평균 비율(peak-to-average ratio) = 67,029 ÷ (일 총합 ÷ 24). 일 총합이 393,000이라면 평균은 16,375, 비율은 4.1배

피크 대 평균 비율은 용량 산정(레슨 2)에서 그대로 쓰인다. 그리고 한 단계 더 짧은 창으로도 본다. 1시간 평균은 1분 피크를 숨긴다. 푸시 알림을 19:30에 보냈다면 19:30~19:31의 도착률이 초당 120건일 수 있다. 1분 단위 집계를 반드시 함께 본다.

awk -F'[:[]' '{print $3":"$4}' access.log | sort | uniq -c | sort -rn | head -5

닫힌 모델과 열린 모델 — 이 선택이 결과를 바꾼다

여기가 이 레슨에서 가장 중요한 대목이다. 부하 생성기는 두 가지 방식 중 하나로 동작한다.

닫힌 모델(closed)열린 모델(open)
조절 대상동시 사용자 수(VU)초당 도착 건수(arrival rate)
다음 요청 시점이전 응답을 받은 뒤 + think time응답과 무관하게 일정 비율로
서버가 느려지면부하가 자동으로 줄어든다부하가 그대로 쌓인다
대응하는 현실콜센터 상담원 N명, 내부 업무 시스템공개 웹/API, 푸시 알림 직후
k6 실행기constant-vus, ramping-vusconstant-arrival-rate, ramping-arrival-rate
JMeter기본 스레드 그룹Concurrency/Arrivals Thread Group, Throughput Shaping Timer

닫힌 모델의 함정이 실무 사고의 단골 원인이다. VU 100명으로 테스트했는데 응답이 2초로 느려지면, 그 VU들은 2초를 기다리느라 다음 요청을 못 보낸다. 부하 생성기가 스스로 브레이크를 밟는 것이다. 결과적으로 서버는 자신이 감당할 수 있는 만큼만 부하를 받고, 리포트에는 "TPS 50에서 안정"이라고 찍힌다. 실제 운영에서는 사용자가 기다려 주지 않고 새 요청이 계속 들어와 큐가 무너진다.

모듈 31 레슨 4에서 다룬 coordinated omission 이 바로 이 구조의 측정 왜곡판이다. 거기서는 "측정기가 느린 응답을 덜 센다"는 통계 문제로 봤고, 여기서는 "부하 생성기가 부하를 덜 준다"는 워크로드 문제로 본다. 같은 뿌리다.

판단 기준은 간단하다.

  • 검증 질문이 "사용자 N명이 동시에 쓰면 어떤가" → 닫힌 모델
  • 검증 질문이 "초당 N건이 들어오면 버티는가" → 열린 모델
  • 공개 서비스의 용량 한계를 찾는 테스트는 거의 항상 열린 모델 이다

think time과 pacing을 근거 있게 정한다

닫힌 모델을 쓸 때 sleep(1) 을 그대로 두면 안 된다. think time은 사용자가 화면을 읽고 다음 동작을 하기까지의 시간 이고, 이것 역시 로그에서 얻는다. 같은 세션 ID의 연속 요청 간격 중앙값을 보면 된다.

화면관측 간격 중앙값테스트에 쓸 값
상품 목록 → 상세4.2초sleep(randomBetween(2, 7))
상세 → 장바구니11.5초sleep(randomBetween(5, 20))
장바구니 → 결제38초시나리오 분리 권장

고정값(sleep(1))은 모든 VU가 같은 박자로 움직이게 만들어 인위적인 동기화 파동 을 만든다. 실제 트래픽은 그렇게 생기지 않는다. 항상 범위를 두고 무작위로 뽑는다.

pacing 은 다른 개념이다. think time이 "요청 사이 쉬는 시간"이라면, pacing은 "한 시나리오를 몇 초에 한 번 반복할 것인가" 다. "사용자 1명이 5분에 한 번 주문 흐름을 돈다"를 재현하려면 시나리오 전체 소요와 무관하게 300초 주기를 맞춰야 한다.

부하의 모양 네 가지를 k6로 구현한다

모듈 16 레슨 2에서 부하·스트레스·스파이크·내구 테스트의 목적 차이 를 배웠다. 여기서는 그것을 실행 가능한 설정으로 옮긴다. 아래는 하나의 k6 스크립트 안에 네 시나리오를 정의하고 --env SHAPE=... 로 골라 실행하는 형태다.

import http from 'k6/http';
import { check, sleep } from 'k6';
import { Trend } from 'k6/metrics';
 
const browseLatency = new Trend('browse_latency', true);
const BASE = __ENV.BASE_URL || 'https://shop.example.com';
 
// --- 부하 모양 정의: 모두 열린 모델(arrival rate) ---
const shapes = {
  // 1) 정상 부하 — 피크 도착률 18.6 rps 를 30분 유지
  normal: {
    executor: 'constant-arrival-rate',
    rate: 19, timeUnit: '1s', duration: '30m',
    preAllocatedVUs: 60, maxVUs: 200,
  },
  // 2) 피크 부하 — 평시에서 피크까지 서서히 올려 한계 탐색
  peak: {
    executor: 'ramping-arrival-rate',
    startRate: 5, timeUnit: '1s',
    preAllocatedVUs: 100, maxVUs: 800,
    stages: [
      { target: 19, duration: '10m' },  // 평시 → 관측된 피크
      { target: 38, duration: '10m' },  // 피크의 2배
      { target: 60, duration: '10m' },  // 한계 탐색
      { target: 0,  duration: '2m'  },
    ],
  },
  // 3) 스파이크 — 푸시 알림 직후 1분(관측된 1분 피크 120 rps)
  spike: {
    executor: 'ramping-arrival-rate',
    startRate: 19, timeUnit: '1s',
    preAllocatedVUs: 200, maxVUs: 2000,
    stages: [
      { target: 19,  duration: '3m'  },
      { target: 120, duration: '20s' },  // 급상승
      { target: 120, duration: '1m'  },
      { target: 19,  duration: '30s' },  // 급하강
      { target: 19,  duration: '10m' },  // 회복 관찰이 진짜 목적
    ],
  },
  // 4) 내구 — 평시의 70% 를 8시간. 메모리 누수·커넥션 누수 관찰
  soak: {
    executor: 'constant-arrival-rate',
    rate: 13, timeUnit: '1s', duration: '8h',
    preAllocatedVUs: 60, maxVUs: 300,
  },
};
 
export const options = {
  scenarios: { main: shapes[__ENV.SHAPE || 'normal'] },
  thresholds: {
    'http_req_failed': ['rate<0.005'],
    'http_req_duration{scenario:main}': ['p(95)<800', 'p(99)<2000'],
    'browse_latency': ['p(95)<600'],
  },
};
 
// --- 트랜잭션 믹스: 로그에서 뽑은 7 : 2 : 1 ---
export default function () {
  const dice = Math.random() * 100;
  if (dice < 71.6) browse();
  else if (dice < 88.0) search();
  else if (dice < 97.2) addToCart();
  else checkout();
}
 
function browse() {
  const res = http.get(`${BASE}/api/products?page=${1 + Math.floor(Math.random() * 20)}`,
    { tags: { txn: 'browse' } });
  check(res, { 'browse 200': (r) => r.status === 200 });
  browseLatency.add(res.timings.duration);
  sleep(randomBetween(2, 7));           // 관측 중앙값 4.2초
}
 
function search() {
  const kw = ['운동화', '가디건', '텀블러', '무선이어폰'][Math.floor(Math.random() * 4)];
  http.get(`${BASE}/api/search?q=${encodeURIComponent(kw)}`, { tags: { txn: 'search' } });
  sleep(randomBetween(3, 9));
}
 
function addToCart() {
  http.post(`${BASE}/api/cart/items`, JSON.stringify({ productId: pickProductId(), qty: 1 }),
    { headers: { 'Content-Type': 'application/json' }, tags: { txn: 'cart' } });
  sleep(randomBetween(5, 20));
}
 
function checkout() {
  const res = http.post(`${BASE}/api/orders`, JSON.stringify({ cartId: 'c-' + __VU }),
    { headers: { 'Content-Type': 'application/json' }, tags: { txn: 'checkout' } });
  check(res, { 'order created': (r) => r.status === 201 });
  sleep(randomBetween(2, 5));
}
 
function randomBetween(a, b) { return a + Math.random() * (b - a); }
 
// 데이터 왜곡 재현: 상위 20% 상품이 조회의 80% 를 차지
function pickProductId() {
  const hot = Math.random() < 0.8;
  return hot ? 1 + Math.floor(Math.random() * 200)
             : 201 + Math.floor(Math.random() * 800);
}
k6 run --env SHAPE=peak --env BASE_URL=https://staging.shop.example.com workload.js

이 스크립트에서 눈여겨볼 다섯 가지가 이 레슨의 요약이다.

  1. 모두 arrival-rate 실행기 다 — 열린 모델을 기본으로 삼았다.
  2. rate 값이 로그에서 나왔다 — 19는 관측 피크 18.6을 올림한 값이고, 120은 1분 피크다.
  3. 시나리오 분기 비율이 로그 비율과 같다 — 71.6 / 16.4 / 9.2 / 1.8.
  4. think time에 범위가 있다 — 관측 중앙값을 중심으로 흔들린다.
  5. 스파이크 시나리오의 진짜 목적은 마지막 10분 이다. 튀는 동안의 오류율보다 튄 뒤에 원래대로 돌아오는가 가 판정 대상이다.

JMeter·Gatling으로 같은 모양 만들기

도구를 바꿔도 개념은 그대로다. JMeter 기본 스레드 그룹은 닫힌 모델이므로, 열린 모델을 쓰려면 플러그인이 필요하다.

JMeter (열린 모델)
  Concurrency Thread Group  또는
  기본 Thread Group + Throughput Shaping Timer (plugins-manager 로 설치)
    - "start 5 rps, end 19 rps in 600s"
    - "hold 19 rps for 600s"
    - "start 19 rps, end 60 rps in 600s"
  Throughput Controller 로 시나리오 비율 71.6 / 16.4 / 9.2 / 1.8 분배
 
비GUI 실행 (부하 테스트는 항상 비GUI)
  jmeter -n -t workload.jmx -l result.jtl -e -o report/

Gatling은 injection 프로파일이 언어 DSL로 표현된다. constantUsersPerSec · rampUsersPerSec 가 열린 모델이고, atOnceUsers 뒤의 maxDuration 조합이 닫힌 모델에 가깝다.

setUp(
  browse.injectOpen(
    constantUsersPerSec(13.6).during(Duration.ofMinutes(10)),   // 71.6% of 19rps
    rampUsersPerSec(13.6).to(43.0).during(Duration.ofMinutes(10))
  ),
  checkout.injectOpen(
    constantUsersPerSec(0.34).during(Duration.ofMinutes(20))    // 1.8% of 19rps
  )
).protocols(httpProtocol)
 .assertions(
    global().responseTime().percentile4().lt(2000),   // p99 < 2s
    global().failedRequests().percent().lt(0.5)
 );

데이터 프로파일 — 같은 요청도 같은 비용이 아니다

워크로드 모델에서 가장 자주 빠지는 항목이다. 세 가지를 확인한다.

항목빠뜨리면 생기는 일재현 방법
데이터 왜곡(skew)테스트 계정은 주문 3건, 실사용 계정은 3,000건 → 쿼리 비용이 다름실운영 분포를 흉내 낸 시드 데이터
캐시 히트율같은 ID만 조회해 히트율 99% → 운영(70%)보다 훨씬 빠르게 측정핫/콜드 비율을 코드로 분배(위 pickProductId)
데이터 증가테이블이 빈 상태로 테스트 → 인덱스 없이도 빠름운영 규모의 행 수를 채운 뒤 측정

캐시 히트율이 특히 조용한 왜곡 원인이다. 부하 테스트는 같은 URL을 반복하므로 자연히 히트율이 높아진다. 운영 히트율이 70%인데 테스트가 99%라면, 그 테스트는 DB 부하의 1/10만 재현한 것이다. 운영 히트율을 먼저 확인하고, 그 값에 맞춰 핫/콜드 요청 비율을 코드에서 강제한다.

워크로드 모델 문서 한 장

마지막으로 위 내용을 한 장으로 고정한다. 이 문서가 없으면 테스트가 끝난 뒤 "이 결과가 어떤 상황을 가정한 건데?"라는 질문에 답할 수 없다.

워크로드 모델 v1.2 — 커머스 웹/API (2026-09-24)
────────────────────────────────────────────
근거 데이터 : 2026-09-17 ~ 09-23 nginx access.log, APM 트랜잭션 통계
대상 시각    : 평일 19:00~19:59 (주간 최대 트래픽 구간)
 
[트랜잭션 믹스]  탐색 71.6% / 검색 16.4% / 장바구니 9.2% / 주문 1.8% / 로그인 0.6%
[도착률]         1시간 평균 18.6 rps, 1분 최대 121 rps (09-20 19:31 푸시 발송 직후)
[피크/평균]      4.1배
[부하 모델]      열린 모델(arrival rate). 근거: 공개 API, 재시도 있음
[think time]     목록→상세 2~7s, 상세→장바구니 5~20s (세션 간격 중앙값 기준)
[데이터 프로파일] 상품 조회 80%가 상위 200개 SKU, 운영 캐시 히트율 72%,
                 주문 이력 분포 p50=4건 / p95=180건 / p99=2,900건
[제외]           내부 배치(02:00), 크롤러 UA 9종 — 별도 시나리오로 분리
[유효기간]       분기 1회 재집계. 프로모션 정책 변경 시 즉시 갱신

[제외] 항목을 반드시 적는다. 크롤러와 헬스체크를 빼지 않고 집계하면 도착률이 부풀려지고, 반대로 내부 배치를 빼고 용량을 잡으면 새벽 장애를 못 본다. 무엇을 뺐는지가 적혀 있어야 다음 사람이 판단할 수 있다.

실무에서 왜 필요한가

성능 테스트가 통과했는데 운영에서 장애가 나는 상황은 거의 언제나 워크로드 불일치에서 온다. 세 가지 전형이 있다.

첫째, 모양이 달랐다. 30분 동안 서서히 올리는 테스트는 통과하지만, 푸시 알림으로 20초 만에 6배가 들어오면 커넥션 풀이 먼저 마른다. 스파이크는 총량이 아니라 기울기 가 문제다.

둘째, 믹스가 달랐다. 조회 위주로 테스트하고 쓰기 비율을 낮게 잡으면 DB 락 경합과 복제 지연을 못 본다. 쓰기 1.8%라는 작은 비율도 절대량이 초당 0.34건에서 2.2건으로 뛰면 전혀 다른 그림이 된다.

셋째, 데이터가 달랐다. 스테이징 DB의 주문 테이블이 5만 행이고 운영이 4,200만 행이면, 같은 쿼리가 같은 계획으로 돌지 않는다. 이건 튜닝으로 해결할 문제가 아니라 워크로드 문서에 적어 둘 전제다.

워크로드 모델은 또한 협상 도구 다. "성능 테스트를 언제까지 해야 하나요"라는 질문에 "피크 도착률 19 rps와 1분 스파이크 121 rps 두 모양을 각각 통과할 때까지"라고 답할 수 있으면 일정이 근거를 갖는다. 반대로 모델이 없으면 성능 테스트는 끝이 없거나, 아무 때나 끝난다.

채용시장에서의 위치

경력기술서에 "k6 사용 경험"이라고 쓰면 도구 사용자로 읽히고, "운영 로그 기반 워크로드 모델 수립 후 부하 시나리오 설계"라고 쓰면 설계자로 읽힌다. 실제 면접에서도 "부하를 어떻게 정했나요"라는 질문이 거의 반드시 따라온다. 이때 열린 모델과 닫힌 모델을 구분해 답하면 성능 테스트를 제대로 해 본 사람이라는 신호가 된다.

실습 과제

과제 1 — 로그에서 믹스와 도착률 뽑기 (25분)

접근 가능한 서비스의 액세스 로그(없으면 팀의 APM 트랜잭션 통계, 그것도 없으면 아래 샘플)를 써서 다음을 만든다.

샘플 로그 요약 (피크 1시간)
GET  /api/feed          31,400
GET  /api/posts/{id}     9,870
GET  /api/users/{id}     4,120
POST /api/posts          1,340
POST /api/comments       2,905
POST /api/likes          8,260
POST /api/auth/login       510
  1. 요청을 시나리오 4~5개로 묶고 비율을 계산한다
  2. 피크 도착률(rps)을 구한다
  3. 읽기 : 쓰기 비율을 따로 계산한다 — 쓰기 절대량이 초당 몇 건인가
  4. 이 서비스에 닫힌 모델과 열린 모델 중 무엇을 쓸지 정하고 한 문장으로 근거를 쓴다

과제 2 — 네 가지 모양 스크립트 작성 (30분)

과제 1의 숫자로 본문 k6 스크립트를 수정해 실행한다. 대상은 공개 테스트 엔드포인트(https://test.k6.io)나 로컬에 띄운 샘플 앱을 쓴다.

  1. SHAPE=normal 과 SHAPE=spike 를 각각 돌린다
  2. 두 실행의 http_req_duration p95·p99와 http_req_failed 를 비교한다
  3. 스파이크 실행에서 부하가 원래대로 돌아온 뒤 응답 시간이 평시 수준으로 복귀하는 데 몇 초가 걸렸는지 잰다
  4. dropped_iterations 지표가 0이 아니면 maxVUs 가 부족한 것이다 — 값을 늘려 다시 돌린다

과제 3 — 닫힌 모델의 함정 재현 (20분)

같은 대상에 대해 두 가지로 부하를 준다.

  • A: constant-vus VU 50, 5분
  • B: constant-arrival-rate rate = (A 실행에서 나온 실제 rps), 5분

두 실행의 p95·오류율·실제 처리량을 비교하고, 왜 B에서만 오류율이 오르는가 를 두 문단으로 설명한다. 차이가 안 나면 rate를 1.5배로 올려 다시 본다.

과제 4 (선택) — 워크로드 모델 문서화 (15분)

본문의 문서 템플릿을 채워 팀 워크로드 모델 v1을 쓴다. [제외] 항목과 [유효기간]을 반드시 채운다.

자가 체크리스트

  • 워크로드 모델의 네 구성요소(사용자 유형·트랜잭션 믹스·도착률·부하 모양)와 데이터 프로파일을 말할 수 있다
  • 액세스 로그에서 경로를 정규화해 시나리오별 비율을 집계할 수 있다
  • 요청 비율과 시나리오 비율이 왜 다른지 설명하고, 시나리오 단위로 셀 수 있다
  • 피크 도착률과 피크 대 평균 비율을 계산하고, 1시간 평균이 1분 피크를 숨긴다는 점을 확인할 수 있다
  • 닫힌 모델과 열린 모델의 차이를 설명하고, 검증 질문에 맞는 쪽을 고를 수 있다
  • 닫힌 모델이 부하를 스스로 줄이는 구조와 coordinated omission의 관계를 설명할 수 있다
  • think time을 로그의 세션 간격에서 근거 있게 정하고 범위로 흔들 수 있다
  • k6의 constant-arrival-rate · ramping-arrival-rate 로 정상·피크·스파이크·내구 네 모양을 구현할 수 있다
  • 캐시 히트율과 데이터 왜곡을 스크립트에서 의도적으로 재현할 수 있다
  • 워크로드 모델 문서에 [제외]와 [유효기간]을 적어 다음 사람이 판단할 수 있게 만든다

흔한 실수

  • 한 가지 요청만 반복해 부하를 준다. 실제 믹스가 재현되지 않아 DB 락·캐시 동작이 운영과 전혀 달라진다. 로그에서 뽑은 비율로 분기시킨다.
  • VU 수를 부하의 단위로 삼는다. "VU 500으로 테스트했습니다"는 서버가 받은 부하량을 말해 주지 않는다. 열린 모델의 rps로 말하고, VU는 그 rps를 내기 위한 수단으로만 쓴다.
  • think time을 sleep(1) 고정으로 둔다. 모든 VU가 같은 박자로 움직여 인위적 파동이 생긴다. 관측 중앙값을 중심으로 범위를 둔다.
  • 스파이크 테스트를 "튀는 동안"만 본다. 진짜 판정 대상은 부하가 내려간 뒤 복귀 시간이다. 스파이크 뒤에 평시 구간을 충분히 붙인다.
  • 크롤러·헬스체크·내부 배치를 집계에 섞는다. 도착률이 부풀거나 반대로 새벽 배치 부하가 통째로 빠진다. 분리하고 문서에 적는다.
  • 스테이징 데이터 규모를 확인하지 않는다. 행 수와 분포가 다르면 같은 쿼리가 다른 실행 계획으로 돈다. 워크로드 문서의 전제로 명시한다.

참고 자료

낯선 말 풀이

이 레슨에 나온 약어입니다. 본문에서도 처음 나올 때 점선으로 표시해 두었습니다 (제목과 링크 글자는 그대로 둡니다).

ID — 식별자
무엇 하나를 다른 것과 구별하기 위해 붙인 이름이나 번호.
JMeter — 제이미터
많은 사람이 한꺼번에 쓰는 상황을 흉내 내 서비스가 버티는지 보는 무료 도구.
TPS — 초당 처리 건수
1초에 몇 건을 처리하는지.
URL — 인터넷 주소
웹에서 무언가의 위치를 가리키는 주소 한 줄.
DB — 데이터베이스
데이터를 넣어 두고 꺼내 쓰는 저장소. 보통 표 여러 개가 서로 연결된 형태다.
APM — 애플리케이션 성능 모니터링
서비스가 실제로 돌아가는 동안 어디가 느린지 들여다보는 도구.
p95 — 95 백분위
느린 쪽 5%를 뺀 나머지가 이 안에 들어온다는 뜻. 100번 중 95번이 그 시간 안에 끝났다는 말이다.
p99 — 99 백분위
100번 중 99번이 이 시간 안에 끝났다는 뜻. 가장 느린 1%만 뺀 값이다.
GUI — 그래픽 사용자 인터페이스
글자만 있는 화면이 아니라 그림·버튼·창으로 조작하는 화면. 보통 말하는 "화면"이 이것이다.
ISTQB — 국제 소프트웨어 테스팅 자격 위원회
테스터 자격 시험을 만들고 용어를 정리하는 국제 단체.
CT-PT — 전문 자격 · 성능 테스팅
성능 시험을 다루는 전문 과목.

용어집 전체 보기