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๊ฑด์ฉ ๋ถ์ฐ๋๋ค.


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% | โ ๏ธ ์คํจ์จ ์ด๊ณผ |




ํตํฉ ์๋๋ฆฌ์ค ๊ฒฐ๊ณผ
ํตํฉ ํ ์คํธ์์๋ ๋จ๋ ํ ์คํธ์ ๋ฌ๋ฆฌ ๊ฐ ์๋๋ฆฌ์ค์ ์ต๋ ๋ถํ๋ฅผ 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()๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.


์ด ๋ฐฉ์์ ์ค๋ณต ์์ฒญ์ด ๋์์ ๋ค์ด์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ต์ข
์ ์ผ๋ก ์ค๋ณต ์ ์ฅ์ ๋ฐฉ์งํ๋ค. ๋์์ ๋งค ์์ฒญ๋ง๋ค ์คํ๋๋ 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 ์๋ต๊ณผ ์ค์ ์๋ฒ ์ค๋ฅ๋ฅผ ๋ถ๋ฆฌํ๊ณ , ํ๊ณ ๊ตฌ๊ฐ๋ ์ ๋์ ์ผ๋ก ๋ค์ ๊ฒ์ฆํ ๊ณํ์ด๋ค.
'๐ป PROJECT > [Spring Boot, React] ๋ ์ ์ต๊ด ๊ด๋ฆฌ ์๋น์ค' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [๊ตฌํ ๊ธฐ๋ก] CustomException + ErrorCode ๊ตฌ์กฐ ์ค๊ณ (0) | 2026.05.22 |
|---|---|
| [์ค๊ณ์์ฌ๊ฒฐ์ ] ์๋ฆฌ์ฆ ๋ชฉ๋ก (0) | 2026.05.21 |
| [์ค๊ณ์์ฌ๊ฒฐ์ ] JWT + Redis ์ธ์ฆ (0) | 2026.05.13 |
| [ํ๋ก์ ํธ ๊ฐ์] ํ๋ก์ ํธ ์๊ฐ ๋ฐ ๊ธฐํ (0) | 2026.05.12 |
| [์ค๊ณ์์ฌ๊ฒฐ์ ] ๊ตญ๋ด ๋์ ๊ฒ์ API ์ ํ ๊ณผ์ (0) | 2026.05.08 |