๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋กœ ์ธํ•œ API ์„ฑ๋Šฅ ๋ฌธ์ œ ํ•ด๊ฒฐ: ์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค ๋ฐ ์ธ๋ฑ์Šค ํŠœ๋‹์„ ํ†ตํ•œ ์ตœ์ ํ™”

2025. 5. 29. 22:47ยทProject

๊ฐœ์š”

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

 

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

 

๋ณธ ๊ธ€์—์„œ๋Š” ๋‹ค์–‘ํ•œ ๊ณ„์ธต์˜ ์ง€ํ‘œ๋“ค์„ ์ข…ํ•ฉ์ ์œผ๋กœ ๋ถ„์„ํ•ด ์„ฑ๋Šฅ ์ €ํ•˜์˜ ๊ทผ๋ณธ ์›์ธ๊ณผ ๋ณ‘๋ชฉ ์ง€์ ์„ ๊ทœ๋ช…ํ•˜๊ณ , ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ค ์กฐ์น˜๋ฅผ ์ทจํ–ˆ๋Š”์ง€ ๊ทธ ๊ณผ์ •์„ ์ƒ์„ธํžˆ ์†Œ๊ฐœํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

 

ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ

์ด๋ฒˆ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ์— ์‚ฌ์šฉ๋œ ๋ฐ์ดํ„ฐ ๊ทœ๋ชจ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Table Size
user 1,000
post_like 50,000
post 1,000,000
content 10,000,000

 

ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค

ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ k6 ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ API์— ๋Œ€ํ•œ ๋ถ€ํ•˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

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

export let options = {
    stages: [
        { duration: '10s', target: 10 },
        { duration: '10s', target: 50 },
        { duration: '10s', target: 100 },
        { duration: '10s', target: 150 },
        { duration: '10s', target: 200 },
        { duration: '10s', target: 250 },
    ],
};

export let errorRate = new Rate("errors");

export default function() {
    const page = Math.floor(Math.random() * 10) + 1
    const url = `http://localhost:8080/v1/posts?page=${page}&size=10`;

    let res = http.get(url);
    let success = check(res, { 'get-posts': (r) => r.status === 200 });

    if (!success) {
        errorRate.add(1);
    }
    
    sleep(1);
}

์œ„ ์Šคํฌ๋ฆฝํŠธ๋Š” ์ด 60์ดˆ ๋™์•ˆ ๊ฐ€์ƒ ์‚ฌ์šฉ์ž๋ฅผ 10๋ช…์—์„œ 250๋ช…๊นŒ์ง€ ์ ์ง„์ ์œผ๋กœ ์ฆ๊ฐ€์‹œํ‚ค๋ฉฐ ๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. 

 

๋ฌธ์ œ ์ƒํ™ฉ

๋ฌธ์ œ๋Š” MySQL์— ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•œ ํ›„ K6 ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜์˜€์„ ๋•Œ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

์ฃผ์–ด์ง„ ์ง€ํ‘œ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์„ฑ๋Šฅ ํ˜„ํ™ฉ์„ ๋ถ„์„ํ•œ ๊ฒฐ๊ณผ, ์‹ฌ๊ฐํ•œ ์ˆ˜์ค€์˜ ์‘๋‹ต ์‹œ๊ฐ„ ์ง€์—ฐ๊ณผ ๋”๋ถˆ์–ด ์˜ค๋ฅ˜์œจ์ด ๊ธ‰์ฆํ•˜๊ณ  ์žˆ์Œ์ด ํ™•์ธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. 

 

