티스토리 뷰

회사 프로젝트를 하면서 한번의 request에 여러개의 response를 반환해야하는 기능을 구현하는 이슈가 있었습니다. 처음에는 웹소켓을 생각했지만, 웹소켓은 무겁고, 무엇보다 양방향 데이터 통신이 아닌 클라이언트가 한번 요청하면 서버에서 일방적으로 데이터를 보내주는 형태이기 때문에 웹소켓은 적합하지 않다고 판단되었습니다. 그러던 중 Server Sent Event - SSE를 발견하게 되어 포스팅을 하고자 합니다.

SSE - Server Sent Event 란?


SSE는 Server Sent Event의 약어로 서버의 데이터를 실시간으로, 지속적으로 Streaming 하는 기술 입니다. SSE는 웹 표준으로써 IE를 제외한 모든 브라우저에서 지원되며, IE역시 polyfill을 통해 지원이 가능합니다.

 

기존에는 서버의 변경된 데이터를 가져오기 위해서 페이지 새로고침, 지속적으로 request를 보내는 ajax 폴링, 외부 플러그인 이용등을 사용해야만 했습니다. 이러한 방법 외에도 websocket을 사용할 수 있지만 HTTP 통신을 이용하는 것이 아닌 웹소켓만을 위한 별도의 서버와 프로토콜로 통신하기 때문에 구현하는 비용이 많이 든다는 단점이 있습니다.

 

하지만 SSE는 기존 HTTP 웹 서버에서 HTTP API 만으로 동작되며 구현도 간단하기 때문에 서버와 프론트엔드 양측 모두 매우 쉽게 개발이 가능합니다. 또한 Topic을 정하고 데이터를 구독할 수 있습니다.

SSE의 동작 원리


일반적인 HTTP는 TCP를 기반으로 1회성으로 데이터를 주고받은 뒤 연결을 종료합니다. 하지만 어떤 경우에는 한번만 데이터를 주고받는 것이 아니라 연결 상태를 유지하고 계속해서 데이터를 보내는 경우가 있습니다. 대표적으로는 많은 양의 데이터를 보내야 하는 파일 전송 입니다.

 

SSE는 파일 전송처럼 첫 연결시에 데이터를 주고받은 뒤 연결된 상태를 유지하고 서버가 일방적으로 데이터를 전송합니다.

 

SSE의 흐름도 - 출처 : https://bit.ly/30z87XW

SSE 사용해보기


■ Fontend

JavaScript는 SSE를 쉽게 사용할 수 있도록 EventSource API를 제공합니다. 아래 코드는 SSE를 통해 서버에서 지속적으로 전송하는 JSON 데이터를 구독하는 예제 입니다.

const eventSource = new EventSource(`/subscribe`);

eventSource.onmessage = event => {
	const data = JSON.parse(event.data);
	console.log(data.message);
};
eventSource.onerror = error => {
	eventSource.close();
};

 

■ Backend

