refactor: k6 스크립트 수정

- 초기 셋업 데이터 로드는 로컬에서 받아오도록 수정
- tag 추가로 ID별 구분이 아닌 API별 구분 집계
This commit is contained in:
이상진 2025-10-16 10:25:31 +09:00
parent e6cfd7b68b
commit 747245d9ac
3 changed files with 22 additions and 19 deletions

View File

@ -22,7 +22,7 @@ export function parseIdToString(response) {
} }
export function maxIterations() { export function maxIterations() {
const maxIterationsRes = http.get(`${BASE_URL}/tests/max-iterations`) const maxIterationsRes = http.get(`http://localhost:8080/tests/max-iterations`)
if (maxIterationsRes.status !== 200) { if (maxIterationsRes.status !== 200) {
throw new Error('max-iterations 조회 실패') throw new Error('max-iterations 조회 실패')
} }
@ -57,7 +57,7 @@ export function login(account, password, principalType) {
password: password, password: password,
principalType: principalType principalType: principalType
}) })
const params = { headers: { 'Content-Type': 'application/json' } } const params = { headers: { 'Content-Type': 'application/json' }, tags: { name: '/auth/login' } }
const loginRes = http.post(`${BASE_URL}/auth/login`, loginPayload, params) const loginRes = http.post(`${BASE_URL}/auth/login`, loginPayload, params)
@ -78,7 +78,7 @@ export function login(account, password, principalType) {
} }
} }
export function getHeaders(token) { export function getHeaders(token, endpoint) {
const headers = { const headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
@ -87,5 +87,5 @@ export function getHeaders(token) {
headers['Authorization'] = `Bearer ${token}`; headers['Authorization'] = `Bearer ${token}`;
} }
return { headers: headers }; return { headers: headers, tags: { name: endpoint } };
} }

View File

