한 줄 정의
성능 테스트는 실제 사용 패턴을 재현한 부하를 점진적으로 높여 가며 시스템의 최대 처리량인 포화점과 성능이 꺾이는 버클존을 찾아내는 측정 작업입니다.
쉽게 말하면
새로 지은 다리를 개통하기 전에 하는 하중 시험과 같습니다.
트럭을 한 대씩 올려 보면 어느 무게까지는 멀쩡히 버티다가, 한계에 가까워지면 미세하게 휘기 시작하고, 어느 지점을 넘으면 급격히 구부러집니다. 구부러지기 직전까지 버틴 최대 하중이 포화점 이고, 구부러지는 구간이 버클존 입니다. 버클존이라는 이름 자체가 이 물리 현상(좌굴, buckling)에서 왔습니다.
그리고 시험 조건이 실전과 다르면 시험 자체가 무의미해집니다. 빈 트럭만 올리거나(너무 적은 테스트 데이터), 같은 트럭 한 대만 왕복시키거나(사용자 한 명의 토큰 재사용), 계측 장비를 다리 위에 올려놓고 재면(부하기와 대상 시스템을 같은 장비에서 실행) 숫자는 나오지만 실제 하중과는 다른 값입니다. 이 장의 설계·주의 사항 전부가 “시험 조건을 실전과 같게 만들라”는 한 문장으로 요약됩니다.
왜 중요한가?
내가 만든 서버가 어느 정도의 트래픽을 감당할 수 있는지는 측정해 보기 전까지 알 수 없습니다. 포화점을 알아야 목표 성능과 비교해 만족할지, 병목을 찾아 제거할지 판단할 수 있습니다.
더 위험한 것은 잘못 설계된 테스트가 주는 거짓 안심입니다. 캐시 효과나 적은 데이터로 실제보다 좋게 나온 숫자를 믿고 배포하면, 한계는 결국 운영 트래픽이 알려 주게 됩니다.
핵심 내용
성능 테스트 종류
성능 테스트는 특정 작업 부하 상태에서 응답성과 안정성 측면에서 시스템이 어떻게 작동하는지 확인하는 테스트이며, 응답 시간·처리량·자원 사용량(CPU, 메모리)을 주로 측정합니다.
| 종류 | 확인하는 것 |
|---|---|
| 부하(load) 테스트 | 예상 부하에서의 동작. 주요 기능의 응답 시간·처리량을 확인하고 병목을 파악 |
| 스트레스(stress) 테스트 | 예상을 뛰어넘는 부하에서 어디까지 성능을 내는지 — 시스템의 최대 성능 |
| 지속 부하(soak) 테스트 | 장시간 일정 부하를 견디는지. 성능 저하 여부와 메모리 누수 탐지 |
| 스파이크(spike) 테스트 | 트래픽이 급변할 때의 반응성과 안정성. 순간 급증 시 성능 저하·실패 확인 |
포화점과 버클존
일반적인 진행 방식은 낮은 부하에서 시작해 점진적으로 높이는 것입니다. 동시 사용자 100명에서 시작해 200명, 300명으로 단계적으로 올리며 측정합니다.
flowchart LR A[부하 증가] --> B[처리량도<br/>비례해 증가] --> C[증가 폭 둔화] --> D[포화점<br/>최대 처리량] --> E[버클존<br/>처리량·응답 시간 급락]
- 포화점(saturation point, tipping point): 성능이 저하되기 전의 최대 처리량. 시스템이 감당할 수 있는 성능 한계 지점입니다
- 버클존(buckle zone): 포화점을 지나 성능이 꺾이기 시작하는 구간입니다
포화점이 목표보다 낮다면
포화점이 목표 성능보다 높다면 만족하면서 테스트를 마칠 수 있지만, 낮거나 같다면 병목을 찾아 제거해야 합니다.
웹 서버의 병목은 대개 호출 비중이 높으면서 응답 시간이 긴 기능과 관련되어 있고, 응답 시간에 영향을 주는 요인은 DB 연동(쿼리 실행 시간), 외부 연동 시간, 트래픽 대비 부족한 커넥션 풀 크기 입니다.
병목 지점을 확인한 뒤 서버 확장·캐시 적용·비동기 연동 같은 수단으로 응답 시간을 줄이면, 줄인 만큼 포화점을 높일 수 있습니다.
주요 측정 지표
| 지표 | 보는 값 | 해석 포인트 |
|---|---|---|
| 응답 시간 | 평균·최대·최소·중앙·95%/99% 백분위 | 평균과 최대의 차이가 크면 백분위를 함께 확인. 백분위가 최대보다 평균에 가깝다면 최대값은 이상치일 수 있음 |
| 처리량 | TPS의 최대·평균·최소 | 테스트 중 계속 변하므로 한 값만 보지 않음 |
| 에러율 | 전체 요청 중 에러 비율 | 처리량은 높은데 에러가 함께 늘면 부하를 감당하지 못한다는 신호. 에러가 증가하기 시작하는 지점이 시스템의 한계 일 가능성이 높음 |
| CPU 사용률 | 대상 시스템의 CPU | 성능 문제는 주로 DB·외부 연동에서 나오지만 높은 CPU 사용률이 원인일 수도 있음. 원인 분석 범위를 좁혀 줌 |
평균만 보면 안 되는 이유
응답 시간 분포에서 중앙값이 평균보다 큰 좌편향 분포라면, 절반 이상의 요청이 평균보다 느리다는 뜻입니다.
평균은 소수의 빠른 요청이 끌어내린 낙관적인 숫자일 수 있으므로, 이런 분포에서는 느린 요청 쪽(백분위)을 기준으로 확인해야 합니다.
성능 테스트 설계 시 고려 사항
전부 “시험 조건을 실전과 같게”라는 한 원칙의 변주입니다. 왜곡 요인을 놓치면 결과가 실제보다 좋게 나옵니다.
| 고려 사항 | 원칙 | 함정 |
|---|---|---|
| 트래픽 패턴 | 서비스의 실제 패턴대로 부하를 설계. 특정 시간에 목표 부하가 바로 발생하는 시스템이라면 테스트도 해당 부하를 바로 발생 | 콜센터 시스템은 업무 시간 내 고른 패턴, 학원 서비스는 주중 오후 급증 — 같은 부하 곡선을 쓸 수 없음 |
| 동시 사용자 수·트래픽 규모 | 규모가 크면 부하 발생기를 여러 대 사용. 목표 동시 사용자 수만큼 서로 다른 사용자로 요청을 생성 | 한 사용자의 인증 토큰으로 모든 요청을 만들면 DB 캐싱 효과로 실제보다 좋은 성능이 나옴 |
| 기능별 요청 비율 | 자주 불리는 API 목록을 추려 실제 호출 비율대로 부하를 구성 | 1개 API에만 부하를 주면 캐시 효과로 결과가 실제보다 잘 나옴 |
| 데이터 크기 | 예상 규모에 맞는 테스트 데이터를 준비 | 100만 건 예상 서비스에 50건만 넣으면 무의미. 반대로 1년에 2천 개 생기는 기능에 50만 개를 만들어도 왜곡 |
| 워밍업 | 캐시가 어느 정도 찬 뒤 본 테스트를 진행. 점진적 부하 증가로 워밍업(예: 첫 30초 20명 → 15초마다 20명씩 추가) | 빈 캐시 상태의 초기 성능 저하를 시스템의 한계로 오판 |
| 목표치 | 실제 예상 사용자 수와 요청 건수 기반으로 설정 | 10만 TPS는 10만 명이 1초마다 계속 클릭해야 나오는 규모 — 의지만 담은 목표는 비현실적 |
성능 테스트 도구
| 도구 | 특징 |
|---|---|
| nGrinder | 네이버가 개발. 컨트롤러 1대 + 에이전트 다수 구조로, 컨트롤러가 웹 UI를 제공하고 에이전트가 부하 발생과 대상 서버 지표(CPU·메모리) 모니터링을 담당. 에이전트를 여러 장비에 두어 대량 부하 가능. 스크립트는 Groovy·Jython |
| k6 | Grafana Labs가 개발. Go 기반이지만 스크립트는 자바스크립트. 고루틴을 사용해 스레드 기반 도구보다 적은 자원으로 더 많은 부하 발생. CLI 실행이라 CI/CD 통합이 쉽고 Prometheus·InfluxDB로 결과 저장 가능. 분산 부하는 유료 k6 클라우드 또는 쿠버네티스의 k6 Operator |
| Locust | 파이썬 코드로 테스트 정의. 명령행·웹 UI 실행, 분산 부하와 클라우드 버전 제공 |
| Gatling | 자바·스칼라·코틀린·자바스크립트 스크립트. 웹 요청 외에 AMQP·카프카 등 다양한 프로토콜 지원. 분산 부하는 엔터프라이즈 버전 |
| JMeter | 가장 오래 명맥을 유지한 도구. GUI·CLI 지원, FTP·DB·TCP 등 폭넓은 프로토콜, 분산 환경 부하 지원 |
어떤 도구가 더 좋다기보다 상황에 맞춰 고르면 됩니다. JVM 환경이 익숙하면 nGrinder나 Gatling, 자바스크립트가 익숙하면 k6부터 시도합니다.
실행 시 주의 사항
-
부하기와 테스트 대상 시스템을 반드시 분리합니다. 부하 생성 자체가 자원을 많이 쓰므로 한 장비에서 실행하면 대상 시스템이 자원을 온전히 쓰지 못해 실제보다 낮은 결과가 나옵니다. 부하기 장비 성능이 나쁘면 애초에 부하를 제대로 만들지 못하므로 부하기 사양에도 신경 씁니다
-
서버 설정 제한을 미리 확인합니다. Nginx의
limit_req_zone으로 IP당 초당 요청을 10개로 제한해 두면, 1대 장비에서 다량의 요청을 만드는 부하기가 전부 제한에 걸립니다. 스레드 풀·DB 커넥션 풀도 테스트할 부하 규모에 맞게 미리 키워 둡니다 -
운영 시스템과 같은 네트워크에서 대량 부하를 만들지 않습니다. 스트레스 테스트로 네트워크가 포화 상태에 이르면 실제 서비스가 먹통이 됩니다. 부하 테스트 환경이 운영과 같은 네트워크에 있다면 사용자가 적은 새벽 시간대로 피해야 할 수도 있습니다
-
외부 서비스는 가짜로 대체합니다. 테스트하려는 대상은 우리 시스템이지 외부 시스템이 아닙니다. 외부 연동을 그대로 둔 채 부하를 주면 외부 시스템이 트래픽을 견디지 못하고 장애가 날 수 있습니다
-
테스트 대상은 운영과 동일한 사양으로 구성합니다. 운영은 8코어 16GB인데 테스트 환경이 2코어 2GB라면 실제 환경의 성능을 확인할 수 없습니다
성능 테스트는 지루한 반복입니다
부하를 높이며 측정하고, 목표 성능이 안 나오면 병목을 분석해 코드를 고치고, 다시 테스트합니다. 원하는 결과가 나올 때까지 이 과정을 반복하는 것이 성능 테스트의 실체입니다.
내 생각
- 성능 테스트의 최악은 실패가 아니라 잘못된 통과입니다. 토큰 한 개 재사용, 50건짜리 DB, 단일 API 부하 — 설계 실수는 전부 숫자를 좋게 만드는 방향으로 왜곡됩니다. 테스트가 잘 나왔다면 먼저 왜곡 요인부터 의심하는 습관이 필요합니다.
- 에러율은 포화점의 조기 경보입니다. TPS 그래프가 꺾이는 것보다 에러율이 증가하기 시작하는 지점이 먼저 보이는 경우가 많으므로, 처리량만 보지 말고 에러율을 같은 화면에 두고 관찰해야 합니다.
- k6 + CI가 현실적인 시작점입니다. CLI 기반이라 파이프라인에 넣어 배포 전 성능 회귀를 자동으로 잡을 수 있습니다. 웹 UI가 필요한 팀 단위 테스트가 아니라면 도구 선택 고민에 시간을 쓸 이유가 없습니다.
- 워밍업 여부는 “무엇을 측정하려는가”로 정합니다. 배포 직후 콜드 스타트 상황 자체를 보려는 게 아니라면 캐시를 채우고 시작해야 하고, 반대로 로컬 캐시를 쓰는 서비스라면 콜드 스타트 시나리오도 별도 테스트로 한 번은 확인할 가치가 있습니다.
관련 개념
- Ch02 느려진 서비스, 어디부터 봐야 할까 — 포화점을 높이는 수단들(커넥션 풀·캐시·확장)과 응답 시간의 구성
- Ch02 성능 테스트 방법론 — 테스트 유형 구분과 측정 안티 패턴을 다루는 자바 최적화 2판의 방법론 정리