๐Ÿ’ป PROJECT/[Spring Boot, React] ๋…์„œ ์Šต๊ด€ ๊ด€๋ฆฌ ์„œ๋น„์Šค

k6 ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋กœ t2.micro ์„œ๋ฒ„ ํ•œ๊ณ„์™€ ๋ณ‘๋ชฉ ๋ถ„์„ํ•˜๊ธฐ

devCloud 2026. 8. 3. 16:48
728x90
 
LOAD TEST

k6 ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋กœ t2.micro ์„œ๋ฒ„ ํ•œ๊ณ„ ๊ตฌ๊ฐ„ ๋ถ„์„ํ•˜๊ธฐ

1. ํ…Œ์ŠคํŠธ ๋ชฉ์ ๊ณผ ํ™˜๊ฒฝ

Booktine์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„๋Š” AWS EC2 t2.micro ๋‹จ์ผ ์ธ์Šคํ„ด์Šค์—์„œ ์šด์˜๋˜๊ณ  ์žˆ์œผ๋ฉฐ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๋ณ„๋„์˜ Amazon RDS MySQL์„ ์‚ฌ์šฉํ•œ๋‹ค. ๋ฐฐํฌ ํ›„ ํ˜„์žฌ ํ™˜๊ฒฝ์ด ์–ด๋А ์ •๋„์˜ ๋ถ€ํ•˜๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•ด k6 ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค. ๋‹จ์ˆœํžˆ ์„œ๋ฒ„๊ฐ€ ์ค‘๋‹จ๋˜๋Š”์ง€๋งŒ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๊ฐ€์ƒ ์‚ฌ์šฉ์ž ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•  ๋•Œ ์‘๋‹ต์‹œ๊ฐ„์ด ๊ธ‰๊ฒฉํžˆ ์•…ํ™”๋˜๋Š” ๊ตฌ๊ฐ„์„ ์ฐพ๊ณ  ๋ณ‘๋ชฉ ๊ฐ€๋Šฅ์„ฑ์„ ์ขํžˆ๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ–ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ํ™˜๊ฒฝ์€ 1 vCPU์™€ 1GB RAM์„ ์ œ๊ณตํ•˜๋Š” t2.micro๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ตฌ์„ฑํ–ˆ๋‹ค. Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ Docker ์ปจํ…Œ์ด๋„ˆ๋กœ ์‹คํ–‰ํ–ˆ๊ณ  Redis๋Š” ๊ฐ™์€ EC2 ์ธ์Šคํ„ด์Šค์—์„œ ์‚ฌ์šฉํ–ˆ๋‹ค. MySQL์€ Amazon RDS์— ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์ด๋ฒˆ ๊ฒฐ๊ณผ๋Š” ์ฃผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„์˜ ์ œํ•œ๋œ CPU·๋ฉ”๋ชจ๋ฆฌ ํ™˜๊ฒฝ๊ณผ ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ ๋น„์šฉ์„ ํฌํ•จํ•œ๋‹ค.

ํ•ญ๋ชฉ ๋‚ด์šฉ
์ธ์Šคํ„ด์Šค AWS EC2 t2.micro
CPU 1 vCPU
๋ฉ”๋ชจ๋ฆฌ 1GB RAM
JVM ํž™ -Xms256m -Xmx512m
๊ตฌ์„ฑ Spring Boot(Docker) + Redis(EC2), MySQL(Amazon RDS)

๋ถ€ํ•˜ ์ƒ์„ฑ ๋„๊ตฌ๋Š” JMeter ๋Œ€์‹  k6๋ฅผ ์„ ํƒํ–ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” JMeter๋ฅผ ๊ณ ๋ คํ–ˆ์ง€๋งŒ JVM ๊ธฐ๋ฐ˜ ๋„๊ตฌ๋ฅผ ํ…Œ์ŠคํŠธ ํด๋ผ์ด์–ธํŠธ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋กœ์ปฌ ์ž์› ์‚ฌ์šฉ๋Ÿ‰์ด ์ปค์ ธ ๊ฒฐ๊ณผ๊ฐ€ ์™œ๊ณก๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์—ˆ๋‹ค. JMeter์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” XML ๊ธฐ๋ฐ˜ JMX ํŒŒ์ผ๋กœ ๊ด€๋ฆฌ๋˜์–ด ํ…Œ์ŠคํŠธ ํ๋ฆ„์ด ๋Š˜์–ด๋‚ ์ˆ˜๋ก ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ๋Š” ๋ฐ˜๋ฉด, k6๋Š” Go ๊ธฐ๋ฐ˜์œผ๋กœ ๋น„๊ต์  ๊ฐ€๋ณ๊ณ  JavaScript๋กœ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์•„์ง JMX ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜๊ธฐ ์ „์ด์–ด์„œ ์ „ํ™˜ ๋น„์šฉ๋„ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— k6๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ „์— JVM ํž™๋„ ๋ช…์‹œ์ ์œผ๋กœ ์ œํ•œํ–ˆ๋‹ค. ๊ธฐ์กด ๋ฐฐํฌ ์„ค์ •์—๋Š” ํž™ ์˜ต์…˜์ด ์—†์—ˆ๊ณ  ์œ ํœด ์ƒํƒœ์—์„œ๋„ ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด 428MB์— ๋„๋‹ฌํ•ด ์žˆ์—ˆ๋‹ค. 1GB RAM ํ™˜๊ฒฝ์—์„œ ๋ถ€ํ•˜๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉด OOM์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์–ด Dockerfile์˜ ์‹คํ–‰ ์˜ต์…˜์„ -Xms256m, -Xmx512m๋กœ ์„ค์ •ํ–ˆ๋‹ค.

# ๋ณ€๊ฒฝ ์ „
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

# ๋ณ€๊ฒฝ ํ›„
ENTRYPOINT ["java", "-Xms256m", "-Xmx512m", "-jar", "/app/app.jar"]

ํž™ ์˜ต์…˜์„ ์ ์šฉํ•˜๊ณ  ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์žฌ์‹œ์ž‘ํ•œ ๋’ค ์ธก์ •ํ•œ ์œ ํœด ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์€ 428MB์—์„œ 323MB๋กœ ๊ฐ์†Œํ–ˆ๋‹ค. ๋‹ค๋งŒ ์ด ์ฐจ์ด์—๋Š” ์žฌ์‹œ์ž‘ ์‹œ์ ์˜ GC ์ƒํƒœ์™€ ์‹คํ–‰ ํ™˜๊ฒฝ ๋ณ€ํ™”๊ฐ€ ํ•จ๊ป˜ ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ํž™ ์„ค์ •๋งŒ์˜ ํšจ๊ณผ๋ผ๊ณ  ๋‹จ์ •ํ•˜์ง€๋Š” ์•Š์•˜๋‹ค. ๋˜ํ•œ -Xmx512m์€ JVM ํž™ ์ƒํ•œ์ด๋ฉฐ ์ปจํ…Œ์ด๋„ˆ ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ ์ƒํ•œ์€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ๋๋‚  ๋•Œ๋งˆ๋‹ค docker stats๋กœ ์ „์ฒด ์ปจํ…Œ์ด๋„ˆ ๋ฉ”๋ชจ๋ฆฌ ์ถ”์ด๋„ ํ•จ๊ป˜ ํ™•์ธํ–ˆ๋‹ค.

 

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ณ‘๋ชฉ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด MySQL Slow Query Log๋„ ํ™œ์„ฑํ™”ํ–ˆ๋‹ค. RDS์—์„œ๋Š” SUPER ๊ถŒํ•œ ์ œํ•œ์œผ๋กœ SQL์„ ํ†ตํ•ด ์ง์ ‘ ์„ค์ •ํ•˜๊ธฐ ์–ด๋ ค์›Œ AWS ์ฝ˜์†”์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ทธ๋ฃน์—์„œ slow_query_log๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ , 1์ดˆ ์ด์ƒ ์‹คํ–‰๋˜๋Š” ์ฟผ๋ฆฌ๋ฅผ ๊ธฐ๋กํ•˜๋„๋ก long_query_time์„ ์„ค์ •ํ–ˆ๋‹ค.

ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’
slow_query_log 1
long_query_time 1

