Server-Sent Events 톺아보기

경이·2025년 5월 3일

최근 SSE라는 개념을 접하게 되었다. 지금까지의 진행한 프로젝트에서는 실시간으로 데이터를 받아와야 하는 일이 없었어서 처음 보는 개념이었다. 그래서 Server-Sent Events(SSE)에 대해 공부한 내용을 (프론트 개발자의 시점으로) 정리하려고 한다.


1️⃣ Server-Sent Events(SSE)란 ?

일반적으로 클라이언트는 새로운 데이터를 받기 위해 서버로 데이터를 요청한다. 하지만 Server-Sent Events 방식을 사용하면 웹페이지의 요청 없이도 언제든지 서버가 새로운 데이터를 보낼 수 있다. 이를 통해 실시간 스트리밍 형태로 데이터를 전달 받을 수 있다.

장점

  • HTTP + TCP 기반의 단일 연결을 사용하므로 메세지 전송 순서가 보장된다.
  • 별도의 라이브러리 없이 브라우저에 내장되어 있는 EventSource 객체로 편리하게 사용할 수 있다.
  • 자동재연결, ID기반 재전송 등의 편의 기능을 제공한다.

단점

  • 단방향 통신만 지원한다.(서버 ➝ 클라이언트만 가능)
  • SSE는 지속적인 HTTP 연결이 필요하므로, 연결 개수가 제한되어 있다.

SSE vs WebSocket

나는 막연하게 실시간으로 데이터를 주고받는 데에는 WebSocket을 사용해야 한다고 알고 있었다. 그래서 SSE와 WebSocket은 각각 어떤 상황에서 사용해야 하는지 간략하게 표로 정리해봤다.

항목SSEWebSocket
프로토콜HTTP 기반WebSocket 프로토콜
통신 방향서버 ➝ 클라이언트서버 ⇄ 클라이언트
설정 난이도간단함 (EventSource)복잡함 (Socket 서버 필요)
용도실시간 알림, 로그, 이벤트 스트림채팅, 게임, 양방향 통신 필요 시

둘의 가장 큰 차이는 통신 방향이다.

  • SSE는 단방향으로 서버가 클라이언트에게만 데이터를 보낼 수 있다.
  • WebSocket은 양방향으로 클라이언트와 서버가 자유롭게 메시지를 주고받을 수 있다.

따라서, 실시간 알림, 로그 전송 같은 가벼운 푸시 용도에는 SSE, 양방향 통신이 필요한 채팅, 게임 등은 WebSocket 이 더 적합하다.


2️⃣ 어떻게 사용하나요 ?

EventSource 인터페이스를 사용해 SSE(Server-Sent Events)를 구현할 수 있다.

EventSource는 브라우저에서 서버가 보내는 스트리밍 데이터를 실시간으로 수신하기 위한 WebAPI 인터페이스로 브라우저에서만 제공되는 기능이다. 따라서 node 환경에서는 동작하지 않아 따로 폴리필을 설치해야 한다.

위 코드처럼 이벤트를 생성하는 스크립트의 URL을 사용해 EventSource 객체를 만들 수 있다.

만약 클라이언트와 서버의 오리진이 다르다면 CORS 에러가 발생할 수 있다. 이 경우 withCredentials옵션을 활성화 해야한다. (서버에서도 헤더설정을 따로 해주어야 한다.)

이제 .onmessage, .onerror, .addEventListener() 등을 이용해서 서버에서 푸시되는 메시지를 처리할 수 있다. 하나씩 살펴보자

메세지 수신하기

서버에서는 보내는 메세지는 위와 같이 event 필드를 포함할 수도 있고, 생략할 수도 있다.

  • event 필드가 없는 경우 → onmessage 사용 기본적으로 event 필드가 생략된 메시지는 onmessage 핸들러로 수신된다. 서버가 메시지를 전송할 때마다 자동으로 onmessage가 호출되므로, 이 핸들러에 메시지를 수신한 뒤 실행할 로직을 등록하면 된다.
    이 때 e.data는 JSON 형태의 문자열 타입이다. 기본적으로 fetch()나 Axios를 통해 데이터를 받아올 때는 내부에서 자바스크립트 객체로 파싱해주기도 하지만 WebSocket이나 SSE를 통해 응답을 받게될 경우 반드시 파싱을 해야 한다.
  • event 필드가 있는 경우 → addEventListener() 사용
    서버가 event: customEvent와 같이 이벤트 이름을 명시한 경우에는, 클라이언트에서도 해당 이름으로 핸들러를 등록해줘야 한다. addEventListener메서드를 사용해 이벤트 이름을 첫 번째 인자로, 두번째 인자로는 메세지를 받았을 때 실행할 콜백함수를 작성하면 된다.

