
๋ฌธ์ ์ํฉ

์ฌ์ฉ์์๊ฒ ์ค์๊ฐ ์๋ฆผ์ ์ ๊ณตํ๊ธฐ ์ํด SSE๋ฅผ ์ฌ์ฉํ๊ณ , ์๋ฆผ ์ ์ก ๋ก์ง์ Spring Event๋ฅผ ํตํด ๊ตฌํํ์ต๋๋ค. ํ์ง๋ง 1000๋ช ์ด ์ฐ๊ฒฐ์ด ๋์ด ์๋ ์ํ์์ 1000๋ช ์ ์ฌ์ฉ์๊ฐ 10๋ง๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๋ ๋ถํ ํ ์คํธ ๊ฒฐ๊ณผ, ์๋ต ์๊ฐ์ ์ฌ๊ฐํ ์ง์ฐ๊ณผ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. ๊ฒ๋ค๊ฐ ์๋ฆผ ๋ฉ์ธ์ง ์ฒ๋ฆฌ ๊ณผ์ ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด, ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ ์๋ณธ ํธ๋์ญ์ ๊น์ง ๋กค๋ฐฑ๋๋ ๋ฌธ์ ๊ฐ ํ์ธ๋์์ต๋๋ค.
๋ฌธ์ ๋ถ์

๋ฌธ์ ์ ์์ธ์ ์ฐพ๊ธฐ ์ํด ๋ชจ๋ํฐ๋ง ๊ฒฐ๊ณผ, ๋ถํ ๋ฐ์ ์ HikariCP์ Active ์ปค๋ฅ์ ์์น๊ฐ ์ต๋์น์ ๋๋ฌํ๊ณ , DB ์ปค๋ฅ์ ์ ํ ๋น๋ฐ๊ธฐ ์ํด ๋๊ธฐํ๋ Pending ์ค๋ ๋์ ์๊ฐ ๊ธ์ฆํ๋ ํ์์ ํ์ธํ์ต๋๋ค. ์ด๋ DB ์ปค๋ฅ์ ์ด ์ฅ๊ธฐ๊ฐ ์ ์ ๋์ด ํ์ ์์ฒญ๋ค์ด ์ฒ๋ฆฌ๋์ง ๋ชปํ๊ณ ๋ณ๋ชฉ ํ์์ ๊ฒช๊ณ ์์์ ํ์ธํ์ต๋๋ค.
๊ทผ๋ณธ ์์ธ

๋ฌธ์ ์ ํต์ฌ ์์ธ์ Spring Event์ ๋๊ธฐ์ ์คํ ๋ฐฉ์๊ณผ ํธ๋์ญ์ ๋ฒ์์ ์์์ต๋๋ค.
@Component
@RequiredArgsConstructor
public class NotificationEventListener {
private final NotificationService notificationService;
private final SseService sseService;
@EventListener
public void handleNotificationEvent(NotificationEvent event) {
notificationService.createNotification(event);
sseService.send(event);
}
}
ํ์ฌ ์ฝ๋์์๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ ์ด์ด์์ @EventListener๋ฅผ ์ฌ์ฉํด ์ถ๊ฐ์ ์ธ ์์ ์ ํธ์ถํ๊ณ ์์์ต๋๋ค. ์ด๋ ๋ณ๋์ ์ค์ ์ ํ์ง ์์ผ๋ฉด @EventListener๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํจ ๋ถ๋ชจ ์ค๋ ๋์์ ๋๊ธฐ์ ์ผ๋ก ์คํ๋ฉ๋๋ค. ์ด๋ก ์ธํด ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ ๋ถ๋ชจ ๋ฉ์๋์ ํธ๋์ญ์ ์ ๊ทธ๋๋ก ์ฐธ์ฌํ๊ฒ ๋ฉ๋๋ค.
์ด๋ฌํ ๊ตฌ์กฐ ๋๋ฌธ์, ๋ถ๋ชจ ํธ๋์ญ์ ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๋ด๋ถ์ ๋ชจ๋ ๋ก์ง(SSE ์๋ฆผ ์ ์ก, ์๋ฆผ ์ ์ฅ)์ด ์๋ฃ ๋ ๋๊น์ง ์ข ๋ฃ๋์ง ์๊ณ DB ์ปค๋ฅ์ ์ ๊ณ์ ์ ์ ํ๊ฒ ๋ฉ๋๋ค. ์ด๋ก ์ธํด ์ปค๋ฅ์ ์ ์ ์ ์๊ฐ์ด ๋ถํ์ํ๊ฒ ๊ธธ์ด์ง๋ฉด์, ์ปค๋ฅ์ ํ ๊ณ ๊ฐ์ ์ด๋ํ์ต๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก, ํ์ ์์ฒญ์ ์ฒ๋ฆฌํด์ผ ํ ๋ค๋ฅธ ์ค๋ ๋๋ค์ด ์ปค๋ฅ์ ์ ํ ๋น๋ฐ์ง ๋ชปํ๊ณ ๋๊ธฐํ๋ ์๊ฐ์ด ๊ธ์ฆํ์ฌ, ์๋ต ์ง์ฐ๊ณผ ๋ณ๋ชฉ ํ์์ด ๋ฐ์ํ์ต๋๋ค.
๊ฐ์ ๋ฐฉ์
1์ฐจ ๊ฐ์