Slow Query Log๋Š” 1์ดˆ๋ฅผ ์ดˆ๊ณผํ•˜๋Š” ๊ฐœ๋ณ„ SQL์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ๋ณด์กฐ ์ง€ํ‘œ๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค. ์ด ๋กœ๊ทธ๋งŒ์œผ๋กœ ์งง์€ ์ฟผ๋ฆฌ์˜ ๋ฐ˜๋ณต ์‹คํ–‰, ์ปค๋„ฅ์…˜ ํ’€ ๋Œ€๊ธฐ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค CPU ์‚ฌ์šฉ๋Ÿ‰๊นŒ์ง€ ๋ฐฐ์ œํ•  ์ˆ˜๋Š” ์—†์œผ๋ฏ€๋กœ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ์™€ ํ•จ๊ป˜ ํ•ด์„ํ–ˆ๋‹ค. ํ†ต๊ณ„ API๋Š” ํŽ˜์ด์ง€๋„ค์ด์…˜ ์—†์ด ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ตฌ์กฐ์˜€๊ธฐ ๋•Œ๋ฌธ์—, ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ๊ณผ ๋น„์Šทํ•œ ๋ถ€ํ•˜๊ฐ€ ๋ฐœ์ƒํ•˜๋„๋ก ์ถฉ๋ถ„ํ•œ ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋„ ์‚ฌ์ „์— ์‚ฝ์ž…ํ–ˆ๋‹ค.

2. ์‹œ๋‚˜๋ฆฌ์˜ค ์„ค๊ณ„

๋‹จ๊ณ„๋ณ„ ๋ถ€ํ•˜ ์„ค๊ณ„

๋ถ€ํ•˜ํ…Œ์ŠคํŠธ์—์„œ ๋ชจ๋“  API๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋™์‹œ์— ์‹คํ–‰ํ•˜๋ฉด ์‘๋‹ต์‹œ๊ฐ„์ด ์ฆ๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์–ด๋А ๊ธฐ๋Šฅ์ด ์›์ธ์ธ์ง€ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์–ด๋ ต๋‹ค. ๊ทธ๋ž˜์„œ ์‹ค์ œ ์‚ฌ์šฉ์ž์˜ ํ–‰๋™ ํ๋ฆ„์„ ๊ธฐ์ค€์œผ๋กœ ๋„ค ๊ฐœ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋‚˜๋ˆ„๊ณ , ๊ฐ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋‹จ๋…์œผ๋กœ ์‹คํ–‰ํ•œ ๋’ค ๋งˆ์ง€๋ง‰์— ํ†ตํ•ฉ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์„ค๊ณ„ํ–ˆ๋‹ค. ๋‹จ๋… ํ…Œ์ŠคํŠธ์—์„œ๋Š” ๊ธฐ๋Šฅ๋ณ„ ํŠน์„ฑ์„ ํ™•์ธํ•˜๊ณ , ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์ด ๋™์‹œ์— ํ˜ธ์ถœ๋˜๋Š” ์‹ค์ œ ์šด์˜ ์ƒํ™ฉ์„ ์žฌํ˜„ํ•˜๊ณ ์ž ํ–ˆ๋‹ค.

๊ฐ€์ƒ ์‚ฌ์šฉ์ž(VU) ์ˆ˜๋„ ํ•œ ๋ฒˆ์— 100์ด๋‚˜ 150์œผ๋กœ ์˜ฌ๋ฆฌ์ง€ ์•Š๊ณ  5, 20, 50, 100, 150 ์ˆœ์„œ๋กœ ๋‹จ๊ณ„์ ์œผ๋กœ ์ฆ๊ฐ€์‹œ์ผฐ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•ด์•ผ ์–ด๋А VU ๊ตฌ๊ฐ„๊นŒ์ง€ ์‘๋‹ต์‹œ๊ฐ„๊ณผ ์‹คํŒจ์œจ์ด ์•ˆ์ •์ ์œผ๋กœ ์œ ์ง€๋˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ , ์„ฑ๋Šฅ์ด ๊ธ‰๊ฒฉํžˆ ์•…ํ™”๋˜๋Š” ๊ฒฝ๊ณ„๋„ ํŠน์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ ๋‹จ๊ณ„๋Š” Warm-up, Light, Normal, Heavy, Over๋กœ ๊ตฌ๋ถ„ํ–ˆ๋‹ค.

๋‹จ๊ณ„ ๊ฐ€์ƒ ์‚ฌ์šฉ์ž(VU) ์œ ์ง€ ์‹œ๊ฐ„ ๋ชฉ์ 
Warm-up 5 VU 1๋ถ„ ์„œ๋ฒ„ JVM warm-up, ์ •์ƒ ๋™์ž‘ ํ™•์ธ
Light 20 VU 3๋ถ„ ๊ธฐ๋ณธ ๋ถ€ํ•˜, ์ฝ”๋“œ ๋ ˆ๋ฒจ ์ด์Šˆ ํ™•์ธ
Normal 50 VU 5๋ถ„ ์‹ค ์‚ฌ์šฉ ์˜ˆ์ƒ ๊ตฌ๊ฐ„, ํ•ต์‹ฌ ์ธก์ •
Heavy 100 VU 5๋ถ„ ๋ชฉํ‘œ ์ƒํ•œ์„ , ํ•œ๊ณ„ ๋„๋‹ฌ ์—ฌ๋ถ€ ๊ด€์ฐฐ
Over 150 VU 3๋ถ„ ์˜๋„์  ํ•œ๊ณ„ ํƒ์ƒ‰

k6์—์„œ๋Š” stages ์˜ต์…˜์œผ๋กœ VU ์ˆ˜์™€ ์œ ์ง€ ์‹œ๊ฐ„์„ ์„ค์ •ํ–ˆ๊ณ , P95 ์‘๋‹ต์‹œ๊ฐ„ 2,000ms ๋ฏธ๋งŒ๊ณผ HTTP ์š”์ฒญ ์‹คํŒจ์œจ 1% ๋ฏธ๋งŒ์„ threshold๋กœ ์ง€์ •ํ–ˆ๋‹ค.

export const options = {
  stages: [
    { duration: '1m', target: 5 },   // Warm-up
    { duration: '3m', target: 20 },  // Light
    { duration: '5m', target: 50 },  // Normal
    { duration: '5m', target: 100 }, // Heavy
    { duration: '3m', target: 150 }, // Over
    { duration: '1m', target: 0 },   // ์ข…๋ฃŒ
  ],
  thresholds: {
    http_req_duration: ['p(95)<2000'],
    http_req_failed: ['rate<0.01'],
  },
};

์‚ฌ์šฉ์ž ์‹œ๋‚˜๋ฆฌ์˜ค

์‹œ๋‚˜๋ฆฌ์˜ค๋Š” ๋…์„œ ๊ธฐ๋ก ์กฐํšŒ, ๋…์„œ ๊ธฐ๋ก ์ž‘์„ฑ, ํ†ต๊ณ„ ๋ฐ ๋ชฉํ‘œ ํ™•์ธ, ์ปค๋ฎค๋‹ˆํ‹ฐ ํƒ์ƒ‰์˜ ๋„ค ๊ฐ€์ง€๋กœ ๊ตฌ์„ฑํ–ˆ๋‹ค. ๋‹จ์ˆœํžˆ ๋‹จ์ผ API๋ฅผ ๋ฐ˜๋ณต ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ๋กœ๊ทธ์ธ๋ถ€ํ„ฐ ์‹ค์ œ ๊ธฐ๋Šฅ ์‚ฌ์šฉ๊นŒ์ง€ ์ด์–ด์ง€๋Š” ํ๋ฆ„์œผ๋กœ ์ž‘์„ฑํ•ด ์ธ์ฆ ์ฒ˜๋ฆฌ, ๋ฐ์ดํ„ฐ ์กฐํšŒ, ์ €์žฅ, ์—ฐ๊ด€ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ์ด ํ•จ๊ป˜ ๋ฐœ์ƒํ•˜๋„๋ก ํ–ˆ๋‹ค.

์‹œ๋‚˜๋ฆฌ์˜ค ํ๋ฆ„
A (๋…์„œ ๊ธฐ๋ก ์กฐํšŒ) ๋กœ๊ทธ์ธ → ๋‚ด ์ •๋ณด → ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก → ๋‹จ๊ฑด ์กฐํšŒ → ๋ฉ”๋ชจ ์กฐํšŒ
B (๋…์„œ ๊ธฐ๋ก ์ž‘์„ฑ) ๋กœ๊ทธ์ธ → ๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ → ๋ฉ”๋ชจ ์ž‘์„ฑ
C (ํ†ต๊ณ„/๋ชฉํ‘œ ํ™•์ธ) ๋กœ๊ทธ์ธ → ๊ธฐ๋ณธ ํ†ต๊ณ„ → ์žฅ๋ฅด๋ณ„ ํ†ต๊ณ„ → ์—ฐ๊ฐ„ ์™„๋… ์ˆ˜ → ์—ฐ๊ฐ„ ์š”์•ฝ → ์›”๊ฐ„/์—ฐ๊ฐ„ ๋ชฉํ‘œ
D (์ปค๋ฎค๋‹ˆํ‹ฐ ํƒ์ƒ‰) ๋กœ๊ทธ์ธ → ์ปค๋ฎค๋‹ˆํ‹ฐ ๋ชฉ๋ก → ๋‹จ๊ฑด ์กฐํšŒ → ๋Œ“๊ธ€ ์กฐํšŒ → ์ข‹์•„์š” → ์ข‹์•„์š” ์ทจ์†Œ

