테스트를 돌리면 숫자와 그래프가 나온다. 하지만 이 숫자들이 "좋은 건지 나쁜 건지"를 판단하려면 패턴을 읽을 줄 알아야 한다. 이 편에서는 TPS와 MTT 그래프에서 나타나는 세 가지 패턴을 익히고, VUser를 단계별로 올려가며 포화 곡선을 그리는 방법을 다룬다.

결과 페이지 구성

테스트가 끝나면 Performance Test 상세 페이지에 결과가 표시된다. 핵심 요소는 이렇다.

  • 요약 테이블 — TPS 평균, Peak TPS, MTT 평균, Error Rate가 숫자로 표시된다
  • TPS 그래프 — X축이 시간, Y축이 초당 처리량. 테스트 Duration 동안 TPS가 어떻게 변했는지 보여준다
  • MTT 그래프 — X축이 시간, Y축이 평균 응답 시간(ms). TPS 그래프와 나란히 본다
  • CSV 다운로드 — 상세 데이터를 CSV로 내려받을 수 있다. 별도 분석이 필요할 때 사용한다

TPS 그래프의 세 가지 패턴

TPS 그래프는 서버의 상태를 가장 직관적으로 보여주는 지표다. VUser를 올려가면서 세 가지 패턴 중 하나가 나타난다.

정상 — 서버에 여유가 있다

TPS가 일정한 수평선을 그린다. 약간의 출렁임은 있지만 전체적으로 안정적이다.

식당 비유: 주방에 여유가 있어서 주문이 들어오는 대로 바로 처리한다. 대기열이 없다.

TPS
 │       ┌─────────────────────┐
 │       │                     │ ← 안정적 수평선
 │───────┘                     └──
 │
 └────────────────────────────── 시간
   ↑ 워밍업

앞부분에 TPS가 올라가는 구간은 JVM 워밍업이다. JIT 컴파일러가 핫 코드를 최적화하는 시간이다. 워밍업 이후의 안정 구간이 실제 성능이다.

포화 — 서버가 한계에 도달했다

VUser를 올렸는데 TPS가 더 이상 올라가지 않고 수평을 유지한다. 이전 VUser 단계와 TPS가 비슷하다.

식당 비유: 가스레인지 4개가 전부 가동 중이다. 손님이 더 와도 동시에 만들 수 있는 요리 수는 4개로 똑같다. 나머지 주문은 대기열에 쌓인다.

TPS
 │          ┌──────────────────┐
 │          │ ← 올라가다 멈춤  │
 │      ┌───┘                  └──
 │  ┌───┘
 │──┘
 └────────────────────────────── 시간

이 시점의 TPS가 서버의 최대 처리량이다. 리소스(CPU, 커넥션, 스레드) 중 하나가 풀가동 상태라는 뜻이다.

과부하 — 서버가 무너지고 있다

TPS가 올라가다가 꺾이면서 하락한다. 또는 급격하게 출렁인다.

식당 비유: 주방이 너무 붐벼서 요리사들이 서로 부딪힌다. 혼자였을 때보다 오히려 효율이 떨어진다. 주문 실수(에러)도 발생한다.

TPS
 │      ╱╲
 │    ╱╱  ╲╲   ← 불안정하게 출렁
 │  ╱╱      ╲╲
 │╱╱          ╲───── ← 하락
 └────────────────────────────── 시간

Error Rate가 0%에서 벗어나기 시작한다면 과부하 신호다. 이 상태에서 VUser를 더 올리면 서버가 응답을 아예 못 하게 될 수 있다.

MTT 그래프의 의미

MTT 그래프는 TPS 그래프의 거울이다. TPS와 반대 방향으로 움직인다.

정상 상태

MTT가 일정하게 유지된다. VUser가 늘어도 각 요청의 응답 시간이 변하지 않는다. 서버가 추가 부하를 감당하고 있다는 뜻이다.

포화 상태

TPS는 안 올라가는데 MTT가 서서히 증가한다. 처리 속도는 한계인데 요청은 더 많이 들어오니까, 대기열에서 기다리는 시간이 늘어나는 것이다.

식당에서 주방은 풀가동인데 손님이 더 들어오면, 음식 만드는 시간은 같아도 대기 시간이 늘어나서 전체 시간이 길어진다.

과부하 상태

MTT가 급격하게 치솟는다. 수십 ms이던 응답이 수백 ms, 수 초로 늘어난다. 타임아웃이 발생하기 시작한다.

TPS와 MTT를 함께 읽기

두 그래프를 나란히 놓고 보면 서버 상태를 정확하게 판단할 수 있다.

서버 상태TPSMTT의미
여유VUser 비례 상승일정리소스가 충분하다
포화수평 유지서서히 상승최대 처리량 도달, 대기열 발생
과부하하락 또는 출렁급격히 상승리소스 경합으로 효율 저하

리틀의 법칙으로 검증

01편에서 소개한 리틀의 법칙을 실전에 적용해본다.

VUser = TPS × MTT(초)

VUser 50, TPS 250, MTT 200ms(0.2초)이면: 50 = 250 × 0.2 = 50. 계산이 맞는다.

