Server-sent events(SSE): 오지 않는 이벤트, 빈 문자열 처리

Kim NakyeongKim Nakyeong
2 min read

문제: event 필드만 전송할 경우 처리되지 않음

결론: EventSource는 데이터 필드가 비었을 경우 메시지를 처리하지 않음


일단 SSE 정의부터 간단하게 알아보자.

SSE는 서버 푸시 기술로, 클라이언트가 아닌 서버가 통신을 시작하는 통신 방법이다. 주로 연속적인 데이터 스트림을 브라우저 클라이언트로 보내는 데 사용된다.

클라이언트에서 EventSource 객체를 이용하여 연결을 설정하면, 서버에서 text/event-stream 형식으로 문자열을 전송한다.


아래는 MDN에 기재된 내용이다.

이벤트 스트림은 UTF-8으로 인코딩 되는 문자열이다. 이벤트 스트림의 메시지는 ${필드명}: ${데이터}의 조합으로 구성된다. 각 필드는 개행문자로 구분되고, 각 메시지는 두 개의 개행 문자로 구분된다. 메시지는 아래와 같은 필드를 포함한다.

  • id

  • event

  • data: EventSourcedata: 로 시작하는 연속된 여러 줄을 받게 되면 이를 연결하여 각 줄 사이에 개행 문자를 삽입합니다. 끝에 있는 개행 문자는 제거됩니다.

  • retry


한국어 버전으로 보면 없지만, 영어로 봤을 때는 data 필드 설명에 링크가 하나 걸려있다.

브라우저에서 이벤트를 처리하는 과정이 나열되어 있는데, 그 중 2번 과정을 보자.

If the data buffer is an empty string, set the data buffer and the event type buffer to the empty string and return.

만약 데이터 버퍼가 빈 문자열일 경우, 이벤트 타입 버퍼를 빈 문자열로 설정하고 반환한다는 내용이다. 무시한다는 뜻이다. 그래서 그동안 이벤트를 아무리 보내도 클라이언트에서 확인하지 못했던 것이다..

메시지가 무시되지 않게 하려면 데이터 필드는 꼭 함께 보내도록 하자.

// client
const event = new EventSource("/stream");
event.addEventListener("end", e => {
  // 연결 종료 처리
});
event.onmessage = (e) => {
  // 데이터 처리
};

// server
fastify.get("/stream", async (request, reply) => {
  reply.raw.writeHead(200, {
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    Connection: "keep-alive",
  });

  for await (const message of ["hello", " ", "world"]) {
    reply.raw.write(`data: ${message}\n\n`);
    await new Promise(resolve => setTimeout(resolve, 100));
  }

  reply.raw.write("event: end\n");
  reply.raw.write("data: Stream end\n\n");
});
0
Subscribe to my newsletter

Read articles from Kim Nakyeong directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Kim Nakyeong
Kim Nakyeong