๊ตฌ์ฒด์ ์œผ๋กœ ์‚ดํŽด๋ณด๋ฉด, HTTP ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต ์‹œ๊ฐ„(http_req_duration)์ด ๋น„์ •์ƒ์ ์œผ๋กœ ๊ธธ์–ด์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ‰๊ท (mean) ์‘๋‹ต ์‹œ๊ฐ„์ด 27.76s์— ๋‹ฌํ•˜๋ฉฐ, ์ „์ฒด ์š”์ฒญ์˜ 90%(p90)๊ฐ€ 35.59s, 95%(p95)๊ฐ€ 35.61s ์ด๋‚ด์— ์‘๋‹ต์„ ๋ฐ›๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๋นจ๋ž๋˜ ์‘๋‹ต ์‹œ๊ฐ„ ๋˜ํ•œ 1.68์ดˆ๋กœ, API์˜ ์ผ๋ฐ˜์ ์ธ ์„ฑ๋Šฅ ๊ธฐ๋Œ€์น˜์— ๋น„์ถ”์–ด ๋ณผ ๋•Œ  ๊ฒฐ์ฝ” ๋น ๋ฅธ ํŽธ์ด ์•„๋‹ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ˆ˜์น˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ์šฉ์ž ์š”์ฒญ์ด ์ •์ƒ์ ์ธ ์‘๋‹ต์„ ๋ฐ›๊ธฐ๊นŒ์ง€ ์ˆ˜ ์ดˆ์—์„œ ์ˆ˜์‹ญ ์ดˆ๊นŒ์ง€ ์†Œ์š”๋œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. 

 

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

 

๊ทผ๋ณธ ์›์ธ ์ฐพ๊ธฐ

์ตœ๋Œ€ CPU ์‚ฌ์šฉ๋ฅ : 92.9%

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

 

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

 

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

 

์ฟผ๋ฆฌ ๋ถ„์„

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

 

์‚ฌ์šฉ๋œ ์ฟผ๋ฆฌ

์ฃผ์–ด์ง„ ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž์ถฐ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ณ ์ž ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

@Service
@Transactional(readOnly=true)
@RequiredArgsConstructor
public class PostService {

    private final PostRepository postRepository;
    private final ContentRepository contentRepository;
    private final UserRepository userRepository;
    
    public SliceResponse<PostFindAllResponse> findAllPost(OffsetLimit offsetLimit) {
        // 1. ๊ฒŒ์‹œ๊ธ€ ํŽ˜์ด์ง• ์กฐํšŒ
        List<PostQueryEntity> posts = postRepository.findAllPost(offsetLimit);
        // 2. ๊ฒŒ์‹œ๊ธ€ ID๋“ค์— ํ•ด๋‹นํ•˜๋Š” ์ฝ˜ํ…์ธ  ์กฐํšŒ
        List<ContentQueryEntity> contents = contentRepository.findAllByPostIds(posts.getIds());
        
        // 3. PostFindAllRespose์—์„œ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ
        return SliceResponse.of(PostFindAllResponse.toList(posts, contents), offsetLimit);
    }
}

postRepository.findAllPost(offsetLimit)๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง€ ๋ฐ ์‚ฌ์ด์ฆˆ์— ํ•ด๋‹นํ•˜๋Š” ๊ฒŒ์‹œ๊ธ€ ์ •๋ณด๋ฅผ ๋จผ์ € ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ์ด์–ด์„œ contentRepository.findAllByPostIds(postIds)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์กฐํšŒ๋œ ๊ฒŒ์‹œ๊ธ€ ID๋“ค์— ํ•ด๋‹นํ•˜๋Š” ๋ชจ๋“  ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์ตœ์ข…์ ์œผ๋กœ PostFindAllResponse์—์„œ ๊ฒŒ์‹œ๊ธ€ ๋ฐ์ดํ„ฐ์™€ ์ฝ˜ํ…์ธ  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒฐํ•ฉํ•˜๋Š”๋ฐ, ์ด๋•Œ ๊ฒŒ์‹œ๊ธ€๊ณผ postId๊ฐ€ ์ผ์น˜ํ•˜๋Š” ์ฝ˜ํ…์ธ  ์ค‘ ContentType์ด TEXT์ธ ๊ฒƒ์ค‘ contentId๊ฐ€ ๊ฐ€์žฅ ์ž‘์€ ๊ฒƒ ํ•˜๋‚˜์™€ ContentType์ด IMAGE์ธ ๊ฒƒ ์ค‘ contentId๊ฐ€ ๊ฐ€์žฅ ์ž‘์€ ๊ฒƒ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜์—ฌ ๊ฒฐํ•ฉํ•œ ํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

์•ž์„œ ์„ค๋ช…ํ•œ ์ฝ˜ํ…์ธ  ์กฐํšŒ ์กฐ๊ฑด์„ ๋งŒ์กฑ์‹œํ‚ค๊ธฐ ์œ„ํ•ด Querydsl์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜์˜€๊ณ , ์ด๋•Œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‹ค์ œ๋กœ ์‹คํ–‰๋˜๋Š” SQL ์ฟผ๋ฆฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