@Component
@RequiredArgsConstructor
public class NotificationEventListener {
private final NotificationService notificationService;
private final SseService sseService;
@Async("notificationExecutor")
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleNotificationEvent(NotificationEvent event) {
notificationService.createNotification(event);
sseService.send(event);
}
}
์์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด @EventListener๋ฅผ @TransactionalEventListener(phase = AFTER_COMMIT)์ผ๋ก ๋ณ๊ฒฝํ์ฌ, ๋ถ๋ชจ ํธ๋์ญ์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์ปค๋ฐ๋ ์ดํ์๋ง ์ด๋ฒคํธ๊ฐ ์ฒ๋ฆฌ๋๋๋ก ๋ณด์ฅํ์๊ณ @Async ์ด๋ ธํ ์ด์ ์ ์ถ๊ฐํ์ฌ ์ด๋ฒคํธ ์ฒ๋ฆฌ๊ฐ ๋ณ๋์ ์ค๋ ๋ ํ์์ ๋น๋๊ธฐ์ ์ผ๋ก ์คํ๋๋๋ก ๋ณ๊ฒฝํ์์ต๋๋ค.
1์ฐจ ๊ฐ์ ๊ฒฐ๊ณผ


1์ฐจ ๊ฐ์ ์ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ๋ก์ง๊ณผ ์๋ฆผ ์ฒ๋ฆฌ ๋ก์ง์ ์ค๋ ๋์ ํธ๋์ญ์ ์ ๋ถ๋ฆฌํจ์ผ๋ก์จ DB ์ปค๋ฅ์ ์ ์ ์ ํ๋ ์๊ฐ์ ์ค์ฌ API ์๋ต ์๊ฐ์ ๊ฐ์ ๋์์ง๋ง, ์ด๋ ๋ฌธ์ ์ ์ผ๋ถ๋ง์ ํด๊ฒฐํ์ ๋ฟ์ด๋ฉฐ, ์ฌ์ ํ ์๋์ ๊ฐ์ ๋ฌธ์ ๋ค์ด ๋จ์์์์ต๋๋ค.
1์ฐจ ๊ฐ์ ๋ฌธ์ ์

๊ฐ์ ๋ ๋ฐฉ์์ ๋ค์์ ์ค๋ ๋๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ ๊ณผ์ ์์ ๋ฐ์ํ๋ ์ปจํ ์คํธ ์ค์์นญ ์ค๋ฒํค๋๋ก ์ธํด, ๋๊ธฐ ๋ฐฉ์์ ๋นํด ๋ ๋์ CPU ์ฌ์ฉ๋์ ๋ณด์์ต๋๋ค. ๋ํ, ์๋ฆผ ์์ฒญ์ด ์ง์ค๋ ๊ฒฝ์ฐ, API ์๋ฒ์ CPU ๋ฆฌ์์ค๊ฐ ๊ณผ๋ํ๊ฒ ์๋ชจ๋๋ ํ์์ด ๋ฐ์ํ์ต๋๋ค.