๋ชจ๋“  ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” ๋กœ๊ทธ์ธ ์‘๋‹ต์—์„œ accessToken์„ ์ถ”์ถœํ•œ ๋’ค ์ดํ›„ ์š”์ฒญ์— Bearer ํ—ค๋”๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ณตํ†ต ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ ์‹œ๋‚˜๋ฆฌ์˜ค A๋Š” ๋กœ๊ทธ์ธ ํ›„ ๋‚ด ์ •๋ณด์™€ ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์„ ์กฐํšŒํ•˜๊ณ , ๋ชฉ๋ก์—์„œ ์–ป์€ ๊ฒŒ์‹œ๊ธ€ ID๋กœ ๋‹จ๊ฑด ์กฐํšŒ์™€ ๋ฉ”๋ชจ ๋ชฉ๋ก ์กฐํšŒ๋ฅผ ์ด์–ด์„œ ์ˆ˜ํ–‰ํ•œ๋‹ค.

export default function () {
  const token = login();
  const headers = authHeaders(token);

  const meRes = http.get(`${BASE_URL}/users/me`, headers);
  check(meRes, { '๋‚ด ์ •๋ณด ์กฐํšŒ ์„ฑ๊ณต': (r) => r.status === 200 });
  sleep(0.5);

  const postsRes = http.get(`${BASE_URL}/posts`, headers);
  check(postsRes, { '๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก ์กฐํšŒ ์„ฑ๊ณต': (r) => r.status === 200 });

  const posts = JSON.parse(postsRes.body);
  if (posts.data && posts.data.length > 0) {
    const postId = posts.data[0].id;

    const postRes = http.get(`${BASE_URL}/posts/${postId}`, headers);
    check(postRes, { '๊ฒŒ์‹œ๊ธ€ ๋‹จ๊ฑด ์กฐํšŒ ์„ฑ๊ณต': (r) => r.status === 200 });
    sleep(0.5);

    const memosRes = http.get(`${BASE_URL}/posts/${postId}/memos`, headers);
    check(memosRes, { '๋ฉ”๋ชจ ๋ชฉ๋ก ์กฐํšŒ ์„ฑ๊ณต': (r) => r.status === 200 });
  }
  sleep(1);
}

3. ์ธก์ • ์ง€ํ‘œ

์ด๋ฒˆ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„๋งŒ์œผ๋กœ ์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ํŒ๋‹จํ•˜์ง€ ์•Š์•˜๋‹ค. ํ‰๊ท ๊ฐ’์€ ์ผ๋ถ€ ๋งค์šฐ ๋А๋ฆฐ ์š”์ฒญ์ด๋‚˜ ๋งค์šฐ ๋น ๋ฅธ ์š”์ฒญ์˜ ์˜ํ–ฅ์„ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ ์‚ฌ์šฉ์ž๊ฐ€ ์ฒด๊ฐํ•˜๋Š” ์ง€์—ฐ์„ ์ถฉ๋ถ„ํžˆ ์„ค๋ช…ํ•˜์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ผ๋ฐ˜์ ์ธ ์š”์ฒญ์˜ ์‘๋‹ต์‹œ๊ฐ„์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด P50์„, ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒฝํ—˜ํ•˜๋Š” ์ƒ์œ„ ์ง€์—ฐ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด P95๋ฅผ ํ•จ๊ป˜ ์ธก์ •ํ–ˆ๋‹ค.

P50์€ ์ „์ฒด ์š”์ฒญ์˜ 50%๊ฐ€ ํ•ด๋‹น ์‹œ๊ฐ„ ์•ˆ์— ์ฒ˜๋ฆฌ๋๋‹ค๋Š” ์˜๋ฏธ๋กœ, ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ์ž์˜ ์‘๋‹ต ๊ฒฝํ—˜์„ ๋ณด์—ฌ์ค€๋‹ค. P95๋Š” ์ „์ฒด ์š”์ฒญ์˜ 95%๊ฐ€ ํ•ด๋‹น ์‹œ๊ฐ„ ์•ˆ์— ์ฒ˜๋ฆฌ๋๋‹ค๋Š” ์˜๋ฏธ์ด๋ฉฐ, ์ผ๋ถ€ ์ง€์—ฐ ์š”์ฒญ๊นŒ์ง€ ํฌํ•จํ•œ ์„œ๋น„์Šค ํ’ˆ์งˆ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฒˆ ํ…Œ์ŠคํŠธ์˜ ํ•ต์‹ฌ ์ง€ํ‘œ๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค. ๋ชฉํ‘œ๊ฐ’์€ 2,000ms ๋ฏธ๋งŒ์œผ๋กœ ์„ค์ •ํ–ˆ๋‹ค.

RPS๋Š” k6์˜ http_reqs๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ณ„์‚ฐํ•œ ์ดˆ๋‹น HTTP ์š”์ฒญ ์ˆ˜๋‹ค. ํ•œ ๋ฒˆ์˜ ์‚ฌ์šฉ์ž ์‹œ๋‚˜๋ฆฌ์˜ค ์•ˆ์—์„œ ์—ฌ๋Ÿฌ API๋ฅผ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ, ์ด ๊ฐ’์€ ๋น„์ฆˆ๋‹ˆ์Šค ํŠธ๋žœ์žญ์…˜ ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ๋Ÿ‰์„ ์˜๋ฏธํ•œ๋‹ค. HTTP ์š”์ฒญ ์‹คํŒจ์œจ์€ ์ „์ฒด HTTP ์š”์ฒญ ์ค‘ k6๊ฐ€ ์‹คํŒจ๋กœ ํŒ์ •ํ•œ ์š”์ฒญ์˜ ๋น„์œจ๋กœ 1% ๋ฏธ๋งŒ์„ ๋ชฉํ‘œ๋กœ ์„ค์ •ํ–ˆ๋‹ค. ๋˜ํ•œ ๊ฐ ์‹œ๋‚˜๋ฆฌ์˜ค ์ข…๋ฃŒ ํ›„ ์ปจํ…Œ์ด๋„ˆ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ธก์ •ํ•ด -Xmx512m ์ œํ•œ ์•ˆ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ์•ˆ์ •์ ์œผ๋กœ ์œ ์ง€๋˜๋Š”์ง€๋„ ํ™•์ธํ–ˆ๋‹ค.

์ง€ํ‘œ ๋ชฉํ‘œ๊ฐ’
ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ < 1,000ms
P95 ์‘๋‹ต์‹œ๊ฐ„ < 2,000ms
HTTP ์š”์ฒญ ์‹คํŒจ์œจ < 1%
RPS > 20

4. ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ค€๋น„

์‹ค์ œ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๊ฐ€ ํ…Œ์ŠคํŠธ ๊ณผ์ •์—์„œ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋„๋ก ์ „์šฉ ๊ณ„์ •์„ ๋ณ„๋„๋กœ ์ƒ์„ฑํ–ˆ๋‹ค. ์„œ๋น„์Šค์—๋Š” ์ด๋ฉ”์ผ ์ธ์ฆ ์ ˆ์ฐจ๊ฐ€ ์žˆ์–ด ํšŒ์›๊ฐ€์ž… API๋งŒ์œผ๋กœ ๊ณ„์ •์„ ๋งŒ๋“ค๊ธฐ ์–ด๋ ค์› ๊ธฐ ๋•Œ๋ฌธ์— EC2์— SSH๋กœ ์ ‘์†ํ•œ ๋’ค BCrypt ๋น„๋ฐ€๋ฒˆํ˜ธ ํ•ด์‹œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  MySQL์— ์ง์ ‘ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ–ˆ๋‹ค.

BCrypt ํ•ด์‹œ๋Š” ๋‹ค์Œ ๋ช…๋ น์œผ๋กœ ์ƒ์„ฑํ–ˆ๋‹ค.

python3 -c 'import bcrypt; pw = b"<REDACTED_PASSWORD>"; print(bcrypt.hashpw(pw, bcrypt.gensalt(10)).decode())'