1. ๊ฒŒ์‹œ๊ธ€ ํŽ˜์ด์ง• ์กฐํšŒ

SELECT 
    p.post_id as postId,
    p.post_title as postTitle,
    u.user_name as username,
    u.user_profile_image as userProfileImage,
    p.registered_date_time as postRegisteredDateTime,
    count(pl.id) as likeCount
FROM 
	post p
INNER JOIN 
	users u on u.user_id = p.user_id
LEFT JOIN 
	post_like pl on pl.post_id = p.post_id
GROUP BY
	p.post_id, p.post_title, u.user_name, u.user_profile_image, p.registered_date_time
ORDER BY 
	p.post_id desc
LIMIT 10
OFFSET 0;

 

2. ๊ฒŒ์‹œ๊ธ€ ID์— ํ•ด๋‹นํ•˜๋Š” ์ฝ˜ํ…์ธ  ์กฐํšŒ

SELECT 
    c.post_id as postId,
    c.content_id as contentId,
    c.content_type as contentType,
    c.content_value as contentValue
FROM 
    content c
WHERE 
    c.post_id in (post_ids);

์ด์ œ๋ถ€ํ„ฐ ์•ž์„œ ์ž‘์„ฑํ•œ ์ฟผ๋ฆฌ๋“ค์ด ๋ฐ์ดํ„ฐ ์–‘์ด ๋งŽ์•„์งˆ์ˆ˜๋ก ์™œ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ผ์œผํ‚ค๋Š”์ง€, ๊ทธ ์›์ธ์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 

 

๊ฒŒ์‹œ๊ธ€ ํŽ˜์ด์ง• ์กฐํšŒ ์ฟผ๋ฆฌ ๋ถ„์„

์ฟผ๋ฆฌ ์‹คํ–‰ ์‹œ๊ฐ„: 4s 312ms (execution: 4ss 262ms, fetching: 50ms)

ํ•ด๋‹น ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด EXPLAIN ANALYZE๋ฅผ ์ˆ˜ํ–‰ํ•œ ๊ฒฐ๊ณผ, ์•„๋ž˜์™€ ๊ฐ™์€ ์‹คํ–‰ ๊ณ„ํš์ด ์ถœ๋ ฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. 

 

์œ„ ์‹คํ–‰ ๊ณ„ํš์„ ๋ถ„์„ํ•ด ๋ณด๋ฉด, ์—ฌ๋Ÿฌ ์„ฑ๋Šฅ ์ €ํ•˜ ์š”์ธ์ด ๋ณตํ•ฉ์ ์œผ๋กœ ์ž‘์šฉํ•˜๊ณ  ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

1. ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ ์กฐ์ธ

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

 

2. ์ข‹์•„์š” ์ง‘๊ณ„ ์‹œ ๋ฐœ์ƒํ•˜๋Š” ๊ทธ๋ฃนํ™” ๋น„์šฉ

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

 

3. OFFSET/LIMIT์˜ ๋น„ํšจ์œจ์ ์ธ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ

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

 

๊ฒŒ์‹œ๊ธ€ ID์— ํ•ด๋‹นํ•˜๋Š” ์ฝ˜ํ…์ธ  ์กฐํšŒ ์ฟผ๋ฆฌ ๋ถ„์„

์ฟผ๋ฆฌ ์‹คํ–‰ ์‹œ๊ฐ„: 393ms (excution: 22ms, fetching: 371ms)

ํ•ด๋‹น ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด EXPLAIN ANALYZE๋ฅผ ์ˆ˜ํ–‰ํ•œ ๊ฒฐ๊ณผ, ์•„๋ž˜์™€ ๊ฐ™์€  ์‹คํ–‰ ๊ณ„ํš์ด ์ถœ๋ ฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

