성능테스트로 응답시간과 처리량 병목 검증하기
성능테스트의 부하 모델, 응답시간·처리량 지표, Loop Back·Tier·Spike 시험과 병목 분석 운영 방식을 정리한다.
2026-08-14 · 최초 발행 2025-12-14
응답속도 목표를 부하 조건에서 검증하는 일
성능테스트는 주어진 환경에서 시스템이 목표 응답속도(SLO/SLA)와 처리량(TPS)을 충족하는지 확인하고, 구성요소별 감당능력과 병목을 찾아내는 시험이다. 정상 부하뿐 아니라 피크와 장애 상황에서 안정성과 확장성을 검증 범위에 둔다.
사용자 수, 요청 간격, 응답시간은 다음 관계로 해석할 수 있다.
- TPS(Throughput) ≈ AU / (RT + TT) [Closed workload 근사]
- AU=Active Users, RT=Response Time, TT=Think Time
- CU(Concurrency) ≈ TPS × RT [Little’s Law]
- RIT(Request Interval Time) = RT + TT
용어가 섞이면 부하 모델도 흔들린다. Named User는 등록 사용자 전체(Concurrent + 비접속자)이며, Concurrent는 특정 시점의 접속자(Inactive + Active)다. Active는 요청을 보내고 응답을 기다리는 사용자, Inactive는 응답을 받은 뒤 다음 요청을 기다리는 사용자(ThinkTime 상태)를 뜻한다. Response Time(RT)은 요청부터 응답 완료까지의 시간이고, Request Interval Time(RIT)은 연속 요청 간격으로 RT + ThinkTime이다.
부하 모델과 관측 기준을 함께 설계한다
사용자 여정과 거래 믹스, ThinkTime 분포, 데이터 소비 패턴을 먼저 정한다. Closed(Open) 모델을 선택하고 피크·일상·배치 구간을 구분한 뒤, 결제·조회·가입 같은 도메인 이벤트별 비중과 목표 TPS, p95 RT를 설정한다.
환경은 Prod와 유사한 스케일, 네트워크, 미들웨어 버전으로 구성한다. 대표 데이터 세트와 캐시 워밍 정책을 준비하고, 메트릭·APM·로그·트레이스를 연관 지어 볼 수 있어야 한다. 타임스탬프 동기화(NTP)도 이 관측 체계의 일부다.
판정 기준에는 p50/p95/p99 RT, TPS, 에러율, CPU·메모리·GC·IO 대기, DB 락·대기, 큐 적체를 포함할 수 있다. 종료 기준은 에러율≤X%, p95 RT≤Y ms, 리소스 임계(예: CPU 80%) 미초과, GC STW≤Z ms처럼 정량적으로 둔다.
재현성을 확보하려면 IaC와 테스트 코드를 관리하고, 시나리오를 버저닝하며, CI 파이프라인과 연결한다. 테스트 데이터 재사용·리셋도 자동화 대상이다. 워밍업, 충분한 Steady-state, 반복 측정은 결과의 통계적 유의성을 위한 조건이다.
병목을 분리해 확인하는 시험 방식
Loop Back Test
특정 지점 이후를 단락(stub)해 하위 계층 부하를 제거하고, 논리 구간별 병목을 좁혀 가는 방식이다. 애플리케이션 경계부에 Loop Back 코드를 넣은 뒤 상위 계층만 부하를 주고 RT 개선 여부를 비교한다. 네트워크, DB, 외부 API의 영향을 분리해 검증할 수 있다.
코드 변경 리스크와 기능 플래그 관리는 고려해야 한다. 하위 리소스의 실제 지연과 오류 특성이 반영되지 않는 한계도 있다.
Tier Test
웹, WAS, DB, 캐시, 메시지 브로커처럼 특정 계층에 직접 부하를 가해 성능 병목을 찾는다. SQL 리플레이나 메시지 퍼블리셔 같은 대상 Tier 전용 드라이버로 트래픽을 주입해 단일 계층의 성능 곡선을 추정한다.
애플리케이션 코드를 바꾸지 않고 수행할 수 있지만, 캐시 적중이나 연결 재활용처럼 계층 간 상호작용은 축소될 수 있다.
Spike Test
모든 단말이 동시에 거래하는 상황처럼 매우 짧은 시간의 트래픽 급증을 재현한다. 즉시 고부하를 넣고 Auto Scaling, 큐 흡수력, 서킷브레이커 동작을 관찰한 뒤 캐시·큐·DB의 리커버리까지 확인한다.
데이터와 자원을 손상시킬 위험이 있으므로 릴리즈 전 격리 환경에서 시행하는 편이 적합하다.
시험은 관측과 재시험까지 이어진다
계획 단계에서는 거래별 p95 RT, 목표 TPS, 최대 동시 접속자, 에러율 한계를 정하고 피크·정상·배치 구간을 나눈다. Closed 모델은 AU를 고정하고 ThinkTime을 사용하며, Open 모델은 도착률을 제어한다. 이 선택과 함께 데이터 동시성 및 충돌도 설계해야 한다.
환경은 Prod 준유사 스케일과 설정을 복제하고, 기능 플래그·서킷브레이커·율제한 구성도 맞춘다. 데이터 시딩과 정리, 캐시 워밍업, GC·컴파일 워밍업을 수행하며 워밍업을 제외할 타임라인 기준점을 명시한다.
Ramp-up에서는 병목을 미리 감지하고, Steady 상태에서는 통계가 수렴할 시간을 확보한다. 배치·백그라운드 작업은 차단하거나 분리한다. 시스템, APM, DB, 네트워크 지표를 동시에 수집해 샘플링 편향을 피하고, 로드 제너레이터를 포함한 테스트 인프라는 리소스 여유 40% 이상을 확보한다.
처리량과 지연의 상관관계에서 무릎 지점(Knee)을 확인하고, 락 대기·큐 적체·풀 고갈·GC 정지 구간을 구분한다. 튜닝은 알고리즘·쿼리·IO 감소, 캐시·비동기화, 스케일 아웃, 파라미터 조정 순으로 진행한다.
수치로 보는 용량 산정 예시
목표 p95 RT=1.5s, 평균 RT=1.2s, ThinkTime=2.0s, AU=600인 Closed 부하를 가정한다.
- RIT = RT + TT = 1.2 + 2.0 = 3.2s
- TPS ≈ AU / RIT = 600 / 3.2 ≈ 187.5 TPS
- 동시 처리 중 요청(CU 근사) ≈ TPS × RT = 187.5 × 1.2 ≈ 225
단일 인스턴스 한계 TPS=80이고 목표 70% 여유율(안정영역)로 운용하면 유효 TPS≈56이다. 필요 인스턴스 수는 ≈187.5 / 56 ≈3.35이므로 4대로 산정한다. Spike 2배 부하(≈375 TPS)에서는 p95 RT, 에러율, 오토스케일 반응 시간을 평가한다.
모델 선택과 운영상 제약
Closed 모델은 사용자 행동을 재현하기 쉽지만 서버 압력은 수요와 공급의 상호작용으로 제한되어 피크 순간의 유입량을 재현하는 데 한계가 있다. Open 모델은 도착률을 직접 제어해 현실적인 트래픽 폭주를 재현하기 좋지만, 실패 전파와 큐잉 폭증 위험을 함께 다뤄야 한다.
데이터는 마스킹된 실제 데이터 또는 합성 데이터를 사용한다. PII와 결제정보는 토큰화·격리하고, 테스트 계정과 권한은 최소화한다. 외부 연동에는 샌드박스나 모의 서버를 사용해 실서버 오염을 막는다.
고해상도 트레이싱은 병렬 시험에서 오버헤드를 늘릴 수 있다. 기준 시험에는 샘플링을 적용하고, 원인 추적이 필요할 때 수집 범위를 확대한다.
| 항목 | Loop Back Test | Tier Test | Spike Test |
|---|---|---|---|
| 성능 병목 식별 | 상위/하위 분리, 코드 단락으로 원인 축소 | 특정 계층 성능 곡선 도출 | 급증 시 응답/회복 특성 평가 |
| 확장성 평가 | 제한적(하위 미포함) | 계층별 확장성 정밀 | 전체 시스템 급격 부하 대응 |
| 일관성/재현성 | 높음(제어 용이) | 높음(드라이버 제어) | 변동성 큼(환경 민감) |
| 안정성 리스크 | 낮음 | 중간(실계층 부담) | 높음(자원 고갈 위험) |
| 운영 편의 | 코드 플래그 필요 | 드라이버/리플레이 준비 필요 | 윈도우·롤백 계획 필수 |
k6로 Closed 부하와 Spike 부하를 함께 검증하기
환경/전제조건은 k6 v0.49+ 설치, 테스트 네트워크 대역폭 여유, 타겟 호스트에 대한 안전한 테스트 승인이다. Closed 모델(ThinkTime 포함)로 정상 부하와 Spike를 함께 검증한다.
// k6 script (Closed + Spike)
import http from 'k6/http';
import { sleep } from 'k6';
import { Trend } from 'k6/metrics';
export const options = {
scenarios: {
steady_closed: {
executor: 'constant-vus',
vus: 600, // AU
duration: '10m',
startTime: '0s',
},
spike_open: {
executor: 'ramping-arrival-rate',
startRate: 50,
timeUnit: '1s',
preAllocatedVUs: 800,
maxVUs: 2000,
stages: [
{ duration: '10s', target: 400 },
{ duration: '30s', target: 400 },
{ duration: '20s', target: 0 },
],
startTime: '10m10s',
},
},
thresholds: {
http_req_duration: ['p(95)<1500'],
http_req_failed: ['rate<0.01'],
},
};
const rt = new Trend('rt');
export default function () {
const res = http.get(__ENV.TARGET_URL || 'https://test.example.com/api/health');
rt.add(res.timings.duration);
// ThinkTime to emulate user pacing
sleep(2.0); // TT
}
실행 예는 TARGET_URL=https://test.example.com k6 run perf.js다. steady_closed 시나리오는 AU=600, TT=2.0s에서 TPS≈AU/(RT+TT)를 기준으로 보고, spike_open은 단위시간 도착률로 급증 부하를 검증한다.
성능테스트는 목표 응답속도와 처리량을 사전에 검증해 장애를 예방하고, 용량 산정 정밀도를 높여 과/과소 프로비저닝을 막는다. 릴리즈 게이트와 회귀 성능 열화 탐지 기준을 만들고, 경보 임계·스케일링 정책·백프레셔·서킷브레이커 기준을 운영 지표로 정립하는 데도 사용된다.