From 28f3bd1cfa31020f9795ea62ef657389dddc9276 Mon Sep 17 00:00:00 2001 From: JiHwan Date: Thu, 28 Aug 2025 22:25:07 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=84=B1=EB=8A=A5=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Performance Test/health-test.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Performance Test/health-test.js diff --git a/Performance Test/health-test.js b/Performance Test/health-test.js new file mode 100644 index 0000000..ab19d8b --- /dev/null +++ b/Performance Test/health-test.js @@ -0,0 +1,22 @@ +import http from 'k6/http'; +import { check, sleep } from 'k6'; + +const baseUrl = __ENV.K6_TEST_URL; +const targetUrl = `${baseUrl.replace(/\/$/, '')}/health-check`; + +console.log(`[k6] 테스트 시작!!. Target URL: ${targetUrl}`); + +export const options = { + thresholds: { + http_req_duration: ['p(95)<300'], + http_req_failed: ['rate<0.01'], + }, +}; + +export default function () { + const res = http.get(targetUrl, { tags: { endpoint: 'health' } }); + check(res, { + 'status is 200': (r) => r.status === 200, + }); + sleep(1); +} \ No newline at end of file From 2e88d75f90c09fbeda5689fbdec214c43619d9be Mon Sep 17 00:00:00 2001 From: JiHwan Date: Sat, 30 Aug 2025 16:24:59 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EB=AA=A9=EC=A0=81=EB=B3=84=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 8 +- Performance Test/health-test.js | 22 ------ .../endurance-test/health-check.js | 76 +++++++++++++++++++ Performance-Test/load-test/health-check.js | 62 +++++++++++++++ Performance-Test/spike-test/health-check.js | 46 +++++++++++ 5 files changed, 191 insertions(+), 23 deletions(-) delete mode 100644 Performance Test/health-test.js create mode 100644 Performance-Test/endurance-test/health-check.js create mode 100644 Performance-Test/load-test/health-check.js create mode 100644 Performance-Test/spike-test/health-check.js diff --git a/.gitignore b/.gitignore index 2c70930..11eb8f2 100644 --- a/.gitignore +++ b/.gitignore @@ -51,4 +51,10 @@ docker-stack.yml .gradle/ gradle-app.setting !gradle-wrapper.jar -!gradle-wrapper.properties \ No newline at end of file +!gradle-wrapper.properties + +# 테스트 파일 제외 +/Performance-Test/load-test/README.md +/Performance-Test/endurance-test/README.md +/Performance-Test/stress-test/README.md +/Performance-Test/spike-test/README.md \ No newline at end of file diff --git a/Performance Test/health-test.js b/Performance Test/health-test.js deleted file mode 100644 index ab19d8b..0000000 --- a/Performance Test/health-test.js +++ /dev/null @@ -1,22 +0,0 @@ -import http from 'k6/http'; -import { check, sleep } from 'k6'; - -const baseUrl = __ENV.K6_TEST_URL; -const targetUrl = `${baseUrl.replace(/\/$/, '')}/health-check`; - -console.log(`[k6] 테스트 시작!!. Target URL: ${targetUrl}`); - -export const options = { - thresholds: { - http_req_duration: ['p(95)<300'], - http_req_failed: ['rate<0.01'], - }, -}; - -export default function () { - const res = http.get(targetUrl, { tags: { endpoint: 'health' } }); - check(res, { - 'status is 200': (r) => r.status === 200, - }); - sleep(1); -} \ No newline at end of file diff --git a/Performance-Test/endurance-test/health-check.js b/Performance-Test/endurance-test/health-check.js new file mode 100644 index 0000000..f02ceda --- /dev/null +++ b/Performance-Test/endurance-test/health-check.js @@ -0,0 +1,76 @@ +import http from 'k6/http'; +import { check, sleep } from 'k6'; + +const baseUrl = __ENV.K6_TEST_URL; +const targetUrl = `${baseUrl.replace(/\/$/, '')}/health-check`; + +// 지속성 테스트 설정 +const TARGET_VUS = Number(__ENV.K6_TARGET_VUS || 1000); +const TEST_DURATION = __ENV.K6_TEST_DURATION || '1h'; // 기본 1시간 +const RAMP_UP_TIME = __ENV.K6_RAMP_UP_TIME || '300s'; +const TARGET_P95 = Number(__ENV.K6_TARGET_P95 || 500); +const MAX_ERROR_RATE = Number(__ENV.K6_MAX_ERROR_RATE || 0.01); + +export const options = { + scenarios: { + 'Endurance-Test': { + executor: 'ramping-vus', + startVUs: 0, + stages: [ + { duration: RAMP_UP_TIME, target: TARGET_VUS }, // 300초에 걸쳐 TARGET_VUS VU까지 증가 + { duration: TEST_DURATION, target: TARGET_VUS }, // 1시간 동안 TARGET_VUS VU 유지 + { duration: '30s', target: 0 }, // 30초에 걸쳐 0으로 감소 + ], + }, + }, + thresholds: { + 'http_req_duration': [`p(95)<${TARGET_P95}`], + 'http_req_failed': [`rate<${MAX_ERROR_RATE}`], + 'http_req_duration{status:200}': [`p(99)<${TARGET_P95 * 2}`], // 99퍼센타일도 모니터링 + 'http_reqs': [`rate>${TARGET_VUS * 0.9}`], // RPS가 목표의 90% 이상 유지되어야 함 + }, +}; + +export function setup() { + console.log(`[지속성 테스트 시작]`); + console.log(`[목표] ${TARGET_VUS} VUs (각 VU가 1초에 1회 요청) = ${TARGET_VUS} RPS`); + console.log(`[목표 성능] p95 < ${TARGET_P95}ms, 에러율 < ${MAX_ERROR_RATE * 100}%`); + console.log(`[타겟] ${targetUrl}`); + console.log(`[테스트 구성]`); + console.log(` - Ramp Up: ${RAMP_UP_TIME} (0 → ${TARGET_VUS} VUs)`); + console.log(` - 유지: ${TEST_DURATION} (${TARGET_VUS} VUs)`); + console.log(` - Ramp Down: 30s (${TARGET_VUS} → 0 VUs)`); + console.log(` - 각 VU: 1초에 1회 요청`); + console.log(` - 총 테스트 시간: ${RAMP_UP_TIME} + ${TEST_DURATION} + 30s`); + return { startTime: new Date() }; +} + +export default function () { + const res = http.get(targetUrl, { + timeout: '10s', + headers: { + 'Connection': 'keep-alive', + 'Accept': 'application/json', + 'User-Agent': 'k6-endurance-test', + }, + }); + + check(res, { + 'status is 200': (r) => r.status === 200, + 'response time < 500ms': (r) => r.timings.duration < 500, + 'response time < 1s': (r) => r.timings.duration < 1000, + }); + + // 각 VU가 1초에 한 번씩 요청하도록 1초 대기 + sleep(1); +} + +export function teardown(data) { + const endTime = new Date(); + const duration = (endTime - data.startTime) / 1000; // 초 단위 + + console.log(`[지속성 테스트 완료]`); + console.log(`[총 테스트 시간] ${duration}초`); + console.log(`[시작 시간] ${data.startTime.toISOString()}`); + console.log(`[종료 시간] ${endTime.toISOString()}`); +} \ No newline at end of file diff --git a/Performance-Test/load-test/health-check.js b/Performance-Test/load-test/health-check.js new file mode 100644 index 0000000..43fefa3 --- /dev/null +++ b/Performance-Test/load-test/health-check.js @@ -0,0 +1,62 @@ +import http from 'k6/http'; +import { check, sleep } from 'k6'; + +const baseUrl = __ENV.K6_TEST_URL; +const targetUrl = `${baseUrl.replace(/\/$/, '')}/health-check`; + +// VU 기반 테스트 설정 +const TARGET_VUS = Number(__ENV.K6_TARGET_VUS || 1000); +const TEST_DURATION = __ENV.K6_TEST_DURATION || '30s'; +const RAMP_UP_TIME = __ENV.K6_RAMP_UP_TIME || '10s'; +const TARGET_P95 = Number(__ENV.K6_TARGET_P95 || 200); +const MAX_ERROR_RATE = Number(__ENV.K6_MAX_ERROR_RATE || 0.01); + +export const options = { + scenarios: { + 'VU-Test': { + executor: 'ramping-vus', + startVUs: 0, + stages: [ + { duration: RAMP_UP_TIME, target: TARGET_VUS }, // RAMP_UP_TIME 걸쳐 TARGET_VUS VU까지 증가 + { duration: TEST_DURATION, target: TARGET_VUS }, // TEST_DURATION동안 TARGET_VUS VU 유지 + { duration: '10s', target: 0 }, // 10초에 걸쳐 0으로 감소 + ], + }, + }, + thresholds: { + 'http_req_duration': [`p(95)<${TARGET_P95}`], + 'http_req_failed': [`rate<${MAX_ERROR_RATE}`], + }, +}; + +export function setup() { + console.log(`[부하 테스트 시작]`); + console.log(`[목표] ${TARGET_VUS} VUs (각 VU가 1초에 1회 요청) = ${TARGET_VUS} RPS`); + console.log(`[목표 성능] p95 < ${TARGET_P95}ms, 에러율 < ${MAX_ERROR_RATE * 100}%`); + console.log(`[타겟] ${targetUrl}`); + console.log(`[테스트 구성]`); + console.log(` - Ramp Up: ${RAMP_UP_TIME} (0 → ${TARGET_VUS} VUs)`); + console.log(` - 유지: ${TEST_DURATION} (${TARGET_VUS} VUs)`); + console.log(` - Ramp Down: 10s (${TARGET_VUS} → 0 VUs)`); + console.log(` - 각 VU: 1초에 1회 요청`); + return { startTime: new Date() }; +} + +export default function () { + const res = http.get(targetUrl, { + timeout: '10s', + headers: { + 'Connection': 'keep-alive', + 'Accept': 'application/json', + 'User-Agent': 'k6-load-test', + }, + }); + + check(res, { + 'status is 200': (r) => r.status === 200, + 'response time < 500ms': (r) => r.timings.duration < 500, + }); + + // 각 VU가 1초에 한 번씩 요청하도록 1초 대기 + sleep(1); +} \ No newline at end of file diff --git a/Performance-Test/spike-test/health-check.js b/Performance-Test/spike-test/health-check.js new file mode 100644 index 0000000..e4dbff4 --- /dev/null +++ b/Performance-Test/spike-test/health-check.js @@ -0,0 +1,46 @@ +import http from 'k6/http'; +import { check, sleep } from 'k6'; + +const baseUrl = __ENV.K6_TEST_URL; +const targetUrl = `${baseUrl.replace(/\/$/, '')}/health-check`; + +// Spike Test Configuration +const SPIKE_VUS = Number(__ENV.K6_SPIKE_VUS || 10000); +const RAMP_UP = __ENV.K6_RAMP_UP || '10s'; +const HOLD = __ENV.K6_HOLD || '30s'; +const RAMP_DOWN = __ENV.K6_RAMP_DOWN || '20s'; + +console.log(`[Spike Test] 시작 - Spike VUs: ${SPIKE_VUS}, Ramp Up: ${RAMP_UP}, Hold: ${HOLD}, Ramp Down: ${RAMP_DOWN}, Target: ${targetUrl}`); + +export const options = { + scenarios: { + spike: { + executor: 'ramping-vus', + stages: [ + { duration: RAMP_UP, target: SPIKE_VUS }, // Sudden spike up + { duration: HOLD, target: SPIKE_VUS }, // Hold at peak + { duration: RAMP_DOWN, target: 0 }, // Rapid decline + ], + }, + }, + thresholds: { + http_req_duration: ['p(95)<1000'], + http_req_failed: ['rate<0.05'], + }, +}; + +export default function () { + const res = http.get(targetUrl, { + tags: { + endpoint: 'health', + test_type: 'spike' + } + }); + + check(res, { + 'status is 200': (r) => r.status === 200, + 'response time < 1s': (r) => r.timings.duration < 1000, + }); + + sleep(1); +} \ No newline at end of file