부하테스트 시나리오 5: 채널 목록 조회 - 100-hours-a-week/2-hertz-wiki GitHub Wiki
시나리오 설명: 사용자가 개인 채널보관함 목록을 조회
로그인한 사용자가 개인 채널보관함 페이지에 접속하여, 본인이 참여 중인 채널 목록을 최근 메시지 기준으로 조회합니다. 해당 목록은 페이지네이션되어 있으며, API는 /api/v1/channel?page={페이지번호}&size={페이지크기}
입니다. 서버는 DB에서 해당 유저의 채널 목록과 메시지 메타데이터를 응답합니다.
항목 | 내용 |
---|---|
테스트 목적 | 사용자 보관함 진입 시 채널 목록 조회 성능 및 부하 검증 |
테스트 환경 | - Monolithic: e2-custom-2-6144 (vCPU 2개, 메모리 6GB) |
대상 시스템 | 백엔드(Spring Boot + MySQL) |
테스트 도구 | k6 |
테스트 일시 | 2025년 5월 27일 |
사용자가 자신의 채널보관함 목록을 확인하는 과정에서 발생하는 부하에 대해, 시스템이 얼마나 안정적으로 응답할 수 있는지 검증합니다. 이는 채널 접속 전 사용자 홈 진입 단계에서 발생하는 트래픽이며, 5초 간격으로 반복 호출되는 API입니다.(Polling)
어플리케이션 | API 엔드포인트 | 메서드 | 설명 |
---|---|---|---|
Spring Boot | /api/v1/channel?page={페이지번호}&size={페이지크기} | GET | 사용자 채널보관함 목록 반환 API (Authorization 필요) |
차수 | 1차 | 2차 | 3차 | 4차 |
---|---|---|---|---|
동시 사용자 수 | 50명 | 100명 | 400명 | 500명 |
총 요청 시간 | 30초 | 30초 | 30초 | 30초 |
요청 간격 | 3초 | 3초 | 3초 | 3초 |
부하 생성 패턴 | shared-iterations (고정 요청 수 분배) | shared-iterations | shared-iterations | shared-iterations |
배치 처리 방식 | 멀티스레드 기반 동시 요청 처리 | 멀티스레드 기반 동시 요청 처리 | 멀티스레드 기반 동시 요청 처리 | 멀티스레드 기반 동시 요청 처리 |
🔹 1차 테스트 결과
K6
항목 | 값 | 정의 |
---|---|---|
최대 VU 수 (vus_max) | 50 | 동시에 실행된 최대 가상 사용자 수 (Virtual Users) |
총 요청 수 (http_reqs) | 300 | 전체 테스트 동안 실행된 HTTP 요청 수 |
평균 응답 시간 (avg) | 137.83ms | 전체 요청의 평균 응답 시간 |
최대 응답 시간 (max) | 648.29ms | 가장 느린 요청의 응답 시간 |
90% 응답 시간 (p90) | 400.11ms | 90%의 요청이 이 시간 이하로 응답됨 |
95% 응답 시간 (p95) | 508.05ms | 95%의 요청이 이 시간 이하로 응답됨 |
에러율 (http_req_failed) | 0.00% | 실패한 HTTP 요청의 비율 |
성공률 (checks_succeeded) | 100.00% | 성공 조건을 만족한 요청 비율 (예: 응답 코드 200, JSON 형식 등) |
요청 완료 시간 (iteration_duration.avg) | 5.17s | 1회 요청당 평균 전체 실행 시간 |
총 테스트 시간 | 약 31.4초 | 전체 부하 테스트가 완료되기까지 걸린 시간 |
전체 리소스 사용량
어플리케이션별 리소스 사용량
APM 결과(API 지연 시간, 호출 수, 에러율)
🔹 2차 테스트 결과
K6
항목 | 값 | 정의 |
---|---|---|
최대 VU 수 (vus_max) | 100 | 동시에 실행된 최대 가상 사용자 수 (Virtual Users) |
총 요청 수 (http_reqs) | 600 | 전체 테스트 동안 실행된 HTTP 요청 수 |
평균 응답 시간 (avg) | 202.07ms | 전체 요청의 평균 응답 시간 |
최대 응답 시간 (max) | 1.17s | 가장 느린 요청의 응답 시간 |
90% 응답 시간 (p90) | 604.86ms | 90%의 요청이 이 시간 이하로 응답됨 |
95% 응답 시간 (p95) | 956.15ms | 95%의 요청이 이 시간 이하로 응답됨 |
에러율 (http_req_failed) | 0.00% | 실패한 HTTP 요청의 비율 |
성공률 (checks_succeeded) | 100.00% | 성공 조건을 만족한 요청 비율 (예: 응답 코드 200, JSON 형식 등) |
요청 완료 시간 (iteration_duration.avg) | 5.21s | 1회 요청당 평균 전체 실행 시간 |
총 테스트 시간 | 약 31.9초 | 전체 부하 테스트가 완료되기까지 걸린 시간 |
전체 리소스 사용량
어플리케이션별 리소스 사용량
APM 결과(API 지연 시간, 호출 수, 에러율)
🔹 3차 테스트 결과
K6
항목 | 값 | 정의 |
---|---|---|
최대 VU 수 (vus_max) | 400 | 동시에 실행된 최대 가상 사용자 수 (Virtual Users) |
총 요청 수 (http_reqs) | 2336 | 전체 테스트 동안 실행된 HTTP 요청 수 |
평균 응답 시간 (avg) | 520.27ms | 전체 요청의 평균 응답 시간 |
최대 응답 시간 (max) | 4.48s | 가장 느린 요청의 응답 시간 |
90% 응답 시간 (p90) | 2.19s | 90%의 요청이 이 시간 이하로 응답됨 |
95% 응답 시간 (p95) | 3.31s | 95%의 요청이 이 시간 이하로 응답됨 |
에러율 (http_req_failed) | 0.00% | 실패한 HTTP 요청의 비율 |
성공률 (checks_succeeded) | 100.00% | 성공 조건을 만족한 요청 비율 (예: 응답 코드 200, JSON 형식 등) |
요청 완료 시간 (iteration_duration.avg) | 5.59s | 1회 요청당 평균 전체 실행 시간 |
총 테스트 시간 | 약 35초 | 전체 부하 테스트가 완료되기까지 걸린 시간 |
전체 리소스 사용량
어플리케이션별 리소스 사용량
APM 결과(API 지연 시간, 호출 수, 에러율)

