[Spring] ์ด๋ฉ”์ผ๐Ÿ“ฉ ์ธ์ฆ ์„ฑ๋Šฅ ๊ฐœ์„ ๊ธฐ - Grafana k6 ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ๋„์ž…

juhyeok01ยท2025๋…„ 2์›” 17์ผ

Project

๋ชฉ๋ก ๋ณด๊ธฐ
2/3
post-thumbnail

๐Ÿ’ก ์ด๋ฉ”์ผ ์ธ์ฆ ์†๋„ ๊ฐœ์„ , ์–ด๋–ป๊ฒŒ ์ธก์ •ํ•ด์•ผ ํ• ๊นŒ?

์ง€๋‚œ๋ฒˆ ๊ธ€์—์„œ๋Š” ์ด๋ฉ”์ผ ์ธ์ฆ์„ Map ๋ฐฉ์‹์œผ๋กœ ๊ด€๋ฆฌํ–ˆ์„ ๋•Œ์˜ ๋ฌธ์ œ์ ๊ณผ Redis๋กœ ์ „ํ™˜ํ•˜๋ฉด์„œ ์–ป์€ ์„ฑ๋Šฅ ํ–ฅ์ƒ์— ๋Œ€ํ•ด ๋‹ค๋ค˜๋‹ค.
(ํ˜น์‹œ ๋ชป ๋ดค๋‹ค๋ฉด ์ด์ „ ๊ธ€์„ ๋จผ์ € ๋ณด๊ณ  ์˜ค๋Š” ๊ฑธ ์ถ”์ฒœํ•œ๋‹ค! ๐Ÿง)

ํ•˜์ง€๋งŒ Redis๋กœ ๋ฐ”๊ฟจ๋‹ค๊ณ  ํ•ด์„œ ๋์ด ์•„๋‹ˆ๋‹ค. ์ •๋ง ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋˜์—ˆ๋Š”์ง€, ๋ณ‘๋ชฉ์€ ์—†๋Š”์ง€ ๊ฐ๊ด€์ ์ธ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

๊ทธ๋ž˜์„œ ์ด๋ฒˆ์—๋Š” "Grafana k6"๋ผ๋Š” ๊ฐ•๋ ฅํ•œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฉ”์ผ ์ธ์ฆ ๋กœ์ง์„ ์ •๋ฐ€ํ•˜๊ฒŒ ๋ถ„์„ํ•ด๋ดค๋‹ค!

๐ŸŽฏ 1. ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋กœ "Grafana k6"๋ฅผ ์„ ํƒํ•œ ์ด์œ 


โœ… ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ๋„๊ตฌ, ์™œ ํ•„์š”ํ• ๊นŒ?

์ด๋ฉ”์ผ ์ธ์ฆ ๋กœ์ง์„ ๊ฐœ์„ ํ•  ๋•Œ, ๋‹จ์ˆœํžˆ "์ฒด๊ฐ ์†๋„๊ฐ€ ๋นจ๋ผ์กŒ๋‹ค!"๋ผ๊ณ  ๋งํ•  ์ˆ˜๋Š” ์—†๋‹ค.

  • ์–ผ๋งˆ๋‚˜ ๋นจ๋ผ์กŒ๋Š”์ง€?

  • ๋ถ€ํ•˜๊ฐ€ ๊ฑธ๋ฆฌ๋ฉด ์†๋„๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ €ํ•˜๋˜๋Š”์ง€?

  • ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€?

์ด๋Ÿฐ ๊ฒƒ๋“ค์„ ์ˆ˜์น˜๋กœ ํ™•์ธํ•ด์•ผ ์ œ๋Œ€๋กœ ๋œ ๊ฐœ์„ ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค. ๐Ÿ“Š

๐Ÿš€ k6๊ฐ€ ๋‹ค๋ฅธ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ํˆด๋ณด๋‹ค ์ข‹์€ ์ด์œ 

