부하 테스트의 정확성
요청을 받는 서버와 테스트를 하는 주체가 같은 PC에 있는, Localhost 테스트는 테스트를 즉각적으로 하기 용이하다는 장점이 있지만, 다음과 같은 실제 환경과의 괴리가 있을 수 있습니다.
- 서버가 힘을 써야 할 때 부하 발생기가 자원을 뺏어서 사용하고, 반대의 경우도 존재합니다. 결국 서로가 영향을 주게 되는데, 이런 점은 누구의 책임인 지를 판가름하기 어렵습니다.
- 실제로는 와이파이나 LTE 등으로 접속을 하는데, 그 과정에서 생기는 현상을 전혀 알수 없습니다. 이는 심한 괴리를 불러옵니다. 특히 대역폭 제한으로 많은 응답 시간, 처리량 차이를 보일 수 있는데, 루프백은 메모리 복사 속도에 가깝기 때문에 로컬 환경에서는 비정상적으로 높은 처리량을 보일 수 있습니다.
- 부하 테스트 프로세스와 WAS 같은 프로세스가 같은 CPU 코어에서 번걸아 실행된다면 Context Switching이 일어나고, 이때 서로의 데이터를 L1, L2 캐시에서 밀어냅니다. 이로 인해 실제 환경보다 잦은 Cache miss가 발생할 수 있고, 이는 순수한 애플리케이션 로직의 성능을 측정하는데 노이즈로 작용합니다. Context Switching 또한 실제 환경보다 더 자주 일어날 수 있습니다.
즉, 부하 테스트를 진행할 때, 개선을 하더라도 실제 환경과 테스트 환경이 최대한 일치해야 정확한 측정 및 병목 현상 관측이 가능할 것입니다.
부하 테스트 환경 구축
필자는 가용할 수 있는 PC가 2대인 관계로, 관측 PC와 대상 PC 각각 1대씩 총 2대로 분리하여 테스트 환경을 조성하려고 합니다. 아래는 조성한 부하 테스트 시스템 아키텍처입니다.

부하 테스트 시나리오는 다음과 같습니다.
- K6를 이용해서 서버에 시나리오 대로 HTTP 요청을 진행합니다.
- 측정하려는 대상이 있다면, 이 대상에 대해서 좁혀서 집중적으로 관측하면 좋습니다.
- HTTP 요청은 조성되어 있는 네트워크 환경을 통해서 서버 PC에 도달합니다.
- WAS에서 해당하는 요청을 처리하기 위해서 내부의 DB와 I/O를 진행합니다.
- 진행된 결과가 다시 네트워크를 통해서 K6에 반환됩니다.
- 이 측정 결과를 InfluxDB에 적재합니다.
- 프로메테우스는 서버 상태 (CPU, 메모리, 에러율 등)을 주기적으로 가져오고 이를 블록 단위로 디스크에 저장합니다.
- 이때 Disk I/O는 매우 느리므로, nmap() 시스템콜을 이용하여 파일을 메모리 공간에 매핑해서 커널이 효율적으로 데이터를 캐싱하도록 합니다. 이에 Grafana가 빠르게 데이터를 대시보드에 적용할 수 있습니다.
- Grafana에서 Server instance를 시각화할 대시보드를 만들어서 관측 PC에 제공합니다.
- 이때 관측하는 정보는 InfluxDB와 Prometheus에서 주기적인 Query로 가져옵니다.
- 모두 HTTP 통신을 이용하여 Query의 결과를 반환받고, 각각 InfluxQL, PromQL를 사용합니다.
- 대시보드에서는 발생하는 에러나 병목 현상을 관찰하고, 이를 개선합니다.
Prometheus
프로메테우스는 자체적인 저장소와 통신 기능을 갖춘 독립적인 모니터링 서버입니다. 내부 관찰의 관점에서, 지금 서버가 어떻게 돌아가고 있는지에 대한 시계열 데이터를 Polling으로 가져오게 됩니다.
JVM 메모리, CPU 사용률, 스레드 상태, DB 커넥션 등을 관측하고, Polling으로 Grafana가 가져와서 대시보드에서 가시화합니다.

