FE 화면을 구성하다 보면 지속적으로 데이터를 갱신해야 하는 요구사항이 생기고는 합니다.
일반적으로 실시간성 데이터 갱신을 구현하기 위해서는 아래 두 가지을 고민 하게 됩니다.
저 역시 프로젝트를 진행하며 데이터 갱신, 보다 정확히는 차트 데이터의 갱신 및 알림 기능을 구현해야 하는 요구 사항이 생겼습니다.
몇 가지 특징 들을 살펴본 뒤 SSE (Server Sent Event)를 통하여 해당 기능을 구현하고 있습니다.
SSE는 서버의 Event를 stream 하는 기술입니다. 일반적 HTTP 통신이 요청에 따른 데이터를 제공한 뒤 연결을 끊는 것과는 달리, 초기 서버 이벤트를 구독해 놓으면, 서버에서는 지정한 이벤트가 발생 할 때마다 Client로 데이터를 보낼 수 있습니다.
특징은 함께 많이 거론되는 Polling, Websocket 과 비교하여 정리해 보았습니다.
const express = require("express");
const cors = require("cors");
const app = express();
app.use(cors());
let counter = 0;
const headers = {
"Content-Type": "text/event-stream",
Connection: "keep-alive",
"Cache-Control": "no-cache",
};
app.get("/subscribe", (req, res) => {
console.log("request received");
res.writeHead(200, headers);
setInterval(async () => {
res.write("event: notification\n");
res.write(
`data: ${JSON.stringify({
text: counter,
date: new Date().toDateString(),
})}`
);
res.write("\n\n");
counter++;
}, 2000);
});
app.listen(4000, () => {
console.log("listening...");
});
server는 간단하게 express로 테스트해 보았습니다. /subscribe 를 통해 구독요청을 한번만 보내면 지속적으로 데이터를 보낼 수 있습니다.
const headers = {
"Content-Type": "text/event-stream",
Connection: "keep-alive",
"Cache-Control": "no-cache",
};
위와 같이 sse연결이라는 것을 나타내기 위해 "Content-Type": "text/event-stream"
으로 header를 설정해 주어야 합니다.
const [listening, setListening] = useState(false)
const [msgs, setMsgs] = useState<INotification[]>([])
useEffect(() => {
if (!listening) {
setListening(true)
const eventSource = new EventSource(`http://localhost:4000/subscribe`)
eventSource.onopen = (e) => {
console.log('open')
}
eventSource.addEventListener('notification', (e) => {
const data = JSON.parse(e.data as string) as INotification
setMsgs((prev) => [data, ...prev])
})
}
}, [listening])
React에서의 구현입니다. JS에서는 SSE를 구현하기 위한 EventSource interface를 제공합니다. html 객체의 이벤트를 다루듯 서버에서 발생하는 이벤트에 대한 데이터를 핸들링 할 수 있습니다.
onopen
, onmessage
, onerror
등의 메소드를 제공하며 addEventListener
를 사용하여 특정 토픽에 대한 이벤트를 선택하여 구독 할 수 있습니다.
연결이 끊을 스토리가 없는 화면이기에 연결을 끊는 코드는 생략했습니다.
Chrome 브라우저 network탭에서 이벤트 스트림 데이터를 확인 할 수 있습니다.\
sse는 실시간 데이터가 필요한 경우, 특히 제 경우 처럼 서버로부터 데이터를 받기만 하는 시나리오에서 유용하게 사용할 수 있는 인터페이스인 것 같습니다.
socket.io 등을 주로 사용하는 웹소켓 구현에 비하여 FE,BE 모두 구현 복잡도가 많이 낮으며, 스펙상으로 성능 또한 socket 통신에 비해여 우수하다고 알려져 있기 때문에 실시간 통신에 있어서 우선적으로 검토해야할 기술이라는 생각했습니다.
Using Fetch Event Source for server-sent events in React - LogRocket Blog