롱 폴링이란?
클라이언트가 정기적인 간격으로 서버에 반복적으로 데이터를 요청하는 전통적인 폴링과 달리, 롱 폴링은 새로운 데이터를 사용할 수 있을 때까지 서버와의 연결을 열려 있는 상태로 유지합니다. 서버에 새 정보가 생기면 클라이언트에 응답을 보내고 연결이 닫힙니다. 서버의 응답을 받자마자 클라이언트는 새로운 요청을 보내는 과정이 반복됩니다.
이를 통해 데이터가 갱신되면 즉시 클라이언트에 전달되며, 불필요한 네트워크 트래픽을 줄일 수 있습니다. 그러나 여전히 통신 지연이 발생할 수 있고, 웹 소켓과 같은 기술에 비해 비효율적일 수 있습니다.
// 자바스크립트 클라이언트의 롱 폴링
function longPoll() {
fetch('http://example.com/poll')
.then(response => response.json())
.then(data => {
console.log("Received data:", data);
longPoll(); // 즉시 새로운 롱 폴링 요청을 설정
})
.catch(error => {
/**
* 에러는 정상적인 조건에서 연결 시간을 초과하거나
* 클라이언트가 오프라인 상태가 될 때 발생할 수 있습니다.
* 에러가 발생하면 약간의 지연 후에 다시 폴링을 시작합니다.
*/
setTimeout(longPoll, 10000);
});
}
longPoll(); // 롱 폴링을 시작합니다.
클라이언트 사이드에서 롱 폴링을 구현하는 것은 위 코드처럼 매우 간단하지만 서버에서는 클라이언트가 모든 이벤트를 받았는지 확인하고 재연결 시 업데이트가 누락되지 않도록 처리해야 하는 복잡성이 따릅니다.
웹 소켓이란?
웹 소켓은 클라이언트와 서버 간의 단일 연결을 통해 양방향 통신을 가능하게 하며, 라이브 채팅, 게임, 금융 거래 플랫폼처럼 실시간 데이터 전송이 중요한 애플리케이션에 적합합니다. 연결이 설정되면 양쪽에서 자유롭게 데이터를 주고받을 수 있어 지연 시간이 적고, 즉각적인 업데이트가 가능합니다.
// 자바스크립트 클라이언트의 웹 소켓
const socket = new WebSocket('ws://example.com');
socket.onopen = function(event) {
console.log('Connection established');
// 서버에 메시지 보내기
socket.send('Hello Server!');
};
socket.onmessage = function(event) {
console.log('Message from server:', event.data);
};
웹 소켓은 사용이 쉬운 API를 제공하지만, 연결 유지, 끊김 감지 및 재연결 처리가 까다롭습니다. 이를 보완하기 위해 Socket.IO와 같은 라이브러리가 주로 사용됩니다.
Server-Sent Events (SSE)란?
SSE는 HTTP를 통해 서버에서 클라이언트로 실시간 데이터를 푸시할 수 있는 단방향 통신 방식입니다. 클라이언트에서 서버로의 데이터 전송이 필요 없는 경우 이상적이며, 뉴스 피드, 스포츠 점수 등 실시간으로 데이터를 받아야 하는 상황에 적합합니다.
// 서버 측 이벤트 스트림에 연결
const evtSource = new EventSource("https://example.com/events");
// 일반 메시지 이벤트 처리
evtSource.onmessage = event => {
console.log('got message: ' + event.data);
};
웹 소켓과 다르게, EventSource는 연결이 끊겼을 때 자동으로 연결합니다.
서버 측에서는 Content-Type을 text/event-stream으로 설정해야 하며, 서버에서 각 메시지를 보내는 방식이 표준화되어 있습니다.
Node.js Express 애플리케이션에서 간단한 SSE 엔드포인트를 설정하는 방법입니다.
import express from 'express';
const app = express();
const PORT = process.env.PORT || 3000;
app.get('/events', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
const sendEvent = (data) => {
// 모든 메시지 줄은 'data: '로 시작해야 합니다.
const formattedData = `data: ${JSON.stringify(data)}\n\n`;
res.write(formattedData);
};
// 2초마다 이벤트를 보냅니다.
const intervalId = setInterval(() => {
const message = {
time: new Date().toTimeString(),
message: 'Hello from the server!',
};
sendEvent(message);
}, 2000);
// 연결이 종료되면 정리합니다.
req.on('close', () => {
clearInterval(intervalId);
res.end();
});
});
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
SSE는 연결이 끊어지면 자동으로 재연결되며, HTTP 기반이라 방화벽과 프록시 환경에서도 유리합니다.
기술의 비교 및 사용 사례
- 지연 시간
- 롱 폴링: HTTP 연결을 반복적으로 생성하므로 지연이 발생할 수 있음.
- 웹 소켓: 지속적인 양방향 연결로 지연이 거의 없음.
- SSE: 서버에서 클라이언트로 단방향 데이터 전송 시 지연이 적음.
- 처리량
- 롱 폴링: 자주 연결을 열고 닫아 서버 자원 소모가 큼.
- 웹 소켓: 지속 연결로 고빈도 데이터 전송이 가능하나 서버 부하가 발생할 수 있음.
- SSE: 단방향 데이터 스트리밍에서 효율적이며 서버 확장성 우수.
- 확장성 및 서버 부하
- 롱 폴링: 확장성이 낮아 대안으로 주로 사용.
- 웹 소켓: 다수의 연결로 서버 부하 발생 가능.
- SSE: 단방향 통신에 최적화되어 서버 부하가 적음.
느낀 점
제가 구현했던 프로젝트는 실시간 알림 기능이 필요했고, 서버와 지속적으로 연결된 상태에서 클라이언트가 콘텐츠를 생성하면 서버가 단방향으로 알림을 보내는 구조였기 때문에 Server-Sent Events (SSE)를 선택했습니다. SSE는 서버에서 클라이언트로의 단방향 푸시 알림에 최적화되어 있고, HTTP 기반이라 추가적인 방화벽 설정 없이도 통신이 안정적이어서 요구사항에 적합한 기술이었습니다.
다음 글은 실제 적용했던 사례에 대해 다뤄볼 예정입니다.
'TIL' 카테고리의 다른 글
가챠권 소모 로직과 공정한 랜덤 확률 시스템 구현: 사용자 재방문율 20% 향상 (0) | 2024.12.17 |
---|---|
[테오스프린트] 테오스프린트 18기를 시작하며 (0) | 2024.12.04 |
주니어 개발자의 코드 리뷰 문화 도입시키기 (0) | 2024.10.24 |
Component-Driven Development(CDD): 효율적인 UI 개발의 핵심 (2) | 2024.10.14 |
프론트엔드 개발자가 백엔드를 고민하게 된 이유: MERN 스택과 Node.js, Nest.js의 차이점 (2) | 2024.10.08 |