์ƒ์„ฑํ•œ ํ•ด์‹œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ์ „์šฉ ์‚ฌ์šฉ์ž๋ฅผ ๋“ฑ๋กํ–ˆ๋‹ค. ๊ฒŒ์‹œ๊ธ€์—๋Š” ์‹ค์ œ ์šด์˜์— ์‚ฌ์šฉํ•œ ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋…ธ์ถœํ•˜์ง€ ์•Š๊ณ  ์˜ˆ์‹œ๊ฐ’์œผ๋กœ ๋Œ€์ฒดํ–ˆ๋‹ค.

INSERT INTO users (created_at, updated_at, auth_provider, email, email_verified, nickname, password, role, suspended)
VALUES (
  NOW(), NOW(), 'LOCAL',
  'loadtest@example.com', b'1',
  'loadtest', '{BCrypt_ํ•ด์‹œ๊ฐ’}',
  'ROLE_USER', b'0'
);

ํ†ต๊ณ„ API์™€ ๋ชฉ๋ก ์กฐํšŒ๊ฐ€ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋Ÿ‰์—์„œ ๋™์ž‘ํ•˜๋„๋ก ์‹œ๋“œ ๋ฐ์ดํ„ฐ๋„ ์ค€๋น„ํ–ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ์ ์œผ๋ฉด ์ „์ฒด ์กฐํšŒ๋‚˜ ์ง‘๊ณ„ API๊ฐ€ ์ง€๋‚˜์น˜๊ฒŒ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ๋˜์–ด ์šด์˜ ํ™˜๊ฒฝ๊ณผ ์ฐจ์ด๊ฐ€ ์ปค์งˆ ์ˆ˜ ์žˆ๋‹ค. posts์—๋Š” 1,200๊ฑด์„ ์‚ฝ์ž…ํ•˜๊ณ  reading_status ๋„ค ์ข…๋ฅ˜์™€ ์žฅ๋ฅด ์—ฌ์„ฏ ์ข…๋ฅ˜๊ฐ€ ๊ณ ๋ฅด๊ฒŒ ๋ถ„์‚ฐ๋˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. community_posts์—๋Š” ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„๋กœ ์ด 200๊ฑด์„ ์‚ฝ์ž…ํ–ˆ๋‹ค.

์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ด๋ธ” ๊ฑด์ˆ˜ ์ด์œ 
A (๋…์„œ ๊ธฐ๋ก ์กฐํšŒ) posts 1,200๊ฑด ํ†ต๊ณ„ ์ง‘๊ณ„ API์™€ ๊ณต์œ 
C (ํ†ต๊ณ„/์ง‘๊ณ„) posts 1,200๊ฑด ํŽ˜์ด์ง€๋„ค์ด์…˜ ์—†๋Š” ์ „์ฒด ์กฐํšŒ
D (์ปค๋ฎค๋‹ˆํ‹ฐ ํƒ์ƒ‰) community_posts 200๊ฑด ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ ์šฉ์œผ๋กœ 200๊ฑด ์ถฉ๋ถ„

๋ฐ˜๋ณต INSERT๋ฅผ ์ˆ˜๋™์œผ๋กœ ์‹คํ–‰ํ•˜์ง€ ์•Š๋„๋ก MySQL ํ”„๋กœ์‹œ์ €๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค. ๋‚ ์งœ, ๋…์„œ ์ƒํƒœ, ์žฅ๋ฅด, ํ‰์  ๋“ฑ์ด ํŠน์ • ๊ฐ’์—๋งŒ ์น˜์šฐ์น˜์ง€ ์•Š๋„๋ก ๋ฐ˜๋ณต ํšŸ์ˆ˜์— ๋”ฐ๋ผ ๊ฐ’์„ ๋ถ„์‚ฐํ–ˆ๊ณ , ์™„๋ฃŒ ์ƒํƒœ์ธ ๋ฐ์ดํ„ฐ์—๋งŒ ์™„๋…์ผ๊ณผ ํ‰์ ์„ ์ž…๋ ฅํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค.

DELIMITER $$

CREATE PROCEDURE insert_loadtest_posts()
BEGIN
  DECLARE i INT DEFAULT 1;
  DECLARE r_status VARCHAR(20);
  DECLARE r_genre VARCHAR(20);
  DECLARE r_rating DOUBLE;
  DECLARE r_completed DATE;
  DECLARE r_start DATE;
  DECLARE v_user_id BIGINT;

  SELECT id INTO v_user_id
  FROM users
  WHERE email = 'loadtest@example.com'
  LIMIT 1;

  WHILE i <= 1200 DO
    SET r_status = ELT((i % 4) + 1, 'COMPLETED', 'READING', 'PAUSED', 'WISHLIST');
    SET r_genre = ELT((i % 6) + 1, '์†Œ์„ค', '์—์„ธ์ด', '์ž๊ธฐ๊ณ„๋ฐœ', '๊ฒฝ์ œ/๊ฒฝ์˜', '๊ณผํ•™', '์—ญ์‚ฌ');
    SET r_rating = IF(r_status = 'COMPLETED', ROUND(2.5 + (i % 3) * 0.5 + (i % 5) * 0.2, 1), NULL);
    SET r_completed = IF(r_status = 'COMPLETED', DATE_SUB(CURDATE(), INTERVAL (i % 365) DAY), NULL);
    SET r_start = DATE_SUB(CURDATE(), INTERVAL (i % 365 + 10) DAY);

    INSERT INTO posts (
      created_at, updated_at, user_id, title, author, publisher,
      genre, reading_status, rating, short_review,
      start_date, completed_date, current_page, total_page,
      summary, cover_image_url, published_date
    ) VALUES (
      NOW(), NOW(), v_user_id,
      CONCAT('ํ…Œ์ŠคํŠธ ๋„์„œ ', i), CONCAT('์ €์ž ', (i % 20) + 1), CONCAT('์ถœํŒ์‚ฌ ', (i % 10) + 1),
      r_genre, r_status, r_rating,
      IF(r_status = 'COMPLETED', CONCAT('ํ•œ์ค„ํ‰ ', i), NULL),
      r_start, r_completed,
      IF(r_status = 'READING', (i % 200) + 50, NULL),
      300 + (i % 200), CONCAT('์ค„๊ฑฐ๋ฆฌ ์š”์•ฝ ', i), NULL,
      DATE_SUB(CURDATE(), INTERVAL (i % 1000) DAY)
    );

    SET i = i + 1;
  END WHILE;
END$$

DELIMITER ;

CALL insert_loadtest_posts();

์‚ฝ์ž… ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. posts๋Š” ๋„ค ๊ฐœ์˜ ๋…์„œ ์ƒํƒœ๊ฐ€ ๊ฐ๊ฐ 300๊ฑด์”ฉ ์ƒ์„ฑ๋๊ณ , community_posts๋Š” ์„ธ ๊ฐœ ์นดํ…Œ๊ณ ๋ฆฌ์— ์•ฝ 66~67๊ฑด์”ฉ ๋ถ„์‚ฐ๋๋‹ค.

posts, community_posts ์‚ฝ์ž… ๊ฒฐ๊ณผ

5. ๊ฒฐ๊ณผ ๋น„๊ต

ํ•ต์‹ฌ ๊ฒฐ๊ณผ
๋‹จ๋… ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰ ์ค‘์—๋Š” 50 VU ๊ตฌ๊ฐ„๊นŒ์ง€ ์‘๋‹ต์‹œ๊ฐ„์ด ๋น„๊ต์  ์•ˆ์ •์ ์œผ๋กœ ๊ด€์ฐฐ๋๊ณ , 100 VU ์ดํ›„ ์ง€์—ฐ ์ฆ๊ฐ€๊ฐ€ ๋‘๋“œ๋Ÿฌ์กŒ๋‹ค. ๋„ค ๊ฐœ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋™์‹œ์— ์‹คํ–‰ํ•œ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ๋Š” P95๊ฐ€ 7,000ms๊นŒ์ง€ ์ƒ์Šนํ–ˆ๋‹ค. Slow Query Log์—์„œ๋Š” 1์ดˆ๋ฅผ ์ดˆ๊ณผํ•œ ๊ฐœ๋ณ„ SQL์ด ํ™•์ธ๋˜์ง€ ์•Š์•˜์œผ๋ฉฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„์˜ 1 vCPU์™€ t2 ๊ณ„์—ด์˜ ๋ฒ„์Šคํ„ฐ๋ธ” ํŠน์„ฑ์„ ๊ณ ๋ คํ•˜๋ฉด CPU ํฌํ™”๊ฐ€ ์œ ๋ ฅํ•œ ์›์ธ์œผ๋กœ ์ถ”์ •๋๋‹ค. ๋‹ค๋งŒ ์ด๋ฅผ ํ™•์ •ํ•˜๋ ค๋ฉด CPU ์‚ฌ์šฉ๋ฅ ๊ณผ CPU ํฌ๋ ˆ๋”ง ๋“ฑ ์ธํ”„๋ผ ์ง€ํ‘œ๊ฐ€ ์ถ”๊ฐ€๋กœ ํ•„์š”ํ•˜๋‹ค.