위와 같이 프로메테우스가 읽을 수 있도록 노출한 필드를 볼수 있습니다.
| 카테고리 | 내용 | 설명 |
| 애플리케이션 상태 | application_ready_time_seconds | 애플리케이션이 요청을 처리할 준비가 되기까지 걸린 시간 |
| application_started_time_seconds | 애플리케이션이 시작되기 까지 걸린 시간 | |
| 디스크 상태 | disk_free_bytes | 사용 가능한 디스크 공간 |
| disk_total_bytes | 전체 디스크 공간 | |
| 스레드 및 실행 제어 | executor_active_threads | 현재 활성 상태인 스레드 수 |
| executor_pool_size_threads | executor 풀의 현재 스레드 수 | |
| executor_queue_remaining_tasks | 큐의 수용 가능한 작업이 남은 공간 | |
| executor_queued_tasks | 큐에서 대기 중인 작업 개수 | |
| File Descriptors | process_files_open_files | 열린 모든 파일 디스크립터 개수 |
| Prometheus Scraping | /actuator/prometheus 호출 횟수 | 프로메테우스 호출 횟수 |
| DB | hikaricp_connections | DB 커넥션이 몇 개 준비되어 있고 사용 중인지 트래킹 |
| JVM | jvm_memory_used_bytes | 메모리 양과 GC 횟수 등을 확인 |
| HTTP | http_server_requests_seconds | Request가 얼마나 왔고, 주문 처리에 얼마나 걸렸는지 확인 |
InfuxDB
K6 등의 부하 테스트의 결과를 직접 push해서 기록하고, 이를 쿼리로 뽑아내서 대시보드에서 가시화합니다.
influxDB는 데이터들을 Measurement (측정값), Tag (메타데이터), Field (실제 수치)의 구조로 저장합니다.
| 카테고리 | 내용 | 설명 |
| 응답 시간 관련 지표 (Latency) |
http_req_duration | 요청이 시작되어 응답을 완전히 받을 때까지의 총 시간 |
| http_req_waiting | 서버가 요청을 받고 첫 번째 바이트를 보낼 때 까지 걸린 시간 -> 이 값이 크다면 비즈니스 로직 처리나 DB 조회에 병목 의심 |
|
| 처리량 및 에러 (Throughput & Error) |
http_reqs | 초당 처리된 요청 수 (RPS) -> 서버의 최대 수용 능력 파악 |
| http_req_failed | 테스트 중 발생한 에러의 비율 | |
| vus | Virtual Users. 가상 유저의 수. |
Grafana
위에서 설정했던 서버의 주소를 매핑하고, 이를 이용해서 대시보드를 가시화합니다. 시간 단위로 폴링 주기를 결정할수 있으며, 다양한 대시보드 탬플릿을 지원합니다.
최초에 구성했던 모니터링 환경


위는 커스터마이징으로 자주 볼것같은 것을 위에서부터 순서대로 만들어본 대시보드입니다. G1의 Eden, Old 영역과 HTTP_Req_Duration 등 직관적이면서도 병목을 관찰할 수 있거나 OOM 등의 사건이 발생할 것 같은 곳 위주로 패널을 넣었습니다. DB 커넥션도 매우 중요하니 히카리CP를 중간에 넣었습니다. 마지막으로 스크롤을 내리면 Stop the world 시간과 JVM에서 쓰이고 있는 종류별 스레드의 개수도 볼수 있도록 설정했습니다.
12900 탬플릿 활용






12900 탬플릿을 적용해보니, 매우 다양하면서도 필요할 것 같은 부분이 많았습니다. 직접 넣었던 힙 메모리의 내용도 좀 더 잘 보일수 있게 그래프가 수정되어 있었고, Stop the world time 뿐만 아니라 GC Count도 있어서 지표로 번갈아 보면서 확인할 수 있었습니다. 또한 힙 메모리 뿐만 아니라 다양한 스태틱 메모리, 커넥션 풀 사이즈도 숫자로 나와있어서 좀 더 가시성이 좋았던 것 같습니다. 이에 따라서 탬플릿을 사용하되, 필요한 부분을 추가 및 수정을 해서 사용할 계획입니다.
참고문헌
K6 부하테스트, Grafana (+ influx DB) 모니터링, Prometheus 인스턴스 메트릭 수집
위 기술에 대해 검색해보면 대부분 로컬 환경에서 혼자 실행하는 것을 목표로 하기 때문에 따로 블로그를 작성한다.조직에서, 특히 폐쇄망에서 공통으로 사용할 수 있는 것을 목표로 진행한다.
velog.io
'Test' 카테고리의 다른 글
| [Test] 점진적 부하테스트: 30,000명 유저의 SSE 구독 요청 - TCP (0) | 2026.02.04 |
|---|---|
| [Test] 점진적 부하테스트: 10,000명 유저의 SSE 구독 요청 - 톰캣 (0) | 2026.02.03 |
| [Test] 500명 유저의 동시 로그인 요청 처리 (0) | 2026.02.02 |
| [Test] 점진적 부하테스트: 10,000명 유저의 동시 SSE 구독 요청 - 삽질 (0) | 2026.02.02 |
| [Test] JaCoCo의 원리와 Test coverage 검증 (0) | 2026.01.26 |