메세지 송신하기

SSE(Server-Sent Events)에서 서버가 클라이언트로 메시지를 보낼 때는 아래와 같이 정해진 텍스트 포맷을 따라야 한다.

  • 응답의 MIME 타입은 반드시  text/event-stream 을 사용한다.
  • 각 메세지는 두개의 줄바꿈 문자(\n\n)로 끝나야 전송이 완료됨을 의미한다.
  • 줄의 첫 문자가 콜론(:)이면 해당 줄은 주석으로 처리된다. 이 주석은 클라이언트와의 연결 유지를 위해 사용된다.
  • 메시지는 여러 개의 필드로 구성되며, 각 필드는 필드명: 값 형태의 한 줄로 작성된다.

필드 종류는 아래와 같다.

  • event(선택) : 이벤트 유형을 식별하는 문자열. 클라이언트 측에서는 event 필드가 있으면 addEventListener() 를 사용하고, event 필드가 없으면 onmessage핸들러를 호출한다.
  • data(필수) : 클라이언트로 전달할 데이터
  • id(선택) : 이벤트 소스 객체의 마지막 이벤트 ID. 연결이 끊겼을 때 이 ID 이후부터 재전송 가능
  • retry(선택) : 재연결까지의 대기 시간(ms) 설정

받을 수 있는 메세지 예시를 살펴보자

  • 첫 번째 블록은 : 문자로 시작하므로 주석이다. 클라이언트에서는 이 줄을 무시하지만, 서버-클라이언트 간 연결 유지를 위한 ping 용도로 자주 사용된다.
  • 두 번째 블록은 data: some text로 이루어진 단일 메시지다. \n\n으로 끝나기 때문에 클라이언트에서는 이 블록을 하나의 메시지로 인식하며, e.data에는 "some text"가 담긴다.
  • 세 번째 블록은 data:로 시작하는 여러 줄로 구성되어 있다. 클라이언트는 이 줄들을 줄바꿈(\n)으로 이어 붙인 뒤, 하나의 메시지로 처리한다. 따라서 e.data에는 "another message\nwith two lines"가 전달된다
  • 네번째 블록은 event 필드를 포함한다. 클라이언트는 이벤트 이름 usermessage를 기준으로 메세지를 처리할 수 있다.

MDN 공식문서에서 PHP로 작성된 서버 예제를 볼 수 있다.

에러 핸들링

네트워크 타임아웃, 서버 다운, 접근제어 등의 문제가 발생하면 onerror 핸들러가 실행된다. SSE는 기본적으로 자동 재연결을 시도하지만, 반복적인 실패에 대한 처리는 직접 구현해야 한다. 에러 발생 시 수행할 콜백함수는 onerror에 등록하면 된다.

이벤트 스트림 닫기

close() 메서드를 호출하면 서버와의 스트림 연결이 종료된다. 페이지 이탈 시 또는 필요 없어진 시점에 명시적으로 닫아주는 것이 좋다.


3️⃣ 언제 사용하나요 ?

앞서 SSE와 WebSocket의 차이에서 간단히 언급했지만, SSE가 실제로 어떤 상황에서 유용하게 쓰일 수 있는지를 조금 더 구체적으로 적어보았다.

  • 뉴스 피드, 주식 시세, 스포츠 점수 등 지속적으로 갱신되는 정보 제공
  • 사용자에게 실시간으로 알림을 전송해야 하는 경우
  • 서버의 로그나 상태 정보를 실시간으로 스트리밍하는 대시보드
  • 챗봇처럼 서버가 클라이언트에게 일방적으로 응답만 보내는 구조

📌 결론

  • SSE는 HTTP 기반의 실시간 단방향 데이터 전송 방식으로 순서를 보장한다.
  • 브라우저에서 기본 제공되는 EventSource로 쉽게 구현 가능하다.
  • 서버 → 클라이언트 방향의 푸시가 중심인 단방향 실시간 통신에 적합하다.

🚀 느낀점

  • SSE 공부를 하면서 MDN 공식문서를 찾아봤는데 브라우저에서 제공하는 WebAPI들이 굉장히 다양하다는 것을 느꼈다. 하나씩 공부해야겠다.
  • SSE는 내부적으로 비동기로 동작하지만, Promise기반이 아니기 때문에 async/await으로 감쌀 필요가 없다는 것
  • 평소 axios로 데이터를 받아올 때는 자동으로 파싱되기 때문에 JSON.parse()를 잊고 있었다🥲 하지만 괜찮음. 이제 기억하면 되니까
  • 나중에 SSE을 사용해서 챗봇을 구현해봐야겠다.

📙 REF

profile
록타르오가르

0개의 댓글