๋‹จ๋… ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒฐ๊ณผ

๊ฐ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ฐœ๋ณ„ ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„์€ 267~781ms๋กœ ๋ชฉํ‘œ์ธ 1,000ms ๋ฏธ๋งŒ์„ ์œ ์ง€ํ–ˆ๋‹ค. ํ†ต๊ณ„ ๋ฐ ๋ชฉํ‘œ ํ™•์ธ ์‹œ๋‚˜๋ฆฌ์˜ค C๋Š” P95 1,390ms์™€ RPS 75.11/s๋กœ ์„ค์ •ํ•œ ๊ธฐ์ค€์„ ๋ชจ๋‘ ๋งŒ์กฑํ–ˆ๋‹ค. ๋ฐ˜๋ฉด ๋…์„œ ๊ธฐ๋ก ์กฐํšŒ์™€ ์ž‘์„ฑ ์‹œ๋‚˜๋ฆฌ์˜ค A, B๋Š” ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„๊ณผ HTTP ์š”์ฒญ ์‹คํŒจ์œจ์€ ๊ธฐ์ค€์„ ๋งŒ์กฑํ–ˆ์ง€๋งŒ P95๊ฐ€ ๊ฐ๊ฐ 2,730ms์™€ 2,800ms๋กœ ๋ชฉํ‘œ๋ฅผ ์ดˆ๊ณผํ–ˆ๋‹ค. ์ปค๋ฎค๋‹ˆํ‹ฐ ํƒ์ƒ‰ ์‹œ๋‚˜๋ฆฌ์˜ค D๋Š” P95๋Š” ๋ชฉํ‘œ ์•ˆ์— ์žˆ์—ˆ์œผ๋‚˜, ๋™์ผ ๊ณ„์ •์˜ ์ค‘๋ณต ์ข‹์•„์š” ์š”์ฒญ์—์„œ ๋ฐ˜ํ™˜๋œ 409 ์‘๋‹ต์ด k6์˜ ์‹คํŒจ ์š”์ฒญ์œผ๋กœ ์ง‘๊ณ„๋˜๋ฉด์„œ ์ „์ฒด HTTP ์š”์ฒญ ์‹คํŒจ์œจ์ด 10.61%๊นŒ์ง€ ์ฆ๊ฐ€ํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ์ˆ˜์น˜๋Š” ์„œ๋ฒ„ ์žฅ์• ์œจ๊ณผ ๋™์ผํ•˜๊ฒŒ ํ•ด์„ํ•˜์ง€ ์•Š์•˜๋‹ค.

์‹œ๋‚˜๋ฆฌ์˜ค ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ P95 ์‘๋‹ต์‹œ๊ฐ„ RPS HTTP ์š”์ฒญ ์‹คํŒจ์œจ ํŒ์ •
A (๋…์„œ ๊ธฐ๋ก ์กฐํšŒ) 781ms 2,730ms 44.95/s 0% โš ๏ธ P95 ์ดˆ๊ณผ
B (๋…์„œ ๊ธฐ๋ก ์ž‘์„ฑ) 762ms 2,800ms 45.62/s 0% โš ๏ธ P95 ์ดˆ๊ณผ
C (ํ†ต๊ณ„/๋ชฉํ‘œ ํ™•์ธ) 267ms 1,390ms 75.11/s 0% โœ…
D (์ปค๋ฎค๋‹ˆํ‹ฐ ํƒ์ƒ‰) 387ms 1,850ms 64.83/s 10.61% โš ๏ธ ์‹คํŒจ์œจ ์ดˆ๊ณผ
์‹œ๋‚˜๋ฆฌ์˜ค A~D ๊ฒฐ๊ณผ

ํ†ตํ•ฉ ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒฐ๊ณผ

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ๋‹จ๋… ํ…Œ์ŠคํŠธ์™€ ๋‹ฌ๋ฆฌ ๊ฐ ์‹œ๋‚˜๋ฆฌ์˜ค์˜ ์ตœ๋Œ€ ๋ถ€ํ•˜๋ฅผ 100 VU๋กœ ์„ค์ •ํ•ด, ๋„ค ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ํ•ฉ์‚ฐํ•œ ์ตœ๋Œ€ ๋ถ€ํ•˜๊ฐ€ 400 VU๊ฐ€ ๋˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค. ์ด๋•Œ ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„์€ 2,270ms, P95๋Š” 7,000ms๋กœ ์ฆ๊ฐ€ํ–ˆ๊ณ  ์ „์ฒด HTTP ์š”์ฒญ ์‹คํŒจ์œจ๋„ 1.90%๋กœ ๋ชฉํ‘œ๋ฅผ ์ดˆ๊ณผํ–ˆ๋‹ค. RPS๋Š” 61.72/s๋กœ ์ฒ˜๋ฆฌ๋Ÿ‰ ๊ธฐ์ค€์€ ๋งŒ์กฑํ–ˆ์ง€๋งŒ, ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋™์•ˆ ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ํฌ๊ฒŒ ๋Š˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์•ˆ์ •์ ์ธ ์šด์˜ ๊ตฌ๊ฐ„์œผ๋กœ ๋ณด๊ธฐ๋Š” ์–ด๋ ค์› ๋‹ค.

์ง€ํ‘œ ๊ฒฐ๊ณผ ๋ชฉํ‘œ ํŒ์ •
ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„ 2,270ms < 1,000ms โŒ
P95 ์‘๋‹ต์‹œ๊ฐ„ 7,000ms < 2,000ms โŒ
HTTP ์š”์ฒญ ์‹คํŒจ์œจ 1.90% < 1% โŒ
RPS 61.72/s > 20 โœ…
ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

๊ฒฐ๊ณผ ๋ถ„์„

๋‹จ๋… ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์˜ ์ฐจ์ด๋Š” ์‘๋‹ต์‹œ๊ฐ„์—์„œ ๊ฐ€์žฅ ํฌ๊ฒŒ ๋‚˜ํƒ€๋‚ฌ๋‹ค. ๋‹จ๋… ํ…Œ์ŠคํŠธ์˜ ํ‰๊ท  ์‘๋‹ต์‹œ๊ฐ„์€ 267~781ms์˜€์ง€๋งŒ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ๋Š” 2,270ms๋กœ ์•ฝ 3~8๋ฐฐ ์ฆ๊ฐ€ํ–ˆ๋‹ค. P95๋„ ๋‹จ๋… ํ…Œ์ŠคํŠธ ์ตœ๋Œ€ 2,800ms์—์„œ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ 7,000ms๋กœ ์ƒ์Šนํ–ˆ๋‹ค. ์—ฌ๋Ÿฌ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋˜๋ฉด์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„์˜ ์ œํ•œ๋œ CPU์™€ ์Šค๋ ˆ๋“œ ์ž์›์— ๊ฒฝํ•ฉ์ด ๋ฐœ์ƒํ–ˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค๊ณ  ํ•ด์„ํ–ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰ ์ค‘ ๊ด€์ฐฐํ•œ ํ•œ๊ณ„ ๊ตฌ๊ฐ„์€ 100 VU ์ „ํ›„์˜€๋‹ค. ๋‹จ๋… ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” 50 VU์ธ Normal ๋‹จ๊ณ„๊นŒ์ง€ ์‘๋‹ต์‹œ๊ฐ„์ด ๋น„๊ต์  ์•ˆ์ •์ ์œผ๋กœ ๋ณด์˜€๊ณ , 100 VU์ธ Heavy ๋‹จ๊ณ„๋ถ€ํ„ฐ ์ง€์—ฐ ์ฆ๊ฐ€๊ฐ€ ๋‘๋“œ๋Ÿฌ์กŒ๋‹ค. ๋‹ค๋งŒ ๊ฒŒ์‹œ๊ธ€์— ์ฒจ๋ถ€ํ•œ k6 ์ตœ์ข… ์š”์•ฝ๊ฐ’์€ ๋ชจ๋“  stage๋ฅผ ํ•ฉ์‚ฐํ•œ ๊ฒฐ๊ณผ์ด๋ฏ€๋กœ, 50 VU์™€ 100 VU์˜ ๊ฒฝ๊ณ„๋ฅผ ์ •๋Ÿ‰์ ์œผ๋กœ ์ž…์ฆํ•˜๋ ค๋ฉด stage๋ณ„ ํƒœ๊ทธ, CSV ๊ฒฐ๊ณผ ๋˜๋Š” ์‹œ๊ฐ„๋Œ€๋ณ„ P95 ๊ทธ๋ž˜ํ”„๊ฐ€ ์ถ”๊ฐ€๋กœ ํ•„์š”ํ•˜๋‹ค. ๋”ฐ๋ผ์„œ ํ˜„์žฌ ๊ฒฐ๋ก ์€ ์ด๋ฒˆ k6 ์‹œ๋‚˜๋ฆฌ์˜ค์™€ ์‹คํ–‰ ์ค‘ ๊ด€์ฐฐ์„ ๊ธฐ์ค€์œผ๋กœ ํ•œ ์ž ์ •์ ์ธ ๋ฒ”์œ„๋‹ค.