같은 테스트에서 VUser를 100으로 올렸더니 TPS가 여전히 250이라면, MTT는 100 ÷ 250 = 0.4초, 즉 400ms가 된다. TPS는 그대로인데 MTT만 2배가 됐다. 서버가 포화 상태에서 추가 VUser를 대기시키고 있다는 뜻이다.

리틀의 법칙이 안 맞을 때

실제 결과에서 VUser = TPS × MTT가 정확히 맞지 않을 수 있다. Think Time(요청 사이 대기)이 있거나, 에러로 중단된 요청이 있으면 오차가 생긴다. 방향성은 맞지만 소수점까지 일치하지 않는 것은 정상이다.

포화 곡선 그리기

가장 실용적인 분석 방법이다. VUser를 단계별로 올려가며 각 단계의 TPS와 MTT를 기록하면, 서버의 한계가 어디인지 한눈에 보인다.

테스트 실행 방법

VUser 1(기준선)은 이미 측정했다. 나머지를 순서대로 실행한다.

단계ProcessThreadVUserDuration
기준선1111분
2단계110103분
3단계130303분
4단계225503분
5단계4251003분

각 단계를 별도 테스트로 만들어서 순차 실행한다. 한 테스트에서 VUser를 동적으로 바꾸는 것보다 결과가 깔끔하다.

2단계부터는 Duration을 3분으로 늘린다. VUser가 많아지면 워밍업과 안정화에 시간이 더 걸리기 때문이다. Ramp-up도 활성화해서 VUser가 점진적으로 추가되도록 한다.

결과 기록 표

각 단계의 안정 구간 값을 기록한다.

| 단계   | VUser | TPS | MTT (ms) | Error Rate | 비고 |
|--------|-------|-----|----------|------------|------|
| 기준선 | 1     |     |          | 0%         |      |
| 2단계  | 10    |     |          |            |      |
| 3단계  | 30    |     |          |            |      |
| 4단계  | 50    |     |          |            |      |
| 5단계  | 100   |     |          |            |      |

포화점 판단 기준

표를 채우면 이런 패턴이 보인다.

  • VUser 1→10→30 : TPS가 비례해서 올라간다 → 서버에 여유가 있다
  • VUser 30→50 : TPS가 조금 올라가거나 멈춘다 → 포화 진입
  • VUser 50→100 : TPS가 안 올라가거나 오히려 떨어진다 → 포화 또는 과부하

TPS가 더 이상 올라가지 않기 시작하는 VUser 수가 포화점이다. 이 시점의 TPS가 서버의 최대 처리량이다.

예를 들어 VUser 30에서 TPS 180, VUser 50에서 TPS 190이라면 포화점은 VUser 30~50 사이다. 최대 처리량은 약 TPS 190이다.

포화점에서 멈추는 이유

서버 리소스(CPU, DB 커넥션, Thread 풀) 중 하나가 한계에 도달한 것이다. 어떤 리소스가 병목인지 찾는 방법은 다음 편에서 다룬다.

에러율 해석

Error Rate는 0%가 정상이다. 0%가 아닌 순간부터 주의가 필요하다.

에러가 갑자기 발생하는 경우

특정 VUser 수를 넘으면 에러가 나기 시작한다. 서버의 하드 리밋에 부딪힌 것이다.

  • Connection timeout — DB 커넥션 풀이 고갈되어 대기 시간이 타임아웃을 초과했다
  • 500 Internal Server Error — 서버 내부 예외. 메모리 부족, 스레드 풀 고갈 등
  • 502/503 — 앞단 프록시(Nginx 등)가 백엔드 서버의 응답을 못 받았다

에러가 간헐적으로 발생하는 경우

VUser가 낮아도 가끔 에러가 나온다. 스크립트 문제일 가능성이 높다.

  • assert 조건이 너무 엄격하다 (예: 200만 허용하는데 서버가 가끔 204를 돌려준다)
  • 인증 토큰이 만료되었다 (Duration이 토큰 유효기간보다 길면)
  • 테스트 데이터가 고갈되었다 (모든 VUser가 같은 데이터를 조회/수정해서 충돌)

자주 하는 실수

워밍업 구간을 포함해서 평균을 냄

TPS 그래프의 앞부분(워밍업)은 JVM이 최적화되기 전이라 낮다. 이 구간을 포함하면 평균 TPS가 실제보다 낮게 나온다. 안정 구간의 값을 기준으로 판단한다.

[!WARNING] 한 번의 테스트로 성능을 판단

VUser 1에서 TPS 50이 나왔다고 "이 서버의 처리량은 TPS 50"이라고 결론 내리면 안 된다. VUser를 올리면 TPS도 올라간다. 포화 곡선을 그려야 서버의 실제 한계를 알 수 있다.

[!WARNING] VUser를 한 번에 너무 크게 점프

VUser 1 → 100으로 바로 뛰면 포화점이 어디인지 알 수 없다. 10 → 30 → 50처럼 단계적으로 올려야 "어디서부터 멈추는지"가 보인다.

[!WARNING] 다른 프로그램이 리소스를 잡아먹는 상태에서 테스트

IDE, 브라우저, 다른 Docker 컨테이너가 CPU와 메모리를 점유하면 결과가 왜곡된다. 부하 테스트 중에는 가능한 한 불필요한 프로세스를 종료한다.