ํ•ด๋‹น ์‹คํ–‰ ๊ณ„ํš์„ ๋ณด๋ฉด ํฌ๊ฒŒ ์ง€์—ฐ๋  ๋งŒํ•œ ํŠน๋ณ„ํ•œ ๋ถ€๋ถ„์€ ๋ˆˆ์— ๋„์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‹คํ–‰ ๊ณ„ํš ์ž์ฒด๋Š” ์ธ๋ฑ์Šค๋ฅผ ์ž˜ ํ™œ์šฉํ•˜๊ณ  ์žˆ์–ด ํฐ ๋ฌธ์ œ๊ฐ€ ์—†์–ด๋ณด์ด์ง€๋งŒ, ๊ฐ ๋‹จ๊ณ„๋ณ„๋กœ ์ฒ˜๋ฆฌ๋˜๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ ์–‘(Actual Rows)์ด ์ƒ๋‹นํžˆ ๋งŽ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. 

 

์ด์ฒ˜๋Ÿผ, ์ธ๋ฑ์Šค ์Šค์บ”์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ฐ€์ ธ์˜จ๋‹ค๊ณ  ํ•ด๋„, 20,000๊ฐœ๋ผ๋Š” ์ƒ๋‹นํ•œ ์–‘์„ ์ฝ์–ด์•ผ ํ•˜๋Š” ์ ์ด ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์ธ๋ฑ์Šค๋ฅผ ์ž˜ ํ™œ์šฉํ•˜๊ณ  ์žˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , ๊ฒฐ๊ตญ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๋ฐ์ดํ„ฐ์˜ ์ ˆ๋Œ€์ ์ธ ์–‘์ด ๋งŽ๋‹ค๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ถˆ์–ด๋‚œ ๋ฐ์ดํ„ฐ๋Š” ์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ์ „๋‹ฌ๋  ๋•Œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ฆ๊ฐ€๋กœ ์ด์–ด์ ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ์— ๋ถ€์ •์ ์ธ ์˜ํ–ฅ ๋˜ํ•œ ๋ฏธ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

์ฟผ๋ฆฌ ๊ฐœ์„ 

๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ ์ฟผ๋ฆฌ ๊ฐœ์„ 

SELECT 
    p.post_id as postId,
    p.post_title as postTitle,
    u.user_name as username,
    u.user_profile_image as userProfileImage,
    p.registered_date_time as postRegisteredDateTime,
    count(pl.id) as likeCount
FROM 
	post p
INNER JOIN 
	users u on u.user_id = p.user_id
INNER JOIN (
    SELECT post_id
    FROM post
    ORDER BY post_id desc
    LIMIT 10
    OFFSET 0
) as latest_post on latest_post.post_id = p.post_id
LEFT JOIN 
	post_like pl on pl.post_id = p.post_id
GROUP BY
	p.post_id, p.post_title, u.user_name, u.user_profile_image, p.registered_date_time;

๊ธฐ์กด ์ฟผ๋ฆฌ์—์„œ๋Š” ์ตœ์‹  ๊ฒŒ์‹œ๋ฌผ์„ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ๋ชจ๋“  post, user, post_like ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ธํ•œ ํ›„ ์ •๋ ฌํ•˜๊ณ  ์ง‘๊ณ„ํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ์ฟผ๋ฆฌ๊ฐ€  ๋งŽ์•„์งˆ์ˆ˜๋ก ์ „์ฒด ํ…Œ์ด๋ธ”์— ๋Œ€ํ•œ ์—ฐ์‚ฐ์ด ๋ฐœ์ƒํ•˜์—ฌ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋œ ์ƒํ™ฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๊ฐœ์„ ํ•˜๊ณ ์ž ์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋ธŒ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ, post_id๋ฅผ ๊ธฐ์ค€์œผ๋กœ limit์— ๋งž๊ฒŒ ๋จผ์ € ์„ ๋ณ„ํ•˜๋„๋ก ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

ํŠนํžˆ, ์ด ์„œ๋ธŒ์ฟผ๋ฆฌ๋Š” post_id ์ปฌ๋Ÿผ๋งŒ ์„ ํƒํ•˜๊ณ  post_id๋กœ ์ •๋ ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, post_id์— ์ด๋ฏธ ์ƒ์„ฑ๋˜์–ด ์žˆ๋Š” ํด๋Ÿฌ์Šคํ„ฐ๋ง ์ธ๋ฑ์Šค๊ฐ€ ์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค ์—ญํ• ์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์‹ค์ œ ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ํ•„์š” ์—†์ด ์ธ๋ฑ์Šค๋งŒ์œผ๋กœ ํ•„์š”ํ•œ post_id๋ฅผ ๋น ๋ฅด๊ฒŒ ์ฐพ์•„๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ฐพ์•„์ง„ ์ตœ์†Œํ•œ์˜ post_id ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€์ง€๊ณ  ๋‚˜๋จธ์ง€ ํ…Œ์ด๋ธ”๋“ค๊ณผ ์กฐ์ธ์„ ์ˆ˜ํ–‰ํ•จ์œผ๋กœ์จ, ๋ถˆํ•„์š”ํ•œ ๋Œ€๊ทœ๋ชจ ์กฐ์ธ ์—ฐ์‚ฐ์„ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