๋ฉ”๋ชจ๋ฆฌ๋Š” ํ…Œ์ŠคํŠธ ์ค‘ ์ฆ๊ฐ€ํ–ˆ์ง€๋งŒ ์ด๋ฒˆ ์ธก์ • ๊ตฌ๊ฐ„์—์„œ ๊ณ„์† ์ƒ์Šนํ•˜๋Š” ํŒจํ„ด์€ ๋ณด์ด์ง€ ์•Š์•˜๋‹ค. ํ…Œ์ŠคํŠธ ์ „ 323MB์˜€๋˜ ์ปจํ…Œ์ด๋„ˆ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์€ ๊ฐ ์‹œ๋‚˜๋ฆฌ์˜ค ํ›„ 570~609MB ๋ฒ”์œ„์—์„œ ์›€์ง์˜€๊ณ  ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ํ›„์—๋Š” 592MB๋ฅผ ๊ธฐ๋กํ–ˆ๋‹ค.

์‹œ์  ์‚ฌ์šฉ๋Ÿ‰
ํ…Œ์ŠคํŠธ ์ „ 323MB
์‹œ๋‚˜๋ฆฌ์˜ค A ํ›„ 570MB
์‹œ๋‚˜๋ฆฌ์˜ค B ํ›„ 583MB
์‹œ๋‚˜๋ฆฌ์˜ค C ํ›„ 609MB
์‹œ๋‚˜๋ฆฌ์˜ค D ํ›„ 575MB
ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ํ›„ 592MB

ํ†ตํ•ฉ 400 VU ํ…Œ์ŠคํŠธ ์ดํ›„์—๋„ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์€ 592MB ์ˆ˜์ค€์ด์—ˆ๋‹ค. ์ด๋ฒˆ ํ…Œ์ŠคํŠธ ๊ตฌ๊ฐ„์—์„œ๋Š” ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๊ณ„์† ์ฆ๊ฐ€ํ•˜๋Š” ํŒจํ„ด์ด ๊ด€์ฐฐ๋˜์ง€ ์•Š์•„ OOM์ด๋‚˜ ๊ธ‰๊ฒฉํ•œ ๋ฉ”๋ชจ๋ฆฌ ์ฆ๊ฐ€๊ฐ€ ์ฃผ๋œ ๋ณ‘๋ชฉ์ผ ๊ฐ€๋Šฅ์„ฑ์€ ๋‚ฎ๋‹ค๊ณ  ๋ดค๋‹ค. ๋‹ค๋งŒ ๋ช‡ ์ฐจ๋ก€์˜ ์ข…๋ฃŒ ์‹œ์  ์ธก์ •๋งŒ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ํ™•์ •์ ์œผ๋กœ ๋ฐฐ์ œํ•  ์ˆ˜๋Š” ์—†์œผ๋ฉฐ, ์žฅ์‹œ๊ฐ„ ๋ฐ˜๋ณต ํ…Œ์ŠคํŠธ์™€ JVM ํž™·GC·๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”๋ชจ๋ฆฌ ์ง€ํ‘œ๋ฅผ ์ถ”๊ฐ€๋กœ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค.

์žฅ์‹œ๊ฐ„ ์‹คํ–‰๋˜๋Š” ๋‹จ์ผ SQL์ด ์ฃผ์š” ์›์ธ์ผ ๊ฐ€๋Šฅ์„ฑ์€ ๋‚ฎ์•˜๋‹ค. ํ…Œ์ŠคํŠธ ์ข…๋ฃŒ ํ›„ MySQL Slow Query Log๋ฅผ ํ™•์ธํ–ˆ์ง€๋งŒ 1์ดˆ ์ด์ƒ ์‹คํ–‰๋œ ์ฟผ๋ฆฌ๋Š” ๊ธฐ๋ก๋˜์ง€ ์•Š์•˜๋‹ค. ๋‹ค๋งŒ ์ด ๊ฒฐ๊ณผ๋งŒ์œผ๋กœ ์งง์€ ์ฟผ๋ฆฌ์˜ ๋ฐ˜๋ณต, ์ปค๋„ฅ์…˜ ํ’€ ๋Œ€๊ธฐ, RDS ์ž์› ์‚ฌ์šฉ๋Ÿ‰์„ ๋ชจ๋‘ ๋ฐฐ์ œํ•  ์ˆ˜๋Š” ์—†๋‹ค. ๋ถ€ํ•˜๊ฐ€ ์ฆ๊ฐ€ํ• ์ˆ˜๋ก ์‘๋‹ต์‹œ๊ฐ„์ด ๊ธ‰๊ฒฉํžˆ ์ƒ์Šนํ–ˆ๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„๊ฐ€ 1 vCPU ํ™˜๊ฒฝ์ด๋ผ๋Š” ์ ์„ ๊ณ ๋ คํ•˜๋ฉด CPU ํฌํ™”๊ฐ€ ์œ ๋ ฅํ•œ ์›์ธ์œผ๋กœ ์ถ”์ •๋˜์ง€๋งŒ, ํ™•์ •ํ•˜๋ ค๋ฉด CloudWatch์˜ CPUUtilization·CPUCreditBalance์™€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์Šค๋ ˆ๋“œ ๋ฐ ์ปค๋„ฅ์…˜ ํ’€ ์ง€ํ‘œ๋ฅผ ํ•จ๊ป˜ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค.

6. ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ์ค‘ ๋ฐœ๊ฒฌํ•œ ์ด์Šˆ - ์ข‹์•„์š” Race Condition

์‹œ๋‚˜๋ฆฌ์˜ค D์˜ ์ตœ์ดˆ ํ…Œ์ŠคํŠธ์—์„œ ์ „์ฒด HTTP ์š”์ฒญ ์‹คํŒจ์œจ์€ 21.67%์˜€๊ณ , ์ข‹์•„์š” ์š”์ฒญ์˜ check ์‹คํŒจ์œจ์€ ์•ฝ 66%๋กœ ๋‚˜ํƒ€๋‚ฌ๋‹ค. ์ฒ˜์Œ์—๋Š” ํ•˜๋‚˜์˜ ํ…Œ์ŠคํŠธ ๊ณ„์ •์œผ๋กœ ์ข‹์•„์š”์™€ ์ข‹์•„์š” ์ทจ์†Œ๋ฅผ ๋™์‹œ์— ๋ฐ˜๋ณตํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค ๋ฌธ์ œ๋กœ ๋ดค์ง€๋งŒ, ์„œ๋น„์Šค ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•˜๋ฉด์„œ ๋™์ผ ์‚ฌ์šฉ์ž์˜ ์ค‘๋ณต ์š”์ฒญ์—์„œ ์‹ค์ œ๋กœ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” Check-Then-Act ํ˜•ํƒœ์˜ Race Condition๋„ ํ•จ๊ป˜ ๋ฐœ๊ฒฌํ–ˆ๋‹ค.

์›์ธ ๋ถ„์„

CommunityService.likePost()๋Š” ๋จผ์ € existsByPostIdAndUserId๋กœ ๊ธฐ์กด ์ข‹์•„์š”๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•œ ๋’ค, ์ข‹์•„์š”๊ฐ€ ์—†์œผ๋ฉด INSERT๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ตฌ์กฐ์˜€๋‹ค.

์ˆ˜์ • ์ „ ์ข‹์•„์š” ๋กœ์ง

