일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 30 | 31 |
- CORDA
- Golang
- hyperledger fabric
- Adapter 패턴
- 파이썬 머신러닝
- 파이썬 동시성
- 그라파나
- 스칼라 강좌
- 주키퍼
- play2 강좌
- Hyperledger fabric gossip protocol
- 하이브리드앱
- Actor
- 플레이프레임워크
- 스칼라
- 안드로이드 웹뷰
- 스위프트
- akka 강좌
- 파이썬
- Akka
- 이더리움
- play 강좌
- 엔터프라이즈 블록체인
- 파이썬 데이터분석
- 스칼라 동시성
- 파이썬 강좌
- 하이퍼레저 패브릭
- 블록체인
- Play2
- Play2 로 웹 개발
- Today
- Total
HAMA 블로그
[웹개발] SSE ( Server-Sent Events) 란 무엇인가 본문
https://www.html5rocks.com/en/tutorials/eventsource/basics/번역
저는 웹 개발 경험이 부족하여 최근에 알게된 기술들이 많습니다. 이것도 꽤 오래된 내용인 듯 한데요. SSE (Server-Sent Events : HTML5 표준안 권고사항) 에 대해서 소개하는 글을 번역해 보았습니다. 간략하게 요약하면 이 SSE 는 어느정도 웹소켓의 역할을 하면서 더 가볍습니다. 주로 서버에서 받는 (푸쉬) 위주의 작업에 유용하게 사용 될 수 있습니다. 웹소켓과 같은 양방향은 아니기 때문에 보낼때는 Ajax 를 활용합니다.
"Play2 와 Iterratee 로 채팅구현 10분완성"에서는Concurrent.broadcast와 iteratee,EventSource를 이용하여 업데이트된 내용을 클라이언트들에 전송하고, Ajax 를 이용하여 메세지를 받는 채팅구현을 소개하고 있습니다. 추후에 저 내용에 Angular2 를 이용해 클라이언트측을 추가한 버전을 이용하여 Play2와 Angular2 를 이용한 Reactive 채팅 구현이라는 글을 게시할 예정입니다. 관심있으시면 참고하세요.
* 참고로 Play2 에서 의 사용되는 모습은 대략 아래와 같습니다. ( Play2 와 Server-Send Events 이용하여 데이터 헨들링하기)
|
자 시작합니다~!
소개
"Server-Sent Events (SSE) 란 무엇입니까?" 라고 궁금해 하는 사람들에 대해 저는 별로 놀라지 않을 거 같네요. 왜냐면 많은 사람들이 그것에 대해 들어 본 적이 별로 없었을 것이란 걸 잘 알기 때문이죠. 지난 몇 년 동안 스펙은 상당한 변화를 겪었으며 API는 WebSocket API와 같이 더 새롭고 더 섹시한 통신 프로토콜에 의해 뒷전으로 밀려갔습니다. SSE의 개념은 아마 익숙 할 수도 있을 것입니다. 웹 응용프로그램은 서버에 의해 생성된 업데이트 스트림를 "구독" 하고 새 이벤트가 발생할 때마다 클라이언트에 알림을 보냅니다. 그렇지만 이런 Server-Sent Events를 제대로 이해하려면 Ajax 전임자들의 한계를 어느 정도는 이해해야 할 겁니다.
폴링은 대다수의 AJAX 응용 프로그램에서 사용되는 전통적인 기술입니다. 기본 개념은 응용 프로그램이 서버에서 데이터를 반복적으로 요청하는 것입니다. HTTP 프로토콜에 익숙하다면 데이터를 가져 오는 것이 요청 / 응답 형식을 중심으로 진행된다는 것을 알 텐데요. 클라이언트는 요청을 하고 서버가 데이터로 응답 할 때까지 기다립니다. 갱신되지 않은 의미없는 응답이 리턴될 수 도 있습니다. 즉 단점으로는 더 많은 HTTP 오버 헤드를 만들 것 입니다. 하지만 일정하게 갱신이 되는 서버 데이터의 경우 매우 유용하며 기존 단순한 모델을 유지 할 수 있습니다.
긴 폴링 (Hanging GET / COMET)은 폴링에 약간의 변형입니다. 긴 폴링에서 서버에 사용 가능한 데이터가 없으면 서버는 새 데이터를 사용할 수 있을 때까지 요청을 보류합니다. 따라서이 기술은 종종 "Hang GET"이라고합니다. 정보를 사용할 수있게되면 서버가 응답하고 연결을 닫은 후 프로세스가 반복됩니다. 결과적으로 서버는 새로운 데이터가 사용 가능할 때마다 지속적으로 응답합니다. 단점은 이러한 절차의 구현은 일반적으로 '무한' iframe에 스크립트 태그를 추가하는 것과 같은 술수가 필요하다는 것입니다.
반면에 Server-Sent 이벤트는 처음부터 효율적으로 설계되었습니다. SSE를 사용하여 통신 할 때 서버는 초기 요청을 하지 않고도 필요할 때마다 데이터를 앱으로 푸시 할 수 있습니다. 즉, 서버에서 클라이언트로 업데이트를 스트리밍 할 수 있습니다. SSE는 서버와 클라이언트 사이에 단일 단방향 채널을 엽니다.
Server-Sent Events와 long-polling의 가장 큰 차이점은 SSE는 브라우저에서 직접 처리되므로 사용자는 메시지를 청취(구독) 해야 한다는 것입니다. (즉 그런 코딩이 추가됨)
Server-Sent Events vs. WebSockets
웹 소켓을 통해 Server-Sent 이벤트를 선택하는 이유는 무엇입니까? 좋은 질문입니다. ^^
SSE가 그림자에 머물러있는 이유 중 하나는 WebSocket과 같은 API가 양방향 전이중 통신을 수행하기 위한 더 풍부한 프로토콜을 제공하기 때문입니다. 양방향 채널을 보유하면 게임, 메시징 앱 및 양방향으로 거의 실시간으로 업데이트해야하는 경우에 더 매력적입니다. 하지만 일부 시나리오에서는 클라이언트에서 데이터를 전송하지 않아도 되며 서버 작업에서만 업데이트 해주면 됩니다. 몇 가지 예는 친구의 상태 업데이트, 주식 시세 표시기, 뉴스피드 또는 기타 자동화 된 데이터 푸시 메커니즘 (예 : 클라이언트 측 웹 SQL 데이터베이스 또는 IndexedDB 객체 저장소 업데이트)입니다. 이때 서버에게 데이터를 보내야 하는 경우 XMLHttpRequest는 항상 친구입니다. (역주: 채팅 기능을 예로 들면 SSE 로 브로드캐스팅된 내용을 듣고, Ajax 를 통해 서버로 말함)
SSE는 전통적인 HTTP를 통해 전송됩니다. 즉, 작동하려면 특별한 프로토콜이나 서버 구현이 필요하지 않습니다. 반면 WebSockets는 프로토콜을 처리하기 위해 전이중 연결과 새로운 웹 소켓 서버가 필요합니다. 또한 서버 보낸 이벤트에는 자동 재 연결, 이벤트 ID 및 임의 이벤트를 보내는 기능과 같이 WebSockets은 디자인 측면에서 부족한 다양한 기능이 있습니다.
JavaScript API
이벤트 스트림을 구독하려면 EventSource 객체를 만들고 스트림의 URL을 전달합니다.
if (!!window.EventSource) { var source = new EventSource('stream.php'); } else { // Result to xhr polling :( }
참고 : EventSource 생성자에 전달 된 URL이 절대 URL 인 경우 해당 출처 (scheme, domain, port)가 호출 페이지의 출처와 일치해야합니다.그런 다음 메시지 이벤트에 대한 핸들러를 설정하십시오. 선택적으로 open 과 error 를 수신 대기 할 수 있습니다.
source.addEventListener('message', function(e) { console.log(e.data); }, false); source.addEventListener('open', function(e) { // Connection was opened. }, false); source.addEventListener('error', function(e) { if (e.readyState == EventSource.CLOSED) { // Connection was closed. } }, false);
서버에서 업데이트된 내용이 푸시되면 onmessage 함수가 실행되고 e.data 속성에서 새 데이터를 사용할 수 있습니다. 마법 같은 부분은 연결이 닫힐 때마다 브라우저가 ~ 3 초 후 자동으로 소스에 다시 연결된다는 것이며 서버 구현은 이 재 연결 시간 초과를 제어 할 수도 있습니다.
그게 전부에요. 이제 클라이언트는 stream.php에서 이벤트를 처리 할 준비가 되었습니다.~
Event Stream Format
소스에서 이벤트 스트림을 보내는 것은 SSE 형식인 text/event-stream
Content-Type을 사용하여 일반 텍스트 응답을 작성하면서 수행되며 기본 형식에서 응답에는 "data :" 행 다음에 메시지가 오고 스트림 뒤에는 두 개의 "\n"문자가 있어야 스트림을 끝낼 수 있습니다.
data: My message\n\n
Multiline Data
메시지가 길면 여러 개의 "data :"행을 사용하여 메시지를 분할 할 수 있습니다. "data :"로 시작하는 두 줄 이상의 연속 된 줄은 하나의 데이터 조각으로 간주되어 하나의 메시지 이벤트 만 발생합니다. 각 행은 단일 "\ n"으로 끝나야합니다 (마지막 행은 2로 끝나야 함). 메시지 처리기에 전달 된 결과는 개행 문자로 연결된 단일 문자열입니다. 예 :
data: first line\n data: second line\n\n
e.data에 "first line \ second line"을 생성합니다. 그런 다음 e.data.split ( '\ n') .join ( '')을 사용하여 "\ n"문자를 다시 생성 할 수 있습니다.
Send JSON Data
여러 줄을 사용하면 구문을 깨지 않고 JSON을 쉽게 보낼 수 있습니다.
data: {\n data: "msg": "hello world",\n data: "id": 12345\n data: }\n\n
해당 스트림을 처리 할 수있는 클라이언트 측 코드
source.addEventListener('message', function(e) { var data = JSON.parse(e.data); console.log(data.id, data.msg); }, false);
Associating an ID with an Event
"id :"로 시작하는 줄을 포함시켜 스트림 이벤트와 함께 고유 한 ID를 보낼 수 있습니다.
id: 12345\n data: GOOG\n data: 556\n\n
ID를 설정하면 브라우저가 마지막 이벤트를 추적하여 서버 연결이 끊어지면 특수한 HTTP 헤더 (Last-Event-ID)가 새 요청으로 설정됩니다. 이렇게 하면 브라우저가 어떤 이벤트를 실행하기에 적합한 지 스스로 결정할 수 있게 됩니다. message 이벤트는 e.lastEventId 속성을 포함합니다.
Controlling the Reconnection-timeout
브라우저는 각 연결이 닫힌 후 대략 3 초 후에 원본에 다시 연결하려고 시도하며 "retry :"로 시작하는 줄과 재 연결을 시도하기 전에 대기 할 시간 (밀리 초)을 포함하여 시간 제한을 변경할 수 있습니다.
다음 예제에서는 10 초 후에 다시 연결을 시도합니다.
retry: 10000\n data: hello world\n\n
Specifying an event name
단일 이벤트 소스는 이벤트 이름을 포함시켜 여러 유형의 이벤트를 생성 할 수 있습니다. "event :"로 시작하는 행 다음에 이벤트의 고유 한 이름이 오는 경우 이벤트는 해당 이름과 연관됩니다. 클라이언트에서 이벤트 리스너를 설정하여 해당 특정 이벤트를 청취 할 수 있습니다.
예를 들어 다음 서버 출력은 일반적인 'message'이벤트, 'userlogon'및 'update'이벤트의 세 가지 유형의 이벤트를 보냅니다.
data: {"msg": "First message"}\n\n event: userlogon\n data: {"username": "John123"}\n\n event: update\n data: {"username": "John123", "emotion": "happy"}\n\n
클라이언트의 이벤트 리스너 설정 :
source.addEventListener('message', function(e) { var data = JSON.parse(e.data); console.log(data.msg); }, false); source.addEventListener('userlogon', function(e) { var data = JSON.parse(e.data); console.log('User login:' + data.username); }, false); source.addEventListener('update', function(e) { var data = JSON.parse(e.data); console.log(data.username + ' is now ' + data.emotion); }, false);
Server Examples
PHP의 간단한 서버 구현 :
<?php header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); // recommended to prevent caching of event data. /** * Constructs the SSE data format and flushes that data to the client. * * @param string $id Timestamp/id of this connection. * @param string $msg Line of text that should be transmitted. */ function sendMsg($id, $msg) { echo "id: $id" . PHP_EOL; echo "data: $msg" . PHP_EOL; echo PHP_EOL; ob_flush(); flush(); } $serverTime = time(); sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
Node JS: 의 유사한 구현
var http = require('http'); var sys = require('sys'); var fs = require('fs'); http.createServer(function(req, res) { //debugHeaders(req); if (req.headers.accept && req.headers.accept == 'text/event-stream') { if (req.url == '/events') { sendSSE(req, res); } else { res.writeHead(404); res.end(); } } else { res.writeHead(200, {'Content-Type': 'text/html'}); res.write(fs.readFileSync(__dirname + '/sse-node.html')); res.end(); } }).listen(8000); function sendSSE(req, res) { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); var id = (new Date()).toLocaleTimeString(); // Sends a SSE every 5 seconds on a single connection. setInterval(function() { constructSSE(res, id, (new Date()).toLocaleTimeString()); }, 5000); constructSSE(res, id, (new Date()).toLocaleTimeString()); } function constructSSE(res, id, data) { res.write('id: ' + id + '\n'); res.write("data: " + data + '\n\n'); } function debugHeaders(req) { sys.puts('URL: ' + req.url); for (var key in req.headers) { sys.puts(key + ': ' + req.headers[key]); } sys.puts('\n\n'); }
sse-node.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <script> var source = new EventSource('/events'); source.onmessage = function(e) { document.body.innerHTML += e.data + '<br>'; }; </script> </body> </html>
Cancel an Event Stream
일반적으로 브라우저는 커넥션이 끊어졌을때 이벤트 소스에 자동으로 다시 연결되지만 클라이언트 또는 서버로 부터 동작을 취소 할 수 있습니다.
클라이언트에서 스트림을 취소하려면 다음을 호출하시구요.
source.close();
서버에서 스트림을 취소하려면 "text/event-stream
" Content-Type 이 아닌 200 OK 이외의 HTTP 상태 (예 : 404 찾을 수 없음)를 반환하면 됩니다. 두 가지 방법 모두 브라우저가 연결을 다시 설정하지 못하게 합니다.
SSE의 장점 정리 - spoqa 기술블로그 (https://spoqa.github.io/2014/01/20/sse.html) 참고
1.전통적인 HTTP를 통해 통신하므로 다른 프로토콜이 필요가 없습니다.
2.재접속 처리 같은 대부분의 저수준 처리가 자동으로 됩니다.
3.표준 기술답게 IE를 제외한 브라우저 대부분을 지원합니다.
4. HTML과 JavaScript만으로 구현할 수 있으므로 현재 지원되지 않는 브라우저(IE 포함)도 polyfill을 이용해 크로스 브라우징이 가능합니다. (여기서 polyfill이란 브라우저가 지원하지 않는 API를 플러그인이나 JavaScript 등으로 흉내 내 구현한 것을 뜻합니다. polyfill에 대한 자세한 설명은 이 블로그 포스트를 참조하시기 바랍니다.)
SSE 단점 및 Websocket 과의 비교
- 인터넷 익스플로러 미지원 ( ㅋㅋ ) 등
'소프트웨어 사색 ' 카테고리의 다른 글
[책이야기] Effective 시리즈 (0) | 2017.04.24 |
---|---|
머신러닝, IoT, 빅데이터 등 학원결정에 어려움을 겪는 취준생들에게 (0) | 2017.04.13 |
Reactive 프로그래밍 - Hello world (0) | 2017.02.19 |
[남의글] 폴그레이엄 - 규모가 안 나오는 일을 해라 (0) | 2017.02.16 |
웹개발 패러다임의 거대한 변화 "Reactive" (2) | 2017.02.15 |