๊ฒŒ์‹œ๊ธ€ ID์— ํ•ด๋‹นํ•˜๋Š” ์ฝ˜ํ…์ธ  ์กฐํšŒ ์ฟผ๋ฆฌ ๊ฐœ์„ 

create index idx_content_post_id_type_id on content(post_id, content_type, content_id);
SELECT 
    c.content_id as contentId,
    c.post_id as postId,
    c.content_type as contentType,
    c.content_value as contentValue 
FROM 
	content c
INNER JOIN (
	SELECT 
    	MIN(c_min.content_id) as min_content_id
    FROM 
    	content c_min
    WHERE
    	c_min.post_id in (post_ids)
    GROUP BY
    	c_min.post_id, c_min.content_type
) as min_ids on content.id = min_ids.min_content_id;

๊ธฐ์กด ์ฟผ๋ฆฌ๋Š” ํŠน์ • post_ids์— ํ•ด๋‹นํ•˜๋Š” ๋ชจ๋“  ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ post_id๋‹น ์—ฌ๋Ÿฌ ์ฝ˜ํ…์ธ ๊ฐ€ ์žˆ์„ ๋•Œ, ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†ก๋ฐ›์•„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ ๋‹ค์‹œ ํ•„ํ„ฐ๋งํ–ˆ๊ธฐ์— ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๋Š” ๋น„ํšจ์œจ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” post_id์™€ content_type์œผ๋กœ ๊ทธ๋ฃนํ•‘๋œ ๋ฐ์ดํ„ฐ ์ค‘ ์ตœ์†Œ content_id๋ฅผ ๊ฐ€์ง„ ๋ ˆ์ฝ”๋“œ๋“ค์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ํ•„์š”๊ฐ€ ์ „ํ˜€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฟผ๋ฆฌ๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด content ํ…Œ์ด๋ธ”์— (post_id, content_type, content_id) ๋ณตํ•ฉ ์ธ๋ฑ์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์„œ๋ธŒ์ฟผ๋ฆฌ์—์„œ ์ด ์ธ๋ฑ์Šค๋ฅผ ์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค๋กœ ํ™œ์šฉํ•˜์—ฌ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ์„ ํšจ์œจ์ ์œผ๋กœ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

์‹คํ–‰ ๊ณ„ํš์„ ๋ณด๋ฉด content ํ…Œ์ด๋ธ”์— ๋Œ€ํ•œ ๋ถˆํ•„์š”ํ•œ ์ „์ฒด ์Šค์บ”์—†์ด, ์ธ๋ฑ์Šค๋งŒ์œผ๋กœ post_id ๋ฐ content_type์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ํ•„ํ„ฐ๋ง ํ•˜๊ณ  ํ•„์š”ํ•œ content_id๊นŒ์ง€ ๋น ๋ฅด๊ฒŒ ์กฐํšŒํ•˜๊ณ  MIN(content_id) ์ง‘๊ณ„๊ฐ€ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ธ๋ฑ์Šค๋งŒ์œผ๋กœ ์ด๋ฏธ ์ตœ์†Œํ™”๋œ ๋ฐ์ดํ„ฐ์…‹์„ ์ถ”์ถœํ•˜์—ฌ ์กฐ์ธํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ๋กœ ์ „์†กํ•˜๋Š” ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์—†์• ๊ณ  ์ฟผ๋ฆฌ ์†๋„๋„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

๊ฐœ์„   ๊ฒฐ๊ณผ

์œ„์™€ ๊ฐ™์ด ์ฟผ๋ฆฌ ๊ฐœ์„ ์„ ์™„๋ฃŒํ•œ ํ›„, K6 ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ๋‹ค์‹œ ์ˆ˜ํ–‰ํ•˜์—ฌ ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ ๋ณ€ํ™”๋ฅผ ์ธก์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

 

๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ์ง€ํ‘œ

๊ฐœ์„  ์ „

๊ฐœ์„  ํ›„

๊ฐœ์„  ์ „์—๋Š” HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค. ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„์ด 27.76s, p90 ์ง€ํ‘œ์—์„œ 34.59s๋กœ ์ „๋ฐ˜์ ์œผ๋กœ ๋†’์€ ์‘๋‹ต ์‹œ๊ฐ„์ด ์ง€์†๋˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์‘๋‹ต ์‹œ๊ฐ„๋ฌธ์ œ ์™ธ์—๋„ ์—๋Ÿฌ๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ํŒจํ„ด์„ ๋ณด์ด๋Š” ์ƒํ™ฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค. 

 

๊ฐœ์„  ํ›„์—๋Š” HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ํš๊ธฐ์ ์œผ๋กœ ๋‹จ์ถ•๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„์ด 5.84ms, p90 ์ง€ํ‘œ์—์„œ 7.43ms๋กœ ๋Œ€ํญ ๊ฐ์†Œํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ณผ๊ฑฐ ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ฐœ์ƒํ–ˆ๋˜ ์—๋Ÿฌ ๋ฐœ์ƒ๋ฅ ์ด ์™„์ „ํžˆ ์‚ฌ๋ผ์กŒ์Šต๋‹ˆ๋‹ค.

 

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ด€๋ จ ์ง€ํ‘œ

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

 

๋˜ํ•œ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ์—์„œ์˜ HikariCP Connection Pool ๋˜ํ•œ ๊ฐœ์„ ์„ ํ†ตํ•ด ํฐ ๋ณ€ํ™”๋ฅผ ๋ณด์˜€์Šต๋‹ˆ๋‹ค. ์ด์ „์—๋Š” 222ํšŒ์˜ ํƒ€์ž„์•„์›ƒ๊ณผ ์ตœ๋Œ€ 189๊ฐœ์˜ ๋Œ€๊ธฐ(Pending) ์ปค๋„ฅ์…˜์ด ๋ฐœ์ƒํ•˜์˜€์œผ๋‚˜, ์ด์ œ๋Š” ํƒ€์ž„์•„์›ƒ์ด ์ „ํ˜€ ์—†๊ณ  ๋Œ€๊ธฐ ์ปค๋„ฅ์…˜๋„ ์—†๋Š” ์•ˆ์ •์ ์ธ ์ปค๋„ฅ์…˜ ํ’€ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋„๋ก ๊ฐœ์„ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. 

 

์ตœ๋Œ€ ๋ถ€ํ•˜ ์„ฑ๋Šฅ

๊ฐœ์„ ๋œ ์‹œ์Šคํ…œ์˜ ์ตœ๋Œ€ ์„ฑ๋Šฅ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด 6,000 VUs ํ™˜๊ฒฝ์—์„œ K6 ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

3.5K VUs ์—์„œ RPS๊ฐ€ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š์Œ

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ, ์•ฝ 3.5K VUs๊นŒ์ง€๋Š” RPS๊ฐ€ ์ง€์†์ ์œผ๋กœ ์ฆ๊ฐ€ํ–ˆ์ง€๋งŒ, ๊ทธ ์ดํ›„๋ถ€ํ„ฐ๋Š” ๋” ์ด์ƒ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ํฌํ™” ์ƒํƒœ์— ๋„๋‹ฌํ•˜๋Š” ์–‘์ƒ์ด ๊ด€์ธก๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋”ฐ๋ผ ์ตœ๋Œ€ ๋ถ€ํ•˜ ์ง€์ ์„ 3.5K VUs ์ˆ˜์ค€์œผ๋กœ ํŒ๋‹จํ•˜๊ณ  ํ•ด๋‹น ์ง€ํ‘œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ธก์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

 

ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„ ๊ฐœ์„ : 27.76s → 15.16ms (1830% ์‘๋‹ต ์‹œ๊ฐ„ ๊ฐœ์„ )
ํ‰๊ท  QPS: 8.75 → 15.5K
CPU ์ตœ๋Œ€ ์‚ฌ์šฉ๋ฅ : 92.9% → 23.1%
์ฃผ์š” ์„ฑ๋Šฅ ์ง€ํ‘œ ๋น„๊ต