🔹 4차 테스트 결과
K6
항목 | 값 | 정의 |
---|---|---|
최대 VU 수 (vus_max) | 500 | 동시에 실행된 최대 가상 사용자 수 (Virtual Users) |
총 요청 수 (http_reqs) | 2399 | 전체 테스트 동안 실행된 HTTP 요청 수 |
평균 응답 시간 (avg) | 693.31ms | 전체 요청의 평균 응답 시간 |
최대 응답 시간 (max) | 4.84s | 가장 느린 요청의 응답 시간 |
90% 응답 시간 (p90) | 2.71s | 90%의 요청이 이 시간 이하로 응답됨 |
95% 응답 시간 (p95) | 3.81s | 95%의 요청이 이 시간 이하로 응답됨 |
에러율 (http_req_failed) | 0.00% | 실패한 HTTP 요청의 비율 |
성공률 (checks_succeeded) | 100.00% | 성공 조건을 만족한 요청 비율 (예: 응답 코드 200, JSON 형식 등) |
요청 완료 시간 (iteration_duration.avg) | 5.79s | 1회 요청당 평균 전체 실행 시간 |
총 테스트 시간 | 약 60초 | 전체 부하 테스트가 완료되기까지 걸린 시간 |
❗리소스 부족으로 서버가 다운됨
전체 리소스 사용량
- 서버 다운으로 인한 모니터링 값 유실
어플리케이션별 리소스 사용량
APM 결과(API 지연 시간, 호출 수, 에러율)
- 동시 사용자 100명 이하에서는 평균 응답 시간 약 200ms로, 사용자 경험 측면에서 매우 빠르고 안정적인 성능을 보임
- 하지만 400명 이상부터는 평균 응답 시간이 500ms 이상, 최대 4.84초 이상까지 증가하며, 성능 저하가 나타남.
- 500명 부하 테스트 시, 응답은 정상적으로 반환되었으나 서버 자체가 다운되었으며, 이는 명백한 리소스 한계 초과를 나타냄.
- 특이사항으로는, 모든 테스트에서 HTTP 요청 에러율은 **0.00%**로, 기능적 오류 없이 응답이 이루어짐. 이는 기능적 안정성은 확보되어 있지만, 성능 및 확장성 측면에서는 개선이 필요한 상태임을 의미함.
- 전체 테스트 전반에서 공통적으로 CPU 사용률은 90% 이상, 메모리도 거의 최대 용량까지 사용되는 모습을 확인할 수 있음
- 특히 3차 및 4차 테스트(400~500 VU)에서는 CPU 및 RAM 사용률이 급격히 상승하며 시스템 자원이 포화 상태에 도달함.
- 500명 동시 요청 테스트 시, CPU와 메모리가 한계치를 넘어서면서 실제 VM이 다운되는 현상이 발생했고, 이는 물리 자원의 부족이 성능 병목을 유발함을 보여줌
- 응답 지연은 요청 수 증가에 따른 자원 부족 및 처리 병목으로 판단됨.
- 특히, Trace에서 확인된 어플리케이션 처리 지연은 리소스 부족으로 인한, 메모리 부족, 스레드 부족, 잦은 GC 수행 등의 문제를 일으켰을 가능성이 높음.
- 현재 서버 자원으로는 100건 이상의 동시 요청을 안정적으로 처리하기 어려우며, 인프라 확장 또는 구조적 최적화가 필요하다.
- 현재 도커 컨테이너 환경으로의 마이그레이션을 통해 nextjs, springboot, mysql을 각 각의 인스턴스로 분리 예정이므로, 마이그레이션 전에는 사용자 수 100명에 맞춰 인스턴스 리소스를 CPU 2코어, RAM 8GB까지 늘리는 것을 제안한다.
- 테스트 스크립트:
/home/devops/stress-testing/get-message-test.js
테스트 스크립트
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 500,
duration: '30s',
};
// 테스트할 API 정보
const BASE_URL = '<https://dev.hertz-tuning.com>';
const ENDPOINT = '/api/v1/channel?page=0&size=10';
const ACCESS_TOKEN = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI3IiwiaWF0IjoxNzQ4MzUwNDQ1LCJleHAiOjE3NDk1NjAwNDV9.jrU_9DS5dLNwXZHGOzcyt1TLbE5MqmuRqzMBEOskdio'; // 실제 토큰으로 교체
export default function () {
const url = `${BASE_URL}${ENDPOINT}`;
const params = {
headers: {
Authorization: `Bearer ${ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
};
const res = http.get(url, params);
check(res, {
'응답 코드가 200이어야 함': (r) => r.status === 200,
'응답이 JSON 형식임': (r) => {
try {
JSON.parse(r.body);
return true;
} catch (e) {
return false;
}
},
'채널 리스트가 포함되어야 함': (r) => r.body.includes('channelRoomId'),
});
sleep(5);
}