SSE는 기존의 웹 서버 통신 방식인 HTTP를 그대로 사용합니다. 따라서 서버쪽에서는 SSE를 사용하겠다는 헤더만 설정한 뒤 데이터를 보내기만 하면 됩니다. 아래 예제는 java servlet을 기준으로 작성한 코드 입니다.

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SSEServlet extends HttpServlet {

	public void subscribe(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
	
		response.setContentType("text/event-stream");	// Header에 Content Type을 Event Stream으로 설정
		response.setCharacterEncoding("UTF-8");		// Header에 encoding을 UTF-8로 설정

		PrintWriter writer = response.getWriter();
		for(int i = 1; i <= 10; i++) {
			writer.write("data: { \"message\" : \"number : "+ i + "\" }\n\n");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		writer.close();
	}
}

여기서 주의해야 할점은 헤더에 Content Type을 "text/event-stream"으로, encoding을 UTF-8로 설정 해야 한다는 점 입니다. 헤더를 설정한 이후에는 데이터를 전송합니다. 데이터를 전송할때는 "data:" prefix를 붙여야 하며 line break(\n)두개를 끝에 추가해 주어야 합니다.

 

이렇게 구현된 코드는 아래와 같이 데이터를 전송합니다.

...
data: { "message" : "number : 1" } \n\n
data: { "message" : "number : 2" } \n\n
data: { "message" : "number : 3" } \n\n
data: { "message" : "number : 4" } \n\n
...

Topic 기반 데이터 구독


SSE는 단순히 모든 데이터를 구독하는 방법 외에도 특정 Topic을 기반으로 데이터를 선택하여 구독할 수 있는 방법을 제공합니다.

 

■ Fontend

EventSource APIS는 addEvenetListener 메소드를 통해 topic을 기반으로 데이터를 구독하는 listener를 등록할 수 있습니다.

const eventSource = new EventSource(`/subscribe`);

eventSource.addEventListener('odd', event => {
	const data = JSON.parse(event.data);
	console.log(`odd - ${data.message}`);
});

eventSource.addEventListener('even', event => {
	const data = JSON.parse(event.data);
	console.log(`even - ${data.message}`);
});

eventSource.onerror = error => {
	eventSource.close();
};

 

■ Backend

서버에서는 "event:" prefix를 통해 TOPIC을 구분합니다.

...
for(int i = 1; i <= 10; i++) {
	String topic = (i % 2 == 0) ? "even" : "odd";
	writer.write("event:" + topic + "\n");
	writer.write("data: { \"message\" : \"number : "+ i + "\" }\n\n");
}
...

여기서 주의할점은 event 키워드 뒤에는 line break(\n)를 하나만 써야 한다는 점 입니다.

 

이렇게 구현된 코드는 아래와 같이 데이터를 전송합니다.

...
event:odd\n
data: { "message" : "number : 1" } \n\n
event:even\n
data: { "message" : "number : 2" } \n\n
event:odd\n
data: { "message" : "number : 3" } \n\n
event:even\n
data: { "message" : "number : 4" } \n\n
...

마치며


무조건 SSE가 모든 상황에서 웹소켓보다 유리하다고 볼 수 는 없습니다. 실시간 채팅처럼 클라이언트와 서버가 양방향으로 데이터를 주고받는 경우에는 웹소켓이 더 적합합니다. 하지만 SSE는 별도 서버가 필요없다는점, 간단한 API를 제공하고 구현이 간단하다는 점이 큰 메리트라고 생각합니다. 상황과 용도에 따라서 더 적합한 기술을 골라서 사용하면 될것같습니다.

 

 

 

SSE , Server-Sent Event 로 뭔가를 해볼수 있을듯...

그렇다 SSE , Server-Sent Event 을 문득 보게 되었다. SSE 는 아마도 Server-Sent Event 의 약자 인듯 싶다. 출처 : https://www.packtpub.com/mapt/book/web_development/9781782166320/6/ch06lvl1sec43/listen..

kimseunghyun76.tistory.com

 

HTML5 Server-Sent Events and Java Servlet example

Introduction to html5 server-sent events and java servlet. Example code for implementing server-sent events using Java Servlet.

www.viralpatel.net

 

SSE를 이용한 실시간 웹앱

HTML5의 표준안으로 권고되어있는 Server-Sent Events에 대해 알아보고 이를 이용하여 실시간 웹앱을 만들어 봅시다.

spoqa.github.io

 

Server Sent Events in Angular + Node - Chris Bautista - Medium

In this article we’ll go over how to implement Server-sent events (SSE)using Node and Angular.

medium.com

 

댓글
  • 프로필사진 과연있나? 현업에서 웹소켓 안 쓰고 SSE를 고집하는 회사가 있나요? 2019.12.27 15:56
  • 프로필사진 박스여우 현업 에서도 SSE를 사용하는 경우를 종종 본 경우가 있습니다.
    개인적으로 웹소켓과 SSE 모두 모든 상황에 정답일 수는 없다고 생각합니다.
    상황에 따라 가장 적합한 기술을 사용하는 것이 좋지 않을까요?
    2020.01.12 23:32 신고
  • 프로필사진 kim 그대로 했는데 진행이 안되네요..
    디스패처 서블릿이 url를 못찾겠다고 ㅠㅠ
    2020.02.02 14:54
댓글쓰기 폼
Total
395,394
Today
0
Yesterday
499
링크
«   2020/02   »
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
글 보관함