성능 테스트
고친 사람 github-actions[bot]
성능 테스트는 시스템에 일감을 걸어 놓고 답하는 속도와 끝내는 양을 재는 시험입니다. 기능이 맞게 도는지를 보는 시험과 달리 여럿이 몰릴 때 어떻게 버티는지를 봅니다. 사람이 몰려 터진 뒤에야 알게 되는 것을 미리 알아내려는 것입니다.
쉽고 빠른 이해
무슨 일을 하는 시험인가 — 요청을 일부러 많이 보내 놓고 답이 몇 초 만에 오는지, 1초에 몇 건이나 끝내는지를 잽니다. 배포 전에 「사용자 500명이 한꺼번에 들어와도 장바구니 화면이 1초 안에 뜨나」를 확인하는 것이 그런 시험입니다.
왜 하나 — 혼자 눌러 볼 때는 모든 화면이 금방 뜹니다. 여럿이 몰리면 줄이 생깁니다. 자원까지 바닥나면 전혀 다르게 굽니다. 그 차이는 재 보기 전에는 알 수 없습니다.
어떻게 도나
- 사용자가 밟는 순서를 골라 차례대로 적습니다
- 요청 수를 정해 둔 모양대로 올려 가며 걸어 봅니다
- 걸린 시간과 끝낸 건수, 실패한 건수를 모아 미리 정한 기준과 견줍니다
대가 — 시험 환경을 운영 환경에 가깝게 맞추는 데 돈과 손이 듭니다. 맞추지 못한 채로 얻은 숫자는 운영 환경에서 그대로 나오지 않습니다.
상세
이 절은 성능 테스트가 기능 시험과 어떻게 다른지부터 짚습니다. 그다음 무엇을 재는지, 부하를 어떤 모양으로 거는지, 그 모양에 따라 시험 이름이 어떻게 갈리는지를 차례로 봅니다. 마지막 세 소절은 한 번 도는 순서, 다 돌리고도 숫자를 못 믿게 되는 다섯 가지, 이 시험을 할 때와 안 할 때입니다.
동네에 수도관을 새로 깔았다고 해 봅시다. 한 집만 수도꼭지를 틀면 물이 세게 나옵니다. 온 동네가 한꺼번에 틀면 수압이 떨어집니다. 몇 집이 함께 틀 때부터 떨어지기 시작하는지는 다 같이 틀어 보기 전에는 알 수 없습니다.
기능 시험과 다른 점
기능을 보는 시험은 결과가 맞는지를 가립니다. 장바구니에 물건 하나를 담고 목록에 그 하나가 뜨면 통과입니다. 답이 맞음과 틀림 둘 중 하나로 떨어집니다.
성능 테스트의 답은 숫자입니다. 같은 장바구니 담기를 수백 명이 동시에 할 때 답이 몇 초 만에 왔고 몇 건이 실패했는지를 셉니다. 숫자 자체에는 통과도 실패도 없어서, 시험 전에 합격선을 정해 두어야 판정이 됩니다.
합격선은 이런 꼴로 적습니다. 「동시 사용자 500명일 때 장바구니 담기 100건 중 99건이 0.5초 안에 끝나고, 실패는 100건 중 1건을 넘지 않는다.」 조건과 숫자가 함께 적혀 있어야 다른 사람이 같은 판정을 되풀이할 수 있습니다.
함께 보는 네 값
아래 네 값을 함께 읽습니다. 하나만 읽으면 그 값이 멀쩡한 동안 나머지가 무너진 것을 놓칩니다.
| 재는 것 | 뜻 | 이것만 보면 놓치는 것 |
|---|---|---|
| 응답 시간 | 요청을 보내고 답을 받기까지 기다린 시간 | 같은 시간에 몇 건을 끝냈는지 |
| 처리량 | 정해진 시간 동안 끝낸 요청 수 | 한 사람이 얼마나 기다렸는지 |
| 오류율 | 전체 요청 중 실패로 끝난 비율 | 성공한 요청이 얼마나 느렸는지 |
| 자원 사용률 | 기계의 계산 능력과 메모리, 디스크, 네트워크가 얼마나 찼는지 | 사용자가 겪는 속도가 어떤지 |
응답 시간은 평균 하나로 줄이지 않습니다. 평균은 오래 걸린 몇 건을 가립니다. 그래서 요청을 걸린 시간 순으로 줄 세워 「100건 중 99건이 이 안에 끝났다」처럼 읽습니다. 이렇게 줄 세워 읽는 값을 백분위수라고 하고, 99번째 값은 흔히 p99 라고 줄여 적습니다.
서버가 일을 하지 않고 곧장 오류를 돌려주면 그 요청은 아주 짧게 끝납니다. 오류를 세지 않으면 무너지기 시작한 순간에 응답 시간이 오히려 나아진 것처럼 보입니다.
부하를 올려 가며 이 값들을 같이 그리면 아래 모양이 나옵니다. 숫자는 모양을 보이려고 든 예입니다. 먼저 처리량입니다.
xychart-beta
title "동시 사용자 수에 따른 처리량"
x-axis ["50명", "100명", "200명", "300명", "400명", "500명", "600명"]
y-axis "1초에 끝낸 건수" 0 --> 350
line [50, 100, 190, 260, 300, 300, 285]
같은 구간에서 잰 응답 시간입니다.
xychart-beta
title "동시 사용자 수에 따른 응답 시간"
x-axis ["50명", "100명", "200명", "300명", "400명", "500명", "600명"]
y-axis "초" 0 --> 6
line [0.1, 0.12, 0.2, 0.35, 0.9, 2.4, 5.0]
처리량은 400명 언저리까지만 늘고 거기서 평평해집니다. 더 넣은 요청은 끝나지 않고 줄에 쌓이므로, 같은 지점부터 응답 시간이 가파르게 꺾여 올라갑니다. 오류율도 이 언저리에서 함께 오릅니다. 두 그림에서 볼 것은 처리량이 평평해지는 지점과 응답 시간이 꺾이는 지점이 같다는 점입니다.
자원 사용률은 숫자가 틀어진 까닭을 찾는 데 씁니다. 응답 시간이 늘었을 때 CPU(Central Processing Unit, 중앙처리장치)가 꽉 찼는지, 메모리가 모자랐는지, 커넥션 풀이 비어 요청이 기다렸는지에 따라 손볼 곳이 달라집니다.
커넥션 풀은 미리 열어 둔 데이터베이스 연결을 모아 두고 빌려주는 장치입니다. 연결을 새로 맺는 데 시간이 걸려서 미리 열어 둡니다. 풀이 비면 요청은 남이 반납할 때까지 기다립니다.
부하를 거는 모양
부하는 시스템에 한꺼번에 걸리는 일감의 양입니다. 동시에 들어와 있는 요청 수나 사용자 수로 잽니다.
이 사용자 수는 사람이 아니라 가상 사용자로 셉니다. 가상 사용자는 도구가 흉내 내는 사용자 하나입니다. 사람 대신 정해 둔 순서대로 요청을 보내고 답을 기다립니다. 합격선에 적는 동시 사용자 500명은 가상 사용자 500개를 뜻합니다.
부하는 처음부터 최대치로 걸지 않습니다. 사람이 한순간에 다 들어오는 일은 드뭅니다. 게다가 한꺼번에 올려 버리면 어느 지점에서 꺾였는지 짚을 수 없습니다.
부하는 보통 아래 모양으로 겁니다. 가상 사용자 수를 시간에 따라 올렸다가 한동안 유지한 뒤 내립니다.
flowchart TD
A["가상 사용자 0에서 시작한다"] --> B["정해진 시간 동안 조금씩 늘린다"]
B --> C["목표 가상 사용자 수에서 일정하게 유지한다"]
C --> D["유지하는 동안 값을 모은다"]
D --> E["부하를 내리고 되돌아오는지 본다"]
그림의 올리는 구간, 유지하는 구간, 내리는 구간은 하는 일이 저마다 다릅니다. 가상 사용자 수를 조금씩 늘리는 첫 구간은 램프업이라고 부릅니다.
값을 모으는 곳은 유지 구간입니다. 가상 사용자 수를 올리는 동안에는 숫자가 계속 흔들려서 어느 값이 그 수의 값인지 정할 수 없습니다. 목표 수에서 한동안 놔두어야 값이 가라앉습니다.
내리는 구간도 봅니다. 부하를 걷었는데 응답 시간이 원래대로 안 돌아오면 무언가가 쌓이고 있다는 뜻입니다. 큐에 밀린 요청이 남았거나 메모리가 새는 경우가 그렇습니다.
부하 모양에 따라 갈리는 시험들
성능 테스트는 우산 같은 이름입니다. 부하를 어떤 모양으로 걸고 무엇을 알아내려 하는지에 따라 아래처럼 갈라 부릅니다. 하나하나는 따로 항목을 갖습니다.
모양부터 그려 보면 세 가지가 이렇게 갈립니다.
xychart-beta
title "시간에 따라 부하를 거는 모양 세 가지"
x-axis ["0분", "5분", "10분", "20분", "30분", "40분", "50분", "60분"]
y-axis "가상 사용자 수" 0 --> 600
line [0, 250, 500, 500, 500, 500, 250, 0]
line [50, 50, 50, 500, 50, 50, 50, 50]
line [100, 100, 100, 100, 100, 100, 100, 100]
사다리꼴로 올라가 한동안 유지하다 내려오는 것이 부하 테스트입니다. 한 점에서만 뾰족하게 솟는 것이 스파이크 테스트, 낮게 깔린 채 끝까지 가는 것이 소크 테스트입니다. 나머지 둘은 모양이 아니라 목적으로 갈립니다.
| 이름 | 무엇을 알아내려 하나 |
|---|---|
| 부하 테스트 | 예상하는 최대 부하에서 합격선을 지키나 |
| 스트레스 테스트 | 한계를 넘기면 어디서 어떻게 무너지나 |
| 스파이크 테스트 | 부하가 한순간에 치솟으면 버티나 |
| 소크 테스트 | 같은 부하를 오래 걸면 무언가 쌓이나 |
| 스모크 테스트 | 시나리오와 도구가 제대로 돌아가나 |
이름은 팀마다 갈립니다. 부하 테스트를 성능 테스트와 같은 뜻으로 쓰는 곳도 있습니다. 남이 쓴 글을 읽을 때는 어느 뜻으로 썼는지 먼저 봅니다.
한 번 도는 순서
성능 테스트는 한 번 돌리고 끝내는 일이 아닙니다. 고칠 곳을 찾아 고치고 다시 재는 되풀이입니다.
flowchart TD
A["합격선을 숫자로 정한다"] --> B["사용 흐름을 시나리오로 적는다"]
B --> C["시험 환경과 데이터를 갖춘다"]
C --> D["부하를 걸고 값을 모은다"]
D --> E["합격선과 견줘 판정한다"]
E --> F["원인을 찾아 고친다"]
F --> D
첫 회차의 값은 기준선으로 남겨 둡니다. 기준선은 손대기 전에 잰 값입니다. 이것이 없으면 고친 뒤의 숫자가 나아진 것인지 그날 시험 환경이 한가했던 것인지 가릴 수 없습니다.
시나리오는 사용자가 밟는 순서입니다. 로그인, 목록 보기, 물건 담기를 차례로 밟는 식입니다.
한 화면만 되풀이해 두드리면 안 됩니다. 그 화면의 결과가 캐싱되기 때문입니다. 캐싱은 한 번 만든 결과를 저장해 두고 다음에 그대로 내주는 것입니다. 두 번째 요청부터는 서버가 일을 안 하므로 운영 환경과 동떨어진 값이 나옵니다.
숫자를 못 믿게 되는 다섯 가지
성능 테스트에서 흔한 실패는 시험이 안 돌아가는 것이 아닙니다. 끝까지 돌았는데 나온 숫자를 쓸 수 없게 되는 것입니다.
환경이 다르다 — 시험 서버가 운영 서버보다 작으면 나온 숫자를 운영 환경으로 옮겨 쓸 수 없습니다. 몇 곱절로 환산하는 것도 안 됩니다. 한계에 가까워질수록 응답 시간이 곧게 늘지 않기 때문입니다.
데이터가 적다 — 빈 테이블에서는 어떤 조회든 금방 끝납니다. 운영 데이터 크기에 가까운 데이터를 넣어 두어야 인덱스가 없는 조회가 드러납니다. 인덱스는 찾는 값을 빨리 짚으려고 따로 만들어 두는 자료입니다.
예열을 안 했다 — 예열은 본 시험 앞에 요청을 조금 흘려보내 서버를 데워 두는 것입니다. 막 뜬 서버는 커넥션 풀이 비어 있고 캐시도 차 있지 않습니다. 이 구간의 값을 섞어 세면 전체가 제 속도보다 처지게 나옵니다.
재는 곳이 다르다 — 서버가 스스로 잰 값에는 네트워크를 건넌 시간과 줄에서 기다린 시간이 빠집니다. 도구가 요청을 보내는 쪽에서 잰 값에는 그 시간이 들어갑니다. 한쪽 구간이 다른 쪽 구간 안에 들어 있는 셈입니다.
sequenceDiagram
participant 도구
participant 대상
도구->>대상: 요청을 보낸다
Note over 대상: 서버가 재는 구간
대상-->>도구: 답을 보낸다
Note over 도구,대상: 도구가 재는 구간
같은 요청 하나를 두고도 두 구간의 값이 다르게 나옵니다. 두 숫자를 견줄 때는 어디서 잰 값인지부터 맞춥니다.
도구 쪽이 먼저 막힌다 — 요청을 보내는 기계의 CPU나 네트워크가 먼저 꽉 차면 그 뒤로는 시험 대상이 아니라 도구를 재게 됩니다. 도구 쪽 자원 사용률도 같이 봐야 이 경우를 가려낼 수 있습니다.
할 때와 안 할 때
거는 값이 있을 때 합니다. 사용자가 몰리는 서비스, 한 번 무너지면 되돌리기 어려운 일을 다루는 서비스, 구조를 크게 바꾼 배포 앞이 그렇습니다.
아직 안 하는 편이 나은 때도 있습니다. 기능이 자주 바뀌는 초기에는 시나리오를 따라 고치는 손이 얻는 것보다 큽니다. 부하가 뻔히 작은 사내 도구도 마찬가지입니다.
다만 「나중에 하겠다」가 길어지면 손댈 수 없게 됩니다. 성능을 망가뜨리는 구조는 대개 한 줄이 아니라 설계에 박혀 있어서, 늦게 찾을수록 고쳐야 하는 범위가 넓어집니다.
관련 항목
성능 테스트가 재는 지표
응답 시간 · 처리량 · 오류율 · 초당 요청 수 · 백분위수 · 자원 사용률 · 포화도 · 꼬리 지연
부하 모양에 따라 갈리는 시험
부하 테스트 · 스트레스 테스트 · 스파이크 테스트 · 소크 테스트 · 스모크 테스트 · 벤치마크 · 회귀 테스트
부하를 만드는 데 쓰는 도구
Locust · k6 · Gatling · JMeter · pgbench · wrk
부하를 거는 방식을 이루는 개념
가상 사용자 · 램프업 · 동시성 · 생각 시간 · 테스트 시나리오 · 기준선 · 워크로드
성능 테스트가 찾아내는 병목
병목 · 커넥션 풀 · 락 경합 · 가비지 컬렉션 · 메모리 누수 · N+1 · 큐잉 이론
성능 테스트 결과로 정하는 약속
서비스 수준 목표 · 서비스 수준 지표 · 용량 계획 · 자동 확장 · 타임아웃
성능 테스트 뒤에 원인을 파는 수단
프로파일링 · 모니터링 · 분산 추적 · 플레임 그래프 · 인덱스 · 캐싱
성능 테스트가 속하는 상위 분류
다른 이름: performance testing · performance test · 퍼포먼스 테스트 · 성능 시험