๋ฌธ์ œ๋Š” ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” SELECT์™€ ์‹ค์ œ INSERT๊ฐ€ ํ•˜๋‚˜์˜ ์›์ž์  ์—ฐ์‚ฐ์ด ์•„๋‹ˆ๋ผ๋Š” ์ ์ด๋‹ค. ๋‘ ์š”์ฒญ์ด ๊ฑฐ์˜ ๋™์‹œ์— ๋“ค์–ด์˜ค๋ฉด ๋‘ ์Šค๋ ˆ๋“œ ๋ชจ๋‘ ์ข‹์•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ํŒ๋‹จํ•œ ๋’ค INSERT๋ฅผ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๋‹ค.

Thread A: ์ข‹์•„์š” ์—†์Œ ํ™•์ธ (1๋ฒˆ ํ†ต๊ณผ)
Thread B: ์ข‹์•„์š” ์—†์Œ ํ™•์ธ (1๋ฒˆ ํ†ต๊ณผ)
Thread A: INSERT ์„ฑ๊ณต
Thread B: INSERT ์‹œ๋„ → DB unique constraint ์œ„๋ฐ˜ → 409

์ด ๊ฒฝ์šฐ ๋จผ์ € ์‹คํ–‰๋œ INSERT๋Š” ์„ฑ๊ณตํ•˜์ง€๋งŒ ๋’ค๋Šฆ๊ฒŒ ์‹คํ–‰๋œ ์š”์ฒญ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ unique constraint๋ฅผ ์œ„๋ฐ˜ํ•œ๋‹ค. ์ด ๋ฌธ์ œ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ™์€ ๊ฒŒ์‹œ๊ธ€์„ ์ข‹์•„์š” ํ•  ๋•Œ๊ฐ€ ์•„๋‹ˆ๋ผ, ๋™์ผ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฐ™์€ ๊ฒŒ์‹œ๊ธ€์— ์š”์ฒญ์„ ๊ฑฐ์˜ ๋™์‹œ์— ์ค‘๋ณต ์ „์†กํ•  ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค. ๋น ๋ฅธ ์—ฐ์† ํด๋ฆญ, ์—ฌ๋Ÿฌ ํƒญ์—์„œ์˜ ์š”์ฒญ, ๋„คํŠธ์›Œํฌ ์žฌ์‹œ๋„์ฒ˜๋Ÿผ ์‹ค์ œ ์šด์˜์—์„œ๋„ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ „ํ˜•์ ์ธ Check-Then-Act ๊ฒฝ์Ÿ ์กฐ๊ฑด์ด๋‹ค.

์ˆ˜์ •๊ณผ ๊ฒ€์ฆ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ˆ˜ํ–‰ํ•˜๋˜ ์‚ฌ์ „ ์กด์žฌ ์—ฌ๋ถ€ ํ™•์ธ์„ ์ œ๊ฑฐํ•˜๊ณ , ์ค‘๋ณต ๋ฐ์ดํ„ฐ ๋ฐฉ์ง€๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ unique constraint์— ๋งก๊ฒผ๋‹ค. ์ค‘๋ณต INSERT์—์„œ DataIntegrityViolationException์ด ๋ฐœ์ƒํ•˜๋ฉด ์ด๋ฏธ ์ข‹์•„์š”๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ๋กœ ๋ณ€ํ™˜ํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ๋‹ค. ๋‹จ, JPA์˜ ์ œ์•ฝ์กฐ๊ฑด ์œ„๋ฐ˜์€ ํŠธ๋žœ์žญ์…˜ commit ์‹œ์ ์— ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ์œผ๋ฏ€๋กœ ์˜ˆ์™ธ๋ฅผ ํ˜„์žฌ try-catch ์•ˆ์—์„œ ํ™•์‹คํžˆ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด saveAndFlush() ๋˜๋Š” ๋ช…์‹œ์ ์ธ flush()๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

1. ์‚ฌ์ „ ์กฐํšŒ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  DB unique constraint๋กœ ์ค‘๋ณต์„ ์ฒ˜๋ฆฌํ•œ ์ข‹์•„์š” ๋กœ์ง 2. ํŠน์ • ๊ฒŒ์‹œ๊ธ€์— ์š”์ฒญ์ด ์ง‘์ค‘๋˜์ง€ ์•Š๋„๋ก ๋ฌด์ž‘์œ„ ๊ฒŒ์‹œ๊ธ€์„ ์„ ํƒํ•œ k6 ์‹œ๋‚˜๋ฆฌ์˜ค

 

์ด ๋ฐฉ์‹์€ ์ค‘๋ณต ์š”์ฒญ์ด ๋™์‹œ์— ๋“ค์–ด์™€๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ตœ์ข…์ ์œผ๋กœ ์ค‘๋ณต ์ €์žฅ์„ ๋ฐฉ์ง€ํ•œ๋‹ค. ๋™์‹œ์— ๋งค ์š”์ฒญ๋งˆ๋‹ค ์‹คํ–‰๋˜๋˜ existsByPostIdAndUserId SELECT๋„ ์ œ๊ฑฐ๋˜์–ด ์ฟผ๋ฆฌ ์ˆ˜๋ฅผ ์ค„์˜€๋‹ค. ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋„ ํŠน์ • ๊ฒŒ์‹œ๊ธ€ ํ•˜๋‚˜์— ์š”์ฒญ์ด ์ง‘์ค‘๋˜์ง€ ์•Š๋„๋ก ์กฐํšŒ๋œ ๊ฒŒ์‹œ๊ธ€ ์ค‘ ํ•˜๋‚˜๋ฅผ ๋ฌด์ž‘์œ„๋กœ ์„ ํƒํ•˜๋„๋ก ๋ณ€๊ฒฝํ–ˆ๋‹ค. ๋‹ค๋งŒ ๋ชจ๋“  VU๊ฐ€ ๊ฐ™์€ ๊ณ„์ •์„ ๊ณต์œ ํ•˜๋Š” ๊ตฌ์กฐ ์ž์ฒด๋Š” ์œ ์ง€๋๊ธฐ ๋•Œ๋ฌธ์— ๋™์ผํ•œ post_id + user_id ์กฐํ•ฉ์˜ ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ์€ ๋‚จ์•„ ์žˆ์—ˆ๋‹ค.

ํ•ญ๋ชฉ ์ˆ˜์ • ์ „ ์ˆ˜์ • ํ›„
์ „์ฒด HTTP ์š”์ฒญ ์‹คํŒจ์œจ 21.67% 10.61%
์ข‹์•„์š” check ์„ฑ๊ณต๋ฅ  34% 68%
์ข‹์•„์š” check ์‹คํŒจ์œจ ์•ฝ 66% ์•ฝ 32%

์ˆ˜์ • ํ›„ ์ „์ฒด HTTP ์š”์ฒญ ์‹คํŒจ์œจ์€ 21.67%์—์„œ 10.61%๋กœ ์ค„์—ˆ๊ณ , ์ข‹์•„์š” check ์„ฑ๊ณต๋ฅ ์€ 34%์—์„œ 68%๋กœ ์ฆ๊ฐ€ํ–ˆ๋‹ค. ์—ฌ๊ธฐ์„œ 409 Conflict๋Š” ์„œ๋ฒ„๊ฐ€ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•œ ์žฅ์• ๋ผ๊ธฐ๋ณด๋‹ค ๋™์ผ ๊ณ„์ •์˜ ์ค‘๋ณต ์ข‹์•„์š”๋ฅผ ๊ฑฐ์ ˆํ•œ ์‘๋‹ต์ด์ง€๋งŒ, k6์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹คํŒจ ์š”์ฒญ์œผ๋กœ ์ง‘๊ณ„๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ์‹คํŒจ์œจ์„ ์„œ๋ฒ„ ๊ฐ€์šฉ์„ฑ ์‹คํŒจ์œจ๋กœ ๊ทธ๋Œ€๋กœ ํ•ด์„ํ•˜์ง€ ์•Š์•˜๋‹ค. ์‹ค์ œ ์šด์˜์—์„œ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ unique key๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ํ…Œ์ŠคํŠธ๋ณด๋‹ค ์ถฉ๋Œ ๋นˆ๋„๊ฐ€ ๋‚ฎ์„ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋™์ผ ์‚ฌ์šฉ์ž์˜ ๋”๋ธ” ํด๋ฆญ์ด๋‚˜ ์žฌ์‹œ๋„๋Š” ์—ฌ์ „ํžˆ ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ œ์•ฝ์กฐ๊ฑด์„ ํ†ตํ•œ ๋ณดํ˜ธ๋Š” ํ•„์š”ํ•˜๋‹ค. ์ด ๋ฌธ์ œ๋Š” ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ์ผ๋ฐ˜์ ์ธ ํ…Œ์ŠคํŠธ๋ณด๋‹ค ๋‹ค์ˆ˜์˜ ์š”์ฒญ์ด ๋™์‹œ์— ๋„์ฐฉํ•˜๋Š” ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ์—์„œ ์žฌํ˜„ํ•˜๊ธฐ ์‰ฌ์› ๋‹ค.