์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ํˆด์€ JMeter, Locust, Gatling ๋“ฑ ๋‹ค์–‘ํ•œ๋ฐ, ๋‚˜๋Š” k6๋ฅผ ์„ ํƒํ–ˆ๋‹ค.

  • ๊ฐ„๊ฒฐํ•œ ์Šคํฌ๋ฆฝํŠธ - JMeter์ฒ˜๋Ÿผ ๋ณต์žกํ•œ GUI ์—†์ด JavaScript ๊ธฐ๋ฐ˜์˜ ์ฝ”๋“œ๋กœ ์‰ฝ๊ฒŒ ์ž‘์„ฑ ๊ฐ€๋Šฅ โœ๏ธ

  • ๊ณ ์„ฑ๋Šฅ - ๊ฐ€๋ฒผ์šด Go ์–ธ์–ด๋กœ ๊ตฌํ˜„๋˜์–ด ์ˆ˜์ฒœ ๊ฐœ์˜ ์š”์ฒญ์„ ๊ฐ€๋ณ๊ฒŒ ์ฒ˜๋ฆฌ ๐Ÿš€

  • ์‹ค์‹œ๊ฐ„ ๊ฒฐ๊ณผ ๋ถ„์„ - ์‹คํ–‰ ์ค‘์—๋„ Grafana ๋Œ€์‹œ๋ณด๋“œ์™€ ์—ฐ๋™ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์‹œ๊ฐํ™” ๊ฐ€๋Šฅ ๐Ÿ“ˆ

  • CI/CD ์—ฐ๋™ ์šฉ์ด - GitHub Actions ๋“ฑ๊ณผ ์‰ฝ๊ฒŒ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด ์ž๋™ํ™”๋œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ โšก

  • ๊ฒฐ๋ก : ๊ฐ„ํŽธํ•˜๋ฉด์„œ๋„ ๊ฐ•๋ ฅํ•œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” "k6"๊ฐ€ ์ด๋ฉ”์ผ ์ธ์ฆ ๋กœ์ง ๊ฐœ์„ ์— ์ตœ์ ์ด์—ˆ๋‹ค!

๐Ÿ”ง 2. k6 ์„ค์น˜ ๋ฐ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•



๐Ÿ› ๏ธ ์„ค์น˜ ๋ฐฉ๋ฒ• (Mac / Linux / Windows ์ง€์›)


๐Ÿ“Œ Mac (Homebrew ์‚ฌ์šฉ)

brew install k6

๐Ÿ“Œ Linux (Debian/Ubuntu)

sudo gpg -k | grep cloudflare
curl -fsSL https://dl.k6.io/key.gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/k6.gpg
sudo apt update && sudo apt install k6

๐Ÿ“Œ Windows (Chocolatey ์‚ฌ์šฉ)

choco install k6

์„ค์น˜๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์œผ๋ฉด, k6 version ๋ช…๋ น์–ด๋กœ ์ •์ƒ์ ์œผ๋กœ ์„ค์น˜๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜์ž!

k6 version
# k6 v0.56.0 (commit/50afb99947, go1.23.4, windows/amd64)

๐Ÿ“œ 3. ์ด๋ฉ”์ผ ์ธ์ฆ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑํ•˜๊ธฐ


k6 ์Šคํฌ๋ฆฝํŠธ๋Š” JavaScript ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋œ๋‹ค. ๋‚˜๋Š” ์ด๋ฉ”์ผ ์ธ์ฆ API๋ฅผ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑํ–ˆ๋‹ค. ๐Ÿ”

import http from 'k6/http';
import { check, sleep, Trend } from 'k6';

let sendMailTrend = new Trend('send_mail_duration');
let verifyCodeTrend = new Trend('verify_code_duration');

export let options = {
  vus: 2,         // ๋™์‹œ์— ์‹คํ–‰๋  ์‚ฌ์šฉ์ž ์ˆ˜ (SMTP ์„œ๋ฒ„ ๋ถ€ํ•˜ ๋ฐฉ์ง€)
  duration: '1m',  // 1๋ถ„ ๋™์•ˆ ํ…Œ์ŠคํŠธ ์‹คํ–‰
};

