Server-Sent Events란?

HTML5가 등장하기 전까지는 HTML에 서버 푸시를 위한 표준화된 기술이 없었기 때문에 웹에서 실시간 정보를 받아와야 할 때 외부 플러그인을 이용하거나 서버 푸시를 흉내 낸 Ajax 폴링(polling) 기법 등을 사용했습니다. 하지만 플러그인 종속적인 웹은 해당 플러그인을 설치해야 한다는 불편함이 있으며 폴링처럼 주기적인 요청을 통한 구현은 쓸모없는 요청의 발생으로 인한 대역폭의 낭비가 불가피하였습니다. HTML5의 Server-Sent Events(이하 SSE)는 이러한 문제 없이 서버가 필요할 때마다 클라이언트에게 데이터를 줄 수 있게 해주는 서버 푸시 기술입니다.

SSE의 장점

SSE의 장점으로는 다음과 같은 것들이 있습니다.

  • 전통적인 HTTP를 통해 통신하므로 다른 프로토콜이 필요가 없습니다.
  • 재접속 처리 같은 대부분의 저수준 처리가 자동으로 됩니다.
  • 표준 기술답게 IE를 제외한 브라우저 대부분을 지원합니다.
  • HTML과 JavaScript만으로 구현할 수 있으므로 현재 지원되지 않는 브라우저(IE 포함)도 polyfill을 이용해 크로스 브라우징이 가능합니다. (여기서 polyfill이란 브라우저가 지원하지 않는 API를 플러그인이나 JavaScript 등으로 흉내 내 구현한 것을 뜻합니다. polyfill에 대한 자세한 설명은 이 블로그 포스트를 참조하시기 바랍니다.)

사실 종합해보면 SSE의 모든 장점은 간편함으로 요약할 수 있습니다.

적용 예

위의 SSE의 장점에서 보듯이 SSE는 구현이 아주 간편합니다. 클라이언트는 서버로부터 스트림을 받아 EventSource 객체를 통해 서버가 푸시하는 데이터를 받아 처리하기만 하면 됩니다.

먼저 클라이언트 측의 코드입니다.

var es = new EventSource(stream_url);

es.onmessage = function (event) {
    // 이벤트 설정이안된 기본 데이터 처리
};
es.addEventListener('myevent', function(e) {
    // 'myevent' 이벤트의 데이터 처리
}, false);

EventSource 객체의 속성은 다음과 같습니다.

onmessage
기본 메시지가 왔을 때 호출
onopen
접속이 맺어졌을 때 호출
onerror
오류 발생 시 호출

EventSourceaddEventListener()를 사용하면 위 3개의 이벤트뿐만 아니라 따로 지정된 이벤트의 데이터도 받아 처리할 수 있습니다.

자 그럼 Flask와 Redis를 이용한 서버 측의 코드를 볼까요? (예제는 Redis를 이용하였지만, Publish/Subscribe Model이라면 다른 걸 쓰셔도 무방합니다.)

def event_stream():
    pub = redis.pubsub()
    pub.subscribe('sse_example_channel')
    for msg in pub.listen():
        if msg['type'] != 'subscribe':
            event, data = json.loads(msg['data'])
            yield u'event: {0}\ndata: {1}\n\n'.format(event, data)
        else:
            yield u'data: {0}\n\n'.format(msg['data'])

@app.route('/stream')
def get_pushes():
    return Response(event_stream(), mimetype="text/event-stream")

@app.route('/post')
def publish_data():
    # ...
    redis.publish('sse_example_channel', json.dumps([event, data]))

응답의 형식을 text/event-stream으로 주고 이벤트 스트림을 걸어 놓습니다. event_stream()에서는 새로운 데이터가 들어오면 데이터를 형식에 맞추어 푸시하고 있습니다. SSE에서의 데이터 형식은 2개의 개행으로 구분됩니다.

data: This is the first message.

data: This is the second message, it
data: has two lines.

event: myevent
data: This is the third message with a separated event.

WebSocket과 Server-Sent Events

완전한 양방향 통신을 지원하는 WebSocket이 SSE보다 더 매력적으로 보일 수 있습니다. WebSocket 또한 표준 기술인데다 양방향 통신이고 브라우저 지원 범위도 더 넓습니다.

그렇지만 WebSocket이 SSE보다 항상 더 좋은 것은 아닙니다. 채팅이나 게임 같이 사용자가 서버의 데이터에 즉각 반응해야 하는 경우라면 WebSocket을 사용하는 것이 더 효율적이겠지만 많은 경우 클라이언트 측에서의 반응은 필요가 없습니다. 주식차트, 뉴스피드, 푸시 노티피케이션 등이 대표적인 예입니다.

브라우저 지원 부분에서도 SSE가 더 유리한 측면이 있습니다. WebSocket은 브라우저가 지원하지 않을 때에는 서버 측에서 이에 대응하는 fallback 프로토콜을 별도로 구현해야만 합니다. 반면, SSE는 클라이언트 기술이기 때문에 서버 측 구현을 더 복잡하게 하지 않고도 브라우저가 JavaScript만 지원한다면 polyfill을 이용하여 구현이 가능합니다.

따라서 상황에 따라 복잡한 WebSocket을 이용하는 것보다는 간단한 SSE를 통해 구현하는 것이 여러 가지로 유리할 수 있습니다.

스포카에서는 프론트엔드 프로그래머, 시니어 풀스택 프로그래머를 채용 중입니다! 웹 프론트엔드에 관심을 가지고 공부하는 디자이너, 뛰어난 서버 개발자 등 각자의 분야에서 전문적인 사람들이 능력있는 분들과 함께 일하기를 기대하고 있습니다. 채용 정보 페이지를 확인해주세요!