7. ๊ฒฐ๋ก  ๋ฐ ํ•œ๊ณ„

์ด๋ฒˆ k6 ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” ๋‹จ์ผ ์‹œ๋‚˜๋ฆฌ์˜ค 50 VU ๊ตฌ๊ฐ„๊นŒ์ง€ ๋น„๊ต์  ์•ˆ์ •์ ์œผ๋กœ ๊ด€์ฐฐ๋๋‹ค. 100 VU ๊ตฌ๊ฐ„๋ถ€ํ„ฐ ์ง€์—ฐ ์ฆ๊ฐ€๊ฐ€ ๋‘๋“œ๋Ÿฌ์กŒ๊ณ  150 VU์—์„œ๋Š” ์‘๋‹ต์‹œ๊ฐ„์ด ๋” ํฌ๊ฒŒ ์ฆ๊ฐ€ํ–ˆ๋‹ค. ๋„ค ๊ฐœ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋™์‹œ์— ์‹คํ–‰ํ•œ ์ตœ๋Œ€ 400 VU ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ๋Š” P95๊ฐ€ 7์ดˆ๊นŒ์ง€ ์ƒ์Šนํ–ˆ๋‹ค. ๋‹ค๋งŒ VU๋Š” ์‹ค์ œ ๋™์‹œ ์‚ฌ์šฉ์ž ์ˆ˜์™€ ๋™์ผํ•œ ๊ฐœ๋…์ด ์•„๋‹ˆ๋ฉฐ, stage๋ณ„ ์ •๋Ÿ‰ ์ž๋ฃŒ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ์ „๊นŒ์ง€ 50 VU๋ฅผ ์ ˆ๋Œ€์ ์ธ ์ˆ˜์šฉ ํ•œ๊ณ„๋กœ ๋‹จ์ •ํ•˜์ง€ ์•Š์•˜๋‹ค.

๋‹ค๋งŒ ์ด๋ฒˆ ๊ฒฐ๊ณผ์—๋Š” ๋‹จ์ผ ๊ณ„์ •์„ ์‚ฌ์šฉํ•œ ํ…Œ์ŠคํŠธ ๊ตฌ์กฐ์˜ ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค. ์‹ค์ œ ์šด์˜์—์„œ๋Š” ์‚ฌ์šฉ์ž๋งˆ๋‹ค ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜์ง€๋งŒ ์ด๋ฒˆ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ๋ชจ๋“  VU๊ฐ€ ๊ฐ™์€ ๊ณ„์ •๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ–ˆ๋‹ค. ํŠนํžˆ ์ข‹์•„์š” API์˜ HTTP ์š”์ฒญ ์‹คํŒจ์œจ์€ ์ด ์ฐจ์ด๋ฅผ ํฌ๊ฒŒ ๋ฐ˜์˜ํ•œ๋‹ค. ์šด์˜ ํ™˜๊ฒฝ์„ ๋” ์ •ํ™•ํžˆ ์žฌํ˜„ํ•˜๋ ค๋ฉด VU๋ณ„ ๊ณ„์ •์„ ์‚ฌ์ „์— ์ค€๋น„ํ•˜๊ณ  ๊ฐ VU๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋„๋ก ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ฐœ์„ ํ•ด์•ผ ํ•œ๋‹ค.

ํ˜„์žฌ ์ž๋ฃŒ์—์„œ๋Š” CPU ํฌํ™”๊ฐ€ ๊ฐ€์žฅ ์œ ๋ ฅํ•œ ๋ณ‘๋ชฉ ๊ฐ€์„ค์ด๋‹ค. Slow Query Log์—์„œ 1์ดˆ๋ฅผ ๋„˜๋Š” ๊ฐœ๋ณ„ SQL์€ ๋ฐœ๊ฒฌ๋˜์ง€ ์•Š์•˜๊ณ , ์งง์€ ํ…Œ์ŠคํŠธ ๊ตฌ๊ฐ„์—์„œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๊ณ„์† ์ฆ๊ฐ€ํ•˜๋Š” ํŒจํ„ด๋„ ๋ณด์ด์ง€ ์•Š์•˜๋‹ค. ๋ฐ˜๋ฉด ๋ถ€ํ•˜ ์ฆ๊ฐ€์— ๋”ฐ๋ผ ์‘๋‹ต์‹œ๊ฐ„์ด ๊ธ‰๊ฒฉํžˆ ์•…ํ™”๋๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„๋Š” 1 vCPU ํ™˜๊ฒฝ์ด์—ˆ๋‹ค. ๋‹ค๋งŒ CPU ๋ณ‘๋ชฉ์„ ํ™•์ •ํ•˜๋ ค๋ฉด CloudWatch์˜ CPU ์‚ฌ์šฉ๋ฅ ๊ณผ CPU ํฌ๋ ˆ๋”ง, ์Šค๋ ˆ๋“œ ํ’€ ๋ฐ ์ปค๋„ฅ์…˜ ํ’€ ๋Œ€๊ธฐ ์ง€ํ‘œ๋ฅผ ์ถ”๊ฐ€๋กœ ์ˆ˜์ง‘ํ•ด์•ผ ํ•œ๋‹ค. ํฌํŠธํด๋ฆฌ์˜ค ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์ด ๊ฒ€์ฆ๊ณผ ํ•จ๊ป˜ ๊ฐ™์€ ์‚ฌ์–‘์—์„œ ๋ถˆํ•„์š”ํ•œ ์—ฐ์‚ฐ๊ณผ ๋ธ”๋กœํ‚น์„ ์ค„์ด๋Š” ์ฝ”๋“œ ๋ ˆ๋ฒจ ๊ฐœ์„ ์„ ์šฐ์„  ์ ์šฉํ•  ๊ณ„ํš์ด๋‹ค.

๊ฐœ์„  ๋ฐฉ๋ฒ• ๊ธฐ๋Œ€ ํšจ๊ณผ
ํ†ต๊ณ„ API Redis ์บ์‹ฑ DB ์ฟผ๋ฆฌ ๊ฐ์†Œ, CPU ์‚ฌ์šฉ๋Ÿ‰ ์ ˆ๊ฐ
์•Œ๋ฆผ ๋ฐœ์†ก ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ์‘๋‹ต ๋ธ”๋กœํ‚น ์ œ๊ฑฐ, ์Šค๋ ˆ๋“œ ์ ์œ  ์‹œ๊ฐ„ ๊ฐ์†Œ
Gzip ์••์ถ• ํ™œ์„ฑํ™” ๋ฐ์ดํ„ฐ ์ „์†ก๋Ÿ‰ ๊ฐ์†Œ
HikariCP ์ปค๋„ฅ์…˜ ํ’€ ํŠœ๋‹ t2.micro ๊ธฐ์ค€ ์ ์ •๊ฐ’์œผ๋กœ ์กฐ์ •

ํ›„์† ์ž‘์—…์—์„œ๋Š” ํ†ต๊ณ„ API์˜ Redis ์บ์‹ฑ, ์•Œ๋ฆผ ๋ฐœ์†ก์˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ, Gzip ์••์ถ•, HikariCP ์ปค๋„ฅ์…˜ ํ’€ ์กฐ์ •์„ ์ˆœ์„œ๋Œ€๋กœ ๊ฒ€ํ† ํ•˜๊ณ  ๋™์ผํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๋กœ ๋‹ค์‹œ ํ…Œ์ŠคํŠธํ•ด ๊ฐ ๋ณ€๊ฒฝ์ด P95์™€ RPS์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ๋น„๊ตํ•  ์˜ˆ์ •์ด๋‹ค. ์ดํ›„ ํ…Œ์ŠคํŠธ์—์„œ๋Š” VU๋ณ„ ๊ณ„์ •๊ณผ stage๋ณ„ ์ง€ํ‘œ๋ฅผ ์‚ฌ์šฉํ•ด ์ •์ƒ์ ์ธ 409 ์‘๋‹ต๊ณผ ์‹ค์ œ ์„œ๋ฒ„ ์˜ค๋ฅ˜๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ , ํ•œ๊ณ„ ๊ตฌ๊ฐ„๋„ ์ •๋Ÿ‰์ ์œผ๋กœ ๋‹ค์‹œ ๊ฒ€์ฆํ•  ๊ณ„ํš์ด๋‹ค.

728x90