@ -15,10 +15,13 @@ export const options = {
scenarios: { scenarios: {
user_reservation: { user_reservation: {
executor: 'ramping-vus', executor: 'ramping-vus',
startVUs: 1500, startVUs: 0,
stages: [ stages: [
{ duration: '10m', target: 1500 }, { duration: '3m', target: 500 },
{ duration: '1m', target: 0 } { duration: '2m', target: 1000 },
{ duration: '2m', target: 1500 },
{ duration: '3m', target: 1500 },
{ duration: '3m', target: 0 },
] ]
} }
}, },
@ -92,7 +95,8 @@ export default function (data) {
while (searchTrial < 5) { while (searchTrial < 5) {
storeId = randomItem(stores).storeId storeId = randomItem(stores).storeId
targetDate = randomDayBetween(1, 7) targetDate = randomDayBetween(1, 7)
const res = http.get(`${BASE_URL}/stores/${storeId}/schedules?date=${targetDate}`) const params = getHeaders(accessToken, "/stores/${storeId}/schedules?date=${date}")
const res = http.get(`${BASE_URL}/stores/${storeId}/schedules?date=${targetDate}`, params)
const result = check(res, {'일정 조회 성공': (r) => r.status === 200}) const result = check(res, {'일정 조회 성공': (r) => r.status === 200})
if (result !== true) { if (result !== true) {
continue continue
@ -118,7 +122,7 @@ export default function (data) {
const randomThemesForFetchDetail = extractRandomThemeForFetchDetail(themesByStoreAndDate) const randomThemesForFetchDetail = extractRandomThemeForFetchDetail(themesByStoreAndDate)
randomThemesForFetchDetail.forEach(id => { randomThemesForFetchDetail.forEach(id => {
http.get(`${BASE_URL}/themes/${id}`) http.get(`${BASE_URL}/themes/${id}`, getHeaders(accessToken, "/themes/${id}"))
sleep(10) sleep(10)
}) })
}) })
@ -137,11 +141,11 @@ export default function (data) {
let isScheduleHeld = false let isScheduleHeld = false
group(`일정 Holding 및 테마 정보 조회 -> 예약 과정중 첫 페이지의 작업 완료`, function () { group(`일정 Holding 및 테마 정보 조회 -> 예약 과정중 첫 페이지의 작업 완료`, function () {
const holdRes = http.post(`${BASE_URL}/schedules/${availableScheduleId}/hold`, null, getHeaders(accessToken)) const holdRes = http.post(`${BASE_URL}/schedules/${availableScheduleId}/hold`, null, getHeaders(accessToken, "/schedules/${id}/hold"))
const body = JSON.parse(holdRes.body) const body = JSON.parse(holdRes.body)
if (check(holdRes, {'일정 점유 성공': (r) => r.status === 200})) { if (check(holdRes, {'일정 점유 성공': (r) => r.status === 200})) {
const themeInfoRes = http.get(`${BASE_URL}/themes/${selectedThemeId}`) const themeInfoRes = http.get(`${BASE_URL}/themes/${selectedThemeId}`, { tag: { name: "/themes/${id}"}})
selectedThemeInfo = parseIdToString(themeInfoRes).data selectedThemeInfo = parseIdToString(themeInfoRes).data
isScheduleHeld = true isScheduleHeld = true
} else { } else {
@ -160,7 +164,7 @@ export default function (data) {
group(`예약 정보 입력 페이지`, function () { group(`예약 정보 입력 페이지`, function () {
let userName, userContact let userName, userContact
group(`회원 연락처 조회`, function () { group(`회원 연락처 조회`, function () {
const userContactRes = http.get(`${BASE_URL}/users/contact`, getHeaders(accessToken)) const userContactRes = http.get(`${BASE_URL}/users/contact`, getHeaders(accessToken, "/users/contact"))
if (!check(userContactRes, {'회원 연락처 조회 성공': (r) => r.status === 200})) { if (!check(userContactRes, {'회원 연락처 조회 성공': (r) => r.status === 200})) {
throw new Error("회원 연락처 조회 과정에서 예외 발생") throw new Error("회원 연락처 조회 과정에서 예외 발생")
@ -191,7 +195,7 @@ export default function (data) {
requirement: requirement requirement: requirement
}) })
const pendingReservationCreateRes = http.post(`${BASE_URL}/reservations/pending`, payload, getHeaders(accessToken)) const pendingReservationCreateRes = http.post(`${BASE_URL}/reservations/pending`, payload, getHeaders(accessToken, "/reservations/pending"))
const responseBody = parseIdToString(pendingReservationCreateRes) const responseBody = parseIdToString(pendingReservationCreateRes)
if (pendingReservationCreateRes.status !== 200) { if (pendingReservationCreateRes.status !== 200) {
@ -229,7 +233,7 @@ export default function (data) {
let isConfirmed = false let isConfirmed = false
while (trial < 2) { while (trial < 2) {
sleep(30) sleep(30)
const confirmOrderRes = http.post(`${BASE_URL}/orders/${reservationId}/confirm`, payload, getHeaders(accessToken)) const confirmOrderRes = http.post(`${BASE_URL}/orders/${reservationId}/confirm`, payload, getHeaders(accessToken, "/orders/${reservationId}/confirm"))
if (check(confirmOrderRes, {'예약 확정 성공': (r) => r.status === 200})) { if (check(confirmOrderRes, {'예약 확정 성공': (r) => r.status === 200})) {
isConfirmed = true isConfirmed = true

View File

@ -1,17 +1,17 @@
import http from 'k6/http'; import http from 'k6/http';
import {check, sleep} from 'k6'; import {check} from 'k6';
import exec from 'k6/execution'; import exec from 'k6/execution';
import {BASE_URL, getHeaders, login, parseIdToString} from "./common.js"; import {BASE_URL, getHeaders, login, parseIdToString} from "./common.js";
import {randomIntBetween} from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; import {randomIntBetween} from 'https://jslib.k6.io/k6-utils/1.2.0/index.js';
const TOTAL_ITERATIONS = 85212; const TOTAL_ITERATIONS = 200000;
export const options = { export const options = {
scenarios: { scenarios: {
schedule_creation: { schedule_creation: {
executor: 'shared-iterations', executor: 'shared-iterations',
vus: 263, vus: 100,
iterations: TOTAL_ITERATIONS, iterations: TOTAL_ITERATIONS,
maxDuration: '30m', maxDuration: '30m',
}, },
@ -97,7 +97,7 @@ function createSchedule(storeId, accessToken, schedule) {
time: schedule.time, time: schedule.time,
themeId: schedule.themeId, themeId: schedule.themeId,
}); });
const params = getHeaders(accessToken) const params = getHeaders(accessToken, "/admin/stores/${id}/schedules")
const res = http.post(`${BASE_URL}/admin/stores/${storeId}/schedules`, payload, params); const res = http.post(`${BASE_URL}/admin/stores/${storeId}/schedules`, payload, params);
const success = check(res, {'일정 생성 성공': (r) => r.status === 200 || r.status === 201}); const success = check(res, {'일정 생성 성공': (r) => r.status === 200 || r.status === 201});
@ -105,7 +105,6 @@ function createSchedule(storeId, accessToken, schedule) {
if (!success) { if (!success) {
console.error(`일정 생성 실패 [${res.status}]: 매장=${storeId}, ${schedule.date} ${schedule.time} (테마: ${schedule.themeId}) | 응답: ${res.body}`); console.error(`일정 생성 실패 [${res.status}]: 매장=${storeId}, ${schedule.date} ${schedule.time} (테마: ${schedule.themeId}) | 응답: ${res.body}`);
} }
sleep(5)
return success; return success;
} }