๋ํ, SSE ์ฐ๊ฒฐ์ API ์๋ฒ์ Tomcat ์ปค๋ฅ์ ์ ์ฅ๊ธฐ๊ฐ ์ ์ ํฉ๋๋ค. ์ด๋ฌํ ์ํฉ์์ ๋ค์์ SSE ํด๋ผ์ด์ธํธ๊ฐ ์ฐ๊ฒฐ์ ์ ์งํ๊ณ ์์ ๋, ๊ทธ ๋งํผ ์ฐ๊ฒฐํ ์ ์๋ ์ปค๋ฅ์ ์ด ์ค์ด๋ค๊ฒ ๋๊ณ , ๋น๋๊ธฐ ์๋ฆผ ์ฒ๋ฆฌ๊น์ง ์ฆ๊ฐํ๋ค๋ฉด, ๊ฐ์ฉ ์ค๋ ๋๊ฐ ๊ณ ๊ฐ๋์ด ์๋ก์ด API ์์ฒญ์ ์ฒ๋ฆฌํ์ง ๋ชปํ๋ ์ฅ์ ๋ก ์ด์ด์ง ์ ์์์ต๋๋ค.
2์ฐจ ๊ฐ์
1์ฐจ ๊ฐ์ ์ผ๋ก API ์๋ต ์๋๋ ๊ฐ์ ํ์ง๋ง, ์๋ฒ์ CPU ๋ฐ ์ค๋ ๋ ๊ณ ๊ฐ์ ๋ํ ์ํ์ ์ฌ์ ํ ๋จ์์์์ต๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์๋ฆผ ์ฒ๋ฆฌ ์์คํ ์ API ์๋ฒ๋ก๋ถํฐ ์์ ํ ๋ถ๋ฆฌํ๋ ์ํคํ ์ฒ ๋ณ๊ฒฝ์ ๊ฒฐ์ ํ์์ต๋๋ค.
๋ ์์คํ ์ ์์ ์ ์ผ๋ก ์ฐ๊ฒฐํ๊ธฐ ์ํ ํต์ฌ ๊ธฐ์ ๋ก ๋ฉ์ธ์ง ํ๋ฅผ ๋์ ํ๊ณ , ์ฌ๋ฌ ์ ํ์ง ์ค RabbitMQ๋ฅผ ์ต์ข ์ ํํ๊ฒ ๋์์ต๋๋ค.
RabbitMQ๋ฅผ ์ ํํ ์ด์
๋ฉ์ธ์ง ํ ๋์ ์ ๊ฒฐ์ ํ์ ๋, RabbitMQ์ Kafka ์ค ์ด๋ค ๊ฒ์ ์ ํํ ์ง ๊ณ ๋ฏผํ์ต๋๋ค. ์ ํฌ์ ์๊ตฌ์ฌํญ์ SSE ์๋ฒ์ ์๋ฆผ์ ์ค์๊ฐ์ผ๋ก ์ ์กํ๋ ๊ฒ์ด์๊ธฐ ๋๋ฌธ์, ๊ฐ ์์คํ ์ ํน์์ค๊ฐ ์ฅ๋จ์ ์ ๋น๊ตํ ํ ๊ฒฐ์ ํ์ต๋๋ค.
RabbitMQ๋ Smart Broker ๋ชจ๋ธ๋ก, ๋ธ๋ก์ปค๊ฐ ๋ฉ์ธ์ง ์ปจ์๋จธ์๊ฒ ์ ๊ทน์ ์ผ๋ก ๋ฐ์ด์ฃผ๋ Push ๋ฐฉ์์ ๋๋ค. ์ด๋ ๋ฉ์ธ์ง๊ฐ ๋์ฐฉํ๋ ์ฆ์ ์ฒ๋ฆฌํด์ผํ๋ ์๊ตฌ์ฌํญ๊ณผ ๋ถํฉํ์ง๋ง, Kafka๋ Dumb Broker ๋ชจ๋ธ๋ก, ์ปจ์๋จธ๊ฐ ์ฃผ๊ธฐ์ ์ผ๋ก ๋ธ๋ก์ปค์๊ฒ ์ ๋ฉ์ธ์ง๋ฅผ ์์ฒญํด์ผ ํ๋ Pull ๋ฐฉ์์ผ๋ก ๋์ฉ๋ ๋ฐ์ดํฐ์ ์ผ๊ด ์ฒ๋ฆฌ์ ๋ ์ต์ ํ๋ ๋ชจ๋ธ์ด๋ผ๊ณ ํ๋จํ์ต๋๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก ์ง์ฐ ์๊ฐ์ด ํต์ฌ์ธ SSE์๋ RabbitMQ๊ฐ ๋ ์ ํฉํ๋ค๊ณ ํ๋จํ์ต๋๋ค.
์ํคํ ์ฒ