ํ•ด๋‹น ๊ฐœ์„  ์ž‘์—…์„ ํ†ตํ•ด, ์ด์ „์— ๋ฐœ์ƒํ•˜๋˜ ์‹ฌ๊ฐํ•œ ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฌธ์ œ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ํ•ด๊ฒฐํ•˜์˜€์œผ๋ฉฐ, 3,500 VUs๊นŒ์ง€ ์•ˆ์ •์ ์ธ RPS ์ฆ๊ฐ€์™€ ํ•จ๊ป˜ ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„ 99.95% ๋‹จ์ถ•์ด๋ผ๋Š” ์„ฑ๊ณผ๋ฅผ ๋‹ฌ์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

๋˜ํ•œ, ํ‰๊ท  QPS๋Š” ๊ธฐ์กด 8.75์—์„œ 15.5K๋กœ ์•ฝ 1,771๋ฐฐ ์ฆ๊ฐ€ํ•˜์˜€์œผ๋ฉฐ, ๋” ๋งŽ์€ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋ฉด์„œ๋„ CPU ์ตœ๋Œ€ ์‚ฌ์šฉ๋ฅ ์ด 92.9%์—์„œ 23.1%๋กœ ํฌ๊ฒŒ ๊ฐ์†Œํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ๋”ํ•ด ์˜ค๋ฅ˜์œจ 0%๋ฅผ ์œ ์ง€ํ•จ์œผ๋กœ์จ, ์„œ๋น„์Šค์˜ ์ฒ˜๋ฆฌ๋Ÿ‰, ์•ˆ์ •์„ฑ, ์ž์› ํšจ์œจ์„ฑ ๋ชจ๋‘๊ฐ€ ํฌ๊ฒŒ ํ–ฅ์ƒ๋˜์—ˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

'Project' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

GitHub Actions + Docker + NGINX๋ฅผ ํ™œ์šฉํ•œ Blue/Green ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ํ™˜๊ฒฝ ๊ตฌ์ถ•  (1) 2025.05.04
์ฑ„ํŒ… ๋ฉ”์„ธ์ง€ ์ฝ์Œ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ ๊ตฌ์กฐ ๊ฐœ์„ ๊ธฐ  (0) 2024.12.18
Embedded Mongo/Redis ์ ์šฉํ•˜๊ธฐ  (0) 2024.11.26
๋ณ„์  ์ค‘๋ณต ์ƒ์„ฑ ๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐ  (0) 2024.10.27
CompletableFuture๋ฅผ ํ™œ์šฉํ•œ ์„ฑ๋Šฅ ๊ฐœ์„ ๊ธฐ  (0) 2024.08.01
'Project' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • GitHub Actions + Docker + NGINX๋ฅผ ํ™œ์šฉํ•œ Blue/Green ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ํ™˜๊ฒฝ ๊ตฌ์ถ•
  • ์ฑ„ํŒ… ๋ฉ”์„ธ์ง€ ์ฝ์Œ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ ๊ตฌ์กฐ ๊ฐœ์„ ๊ธฐ
  • Embedded Mongo/Redis ์ ์šฉํ•˜๊ธฐ
  • ๋ณ„์  ์ค‘๋ณต ์ƒ์„ฑ ๋™์‹œ์„ฑ ์ด์Šˆ ํ•ด๊ฒฐ
jwooo๐ŸŒฑ
jwooo๐ŸŒฑ
jwooo's log ์ž…๋‹ˆ๋‹ค.
  • jwooo๐ŸŒฑ
    jwooo's log
    jwooo๐ŸŒฑ
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (12)
      • Java (4)
      • Project (6)
      • Computer Science (2)
        • Network (1)
        • Security (1)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.3
jwooo๐ŸŒฑ
๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋กœ ์ธํ•œ API ์„ฑ๋Šฅ ๋ฌธ์ œ ํ•ด๊ฒฐ: ์ปค๋ฒ„๋ง ์ธ๋ฑ์Šค ๋ฐ ์ธ๋ฑ์Šค ํŠœ๋‹์„ ํ†ตํ•œ ์ตœ์ ํ™”
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”