export default function () {
  let uniqueEmail = `test+${__VU}@kumoh.ac.kr`;
  let sendStart = new Date();

  let sendRes = http.post('http://localhost:8080/register/mail', JSON.stringify({
    username: uniqueEmail
  }), { headers: { 'Content-Type': 'application/json' } });
  let sendDuration = new Date() - sendStart;
  sendMailTrend.add(sendDuration);

  console.log(`๐Ÿ“ฉ Send API Response: ${sendRes.status} - ${sendRes.body}`);
  check(sendRes, { 'โœ… send status is 200': (r) => r.status === 200 });

  sleep(Math.random() * 5 + 3);  // 3~8์ดˆ ๋žœ๋ค ๋Œ€๊ธฐ

  let verifyStart = new Date();
  let verifyRes = http.post('http://localhost:8080/register/mail/verification', JSON.stringify({
    username: uniqueEmail,
    mailCode: 123456
  }), { headers: { 'Content-Type': 'application/json' } });
  let verifyDuration = new Date() - verifyStart;
  verifyCodeTrend.add(verifyDuration);

  console.log(`๐Ÿ”‘ Verify API Response: ${verifyRes.status} - ${verifyRes.body}`);
  check(verifyRes, { '๐Ÿ”‘ verify status is 200': (r) => r.status === 200 });

  sleep(1);
}

๐Ÿ“Š 4. k6 ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋ฐ ๊ฒฐ๊ณผ ๋ถ„์„


โœ… ์‹คํ–‰ ๋ฐฉ๋ฒ•

k6 run test.js

๐Ÿ“ˆ ์‹คํ–‰ ๊ฒฐ๊ณผ (Redis vs Java ๋‚ด์žฅ HashMap ๋น„๊ต)

Metric Redis ์‚ฌ์šฉ HashMap ์‚ฌ์šฉ
์š”์ฒญ ์„ฑ๊ณต๋ฅ  โœ… 100% โœ… 100%
ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„ 13.56ms 31.83ms
์ตœ๋Œ€ ์‘๋‹ต ์‹œ๊ฐ„ 26.49ms 692.69ms
์š”์ฒญ ๋Œ€๊ธฐ ์‹œ๊ฐ„ 12.19ms 30.04ms
๋ฉ”์ผ ์ „์†ก ์‹œ๊ฐ„ 15.35ms 53.45ms

๐Ÿ“Œ ๊ฒฐ๋ก : Redis ์‚ฌ์šฉ ์‹œ ์‘๋‹ต ์†๋„๊ฐ€ 2~3๋ฐฐ ๋น ๋ฅด๊ณ , ๊ทน๋‹จ์ ์ธ ์ง€์—ฐ(692ms)์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜๋‹ค. ๐Ÿš€


๐Ÿš€ 5. ๋‹ค์Œ ๋‹จ๊ณ„?

์ด๋ฒˆ์—๋Š” ๋‹จ์ˆœํžˆ POST ์š”์ฒญ์„ ๋ณด๋‚ด์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€๋‹ค!

๊ทธ๋Ÿฌ๋‚˜ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ๋‹จ์ˆœํžˆ ๋‹จ์ผ POST ๋ฐ˜๋ณต ์š”์ฒญ์œผ๋กœ ์ง„ํ–‰ํ•˜๋ฉด ์บ์‹œ ์ ์ค‘๋ฅ ์ด ๋†’์•„์ ธ์„œ ์‹ค์ œ ์„œ๋น„์Šค ๋ถ€ํ•˜๋ฅผ ์ œ๋Œ€๋กœ ์ธก์ •ํ•˜๊ธฐ ์–ด๋ ค์›Œ, ๋”ฐ๋ผ์„œ ๋‹ค์–‘ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ ์šฉํ•œ ํ…Œ์ŠคํŠธ ์ „๋žต์„ ์„ธ์šฐ๋Š”๊ฒŒ ์ค‘์š”ํ•˜๋‹ค!

๊ทธ๋ž˜์„œ ๋‹ค์Œ ํฌ์ŠคํŒ…์—๋Š” ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค ์ „๋žต์„ ์ ์šฉํ•ด์„œ Grafna๋ฅผ ์ด์šฉํ•ด์„œ k6 ์‹œ๊ฐํ™”๋ฅผ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

(๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ ๊ณ„์†)

profile
๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ฅผ ์ง€๋งํ•˜๋Š” ์ปดํ“จํ„ฐ๊ณตํ•™๊ณผ 4ํ•™๋…„ ํ•™์ƒ์ž…๋‹ˆ๋‹ค https://github.com/Juhye0k

0๊ฐœ์˜ ๋Œ“๊ธ€