API ์๋ฒ๋ ๋น์ฆ๋์ค ๋ก์ง ์ฒ๋ฆฌ ํ, ์๋ฆผ์ ํ์ํ ์ ๋ณด๋ฅผ Spring Event๋ก ๋๊ธฐ๊ณ ์๋ฆผ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ํ RabbitMQ๋ก ๋ฐํํฉ๋๋ค. SSE ์๋ฒ์์๋ ํด๋น ํ๋ฅผ ๊ตฌ๋ ํ๊ณ ์๋ค๊ฐ ์ ์ฅ๋ ๋ฉ์ธ์ง๊ฐ ํ์ ์ ์ก๋๊ฒ ๋๋ฉด, ์ด๋ฅผ ๊ฐ์ ธ์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ํ, ํด๋น ์ฌ์ฉ์์๊ฒ SSE ์ฐ๊ฒฐ์ ํตํด ์๋ฆผ์ ์ ์กํ๊ฒ ๋ฉ๋๋ค.
๊ฐ์ ๊ฒฐ๊ณผ


- RPS: 697 → 951 (36.4% ์ฆ๊ฐ)
- ์๋ต ์๊ฐ (median): 910.49ms → 56.32ms (93.8% ๊ฐ์)
- ์๋ต ์๊ฐ (mean): 1.08s → 83.73ms (92.2% ๊ฐ์)
- ์๋ต ์๊ฐ (p90): 1.83s → 104.47ms (94.3% ๊ฐ์)
- ์๋ต ์๊ฐ (p95): 2.60s → 159.16ms (93.9% ๊ฐ์)
์ด๋ฒ ๊ฐ์ ์ ํตํด SSE ์๋ฆผ ๊ธฐ๋ฅ์ด ์ ์ฒด ์์คํ ์ฑ๋ฅ์ ๋ฏธ์น๋ ๋ถ๋ด์ ๊ทผ๋ณธ์ ์ผ๋ก ์ ๊ฑฐํ ์ ์์๊ณ , ์์ ์ฑ๊ณผ ํ์ฅ์ฑ์ ๋์์ ํ๋ณดํ ์ ์์์ต๋๋ค. ํนํ API ์๋ฒ์ SSE ์๋ฒ๋ฅผ ๋ถ๋ฆฌํ์ฌ SSE์ ๋ํ ๋ถํ๊ฐ ์ฆ๊ฐํ๋๋ผ๋ ๋ค๋ฅธ ์๋น์ค์ ์ํฅ์ ์ฃผ์ง์๊ณ ๋ ๋ฆฝ์ ์ผ๋ก ํ์ฅํ ์ ์๋ ๊ธฐ๋ฐ์ด ๋ง๋ จ๋์์ต๋๋ค.