1. 프로젝트 배경
이전 포스팅에서 SSE(Server-Sent Events)에 대한 기본 개념과 사용 사례를 설명드렸습니다. 이번에는 실전에서 SSE를 활용해 실시간 알림 기능을 구축하면서 겪은 문제와 그 해결 과정을 공유하고자 합니다. 이 글은 실시간 알림이 필요한 상황에서 SSE를 어떻게 효과적으로 적용하고, 성능 문제를 어떻게 해결했는지를 중심으로 작성하고 있습니다.
2. 문제 상황: 다수의 연결 시도로 인한 서버 성능 저하
알림 시스템을 구현할 때 가장 큰 도전 과제는 동시에 많은 사용자가 접속해 실시간으로 알림을 받을 때 발생하는 서버 부하였습니다. 특히, 프로젝트 초기에는 사용자의 연결이 끊길 때마다 자동으로 재연결을 시도하게 설정해 놓았는데, 이로 인해 다수의 사용자가 알림을 동시에 요청하면 서버에 부담이 가중되었습니다. 더불어 각 사용자에게 지속적으로 데이터베이스 세션을 열어 두는 방식으로 인해 연결 유지에 따른 리소스 소비가 상당히 높아졌습니다.
Before: 초기 SSE 구현 코드
초기에는 사용자 연결이 끊길 때마다 즉시 자동 재연결이 이루어지도록 구현되어 있었으며, 서버 부하를 고려하지 않은 방식이었습니다.
// 초기 SSE 연결 설정
document.addEventListener("DOMContentLoaded", () => {
// 서버와의 SSE 연결 설정
const eventSource = new EventSource("http://localhost:3000/sse");
// 서버로부터 메시지를 받을 때마다 실행
eventSource.onmessage = (event) => {
const notificationData = JSON.parse(event.data);
displayNotification(notificationData);
};
// 연결 오류 발생 시 즉시 재연결 시도
eventSource.onerror = () => {
console.error("연결이 끊어졌습니다. 재연결을 시도합니다.");
// 즉시 새 EventSource로 다시 연결
eventSource = new EventSource("http://localhost:3000/sse");
};
});
// 알림을 화면에 표시하는 함수
function displayNotification(data) {
const notificationContainer = document.getElementById("notification-container");
const notificationElement = document.createElement("div");
notificationElement.classList.add("notification");
notificationElement.textContent = `새로운 알림: ${data.message}`;
notificationContainer.appendChild(notificationElement);
}
3. 문제 해결 과정
- 데이터베이스 세션 관리 최적화
실시간 알림의 핵심은 효율적인 세션 관리에 있습니다. 처음에는 사용자의 알림 수신을 위해 각 세션을 지속적으로 열어 두었으나, 이는 다수의 연결을 처리할 때 과도한 자원을 소모하게 했습니다. 이를 개선하기 위해, 알림이 필요할 때만 데이터베이스 세션을 열고, 작업이 완료되면 즉시 종료하는 방식으로 전환했습니다. 이로써 세션 유지에 따른 리소스 사용량을 크게 줄일 수 있었습니다. - SSE 재연결 로직 최적화
사용자의 네트워크 상황이 불안정하거나 일시적인 연결 끊김이 발생할 때를 대비해, 최대 3번까지 자동으로 재연결하도록 설정했습니다. 이때 재연결 시도를 간격을 두고 하여 서버 자원이 한 번에 몰리지 않도록 조정했습니다. 만약 재연결이 실패할 경우, 알림을 통해 사용자에게 연결 실패를 안내함으로써 불필요한 자원 낭비를 방지할 수 있었습니다. 이를 통해 서버 자원 효율성을 높이고, 사용자 입장에서도 안정적인 알림 수신 환경을 제공할 수 있었습니다.
After: 최적화된 SSE 구현 코드
최적화된 코드에서는 재연결을 최대 3번까지만 시도하도록 변경하고, 각 재연결 사이에 일정 간격을 두어 서버 부하를 줄였습니다.
// 최적화된 SSE 연결 설정
document.addEventListener("DOMContentLoaded", () => {
let reconnectAttempts = 0;
const maxReconnectAttempts = 3;
// SSE 연결 함수 정의
function initializeSSE() {
const eventSource = new EventSource("http://localhost:3000/sse");
// 서버로부터 메시지를 받을 때마다 실행
eventSource.onmessage = (event) => {
const notificationData = JSON.parse(event.data);
displayNotification(notificationData);
};
// 연결 오류 발생 시 재연결 시도
eventSource.onerror = () => {
console.error("연결이 끊어졌습니다.");
if (reconnectAttempts < maxReconnectAttempts) {
setTimeout(() => {
console.log("재연결을 시도합니다.");
reconnectAttempts++;
initializeSSE(); // 재연결 시도
}, 3000); // 3초 후 재연결 시도
} else {
alert("연결 실패. 알림을 수신할 수 없습니다.");
}
};
}
// SSE 초기화
initializeSSE();
});
// 알림을 화면에 표시하는 함수
function displayNotification(data) {
const notificationContainer = document.getElementById("notification-container");
const notificationElement = document.createElement("div");
notificationElement.classList.add("notification");
notificationElement.textContent = `새로운 알림: ${data.message}`;
notificationContainer.appendChild(notificationElement);
}
4. 주요 개선 사항
- 재연결 시도 횟수 제한: maxReconnectAttempts를 설정해 최대 3번까지만 재연결을 시도하도록 제한했습니다.
- 재연결 지연 시간 추가: 각 재연결 시도마다 setTimeout을 통해 3초의 대기 시간을 두어 서버의 부하를 줄였습니다.
- 재연결 실패 시 사용자 안내: 3번 시도 후에도 연결되지 않을 경우 사용자에게 알림을 제공해 상태를 인지할 수 있도록 했습니다.
5. 결과 및 효과
이러한 최적화 작업 이후, 알림 시스템의 안정성과 성능이 크게 향상되었습니다. 데이터베이스 세션을 효율적으로 관리하여 리소스 사용량을 줄였고, 재연결 로직을 통해 끊김 없는 사용자 경험을 제공할 수 있었습니다. 서버 부하를 낮추고, 사용자에게 실시간으로 알림을 전송하는 기능을 안정적으로 유지함으로써 사용자 만족도를 높이는 데 성공했습니다.
느낀 점
이번 경험을 통해 실시간 알림이 필요한 상황에서 SSE를 더욱 효율적으로 활용할 수 있었으며, 특히 자원 관리의 중요성을 실감했습니다. 이와 같은 실전 경험이 실시간 알림 시스템을 구축하고 운영하는 분들께 도움이 되기를 바랍니다 : >