이 포스트에 있는 내용이 언제나 확실한 정답은 아닙니다. 진실이라고 생각해 왔던 전제가 시간의 지남에 따라 들어나지 않았던 다른 이면 때문에 좋은 방향으로 이끌어 낼 수 있는 역할로 변환 되는게 역사적으로도 많은 증명 있었습니다. 그렇지만 저는 현재 상황에서 최선의 답을 찾고자 노력하였으며 이 글을 읽는 다른 분들에게 다음 길을 갈 수 있도록 도와주는 디딤돌이 되고자 노력하고자 포스팅을 통해 공유하고자 하는 것입니다. 그리고 프로그래머라는 타이틀을 달고 살아야 한다면 "왜"라는 의문을 항상 가지고 다니면서 자신의 위치에 안주하지 않고 항상 노력하는 모습으로 살아 가고자 합니다. 언제든 지적이나 오류가 있으면 피드백 부탁 드리겠습니다.
ASP.NET SignalR is a new library for ASP.NET developers that makes it incredibly simple to add real-time web functionality to your applications. What is "real-time web" functionality? It's the ability to have your server-side code push content to the connected clients as it happens, in real-time.
말하자면 ASP.NET SignalR은 어플리케이션에 매우 간단하게 실시간 웹 기능을 만들 수 있도록 해주는 ASP.NET 개발자를 위한 라이브러리이다. 실시간 웹 기능이란? 서버 사이드에서 실시간으로 일어난 일을 연결된 클라이언트에게 내용을 능동적으로 전달하는 것이다. 즉 SignalR은 즉각적인 사용자와의 상호작용과 실시간 데이터 갱신이 필요한 웹 응용 프로그램 개발을 지원하기 위한 라이브러리이다. 예로 대시보드에서 다른 시스템을 모니터링을 할 때 즉각적으로 변하는 값을 브라우저의 그래프에 즉시 반영하는 시나리오에 적합하다.
SignalR은 실시간 응용 프로그램 개발 과정을 WebSocket를 이용한 개발보다 더 단순화시켜 줍니다. ASP.NET의 서버 라이브러리와 SignalR에서 같이 제공되는 자바스크립트 클라이언트 라이브러리를 통해서 WebSocket을 직접 사용하는 것보다 더 추상화 시킨 라이브러리이다. '그림8'과 같이 추상화된 라이브러리로 API를 사용하면 WebSocket을 지원하는 클라이언트 브라우저에서는 WebSocket를 통해 서버와 통신하고, 그렇지 않을 경우에는 대체되는 통신 기법으로 바뀌어 서버와의 통신을 유지하도록 해준다.
이제 개발에 필요한 라이브러리를 추가 하자. Nuget을 통해 SignalR로 검색하면 '그림12'와 같은 화면을 확인할 수 있을 것이다. 이 중에서 'Microsoft ASP.NET SignalR'을 선택하고 설치하면 자동으로 Visual Studio에 필요한 DLL들이 추가가 될 것이다. 그런다음 Global.asax.cs 파일을 열어서 '코드12'와 같이 'RouteTable.Routes.MapHubs();'를 Application_Start에 추가 하고 저장 후 닫아 버리자
위 코드는 SignalR이 ASP.NET에서 초기화가 되도록 세팅하는 것이며 Global.asax.cx의 Application_Start 메소드는 웹 서버가 처음 기동될 때 단 한번만 실행이 된다. 그러므로 SignalR 관련 초기화는 한번만 이뤄지게 된다.
<!-- SignalR로 통신하여 받은 데이터를 반영할 수 있는 DOM --><ulid="messages"></ul>@using (Ajax.BeginForm("PostMessage", newAjaxOptions())) {
<p>
Say : @Html.TextBox("messageText")
<inputtype="submit"/></p>
}
@section scripts {<scriptsrc="~/Scripts/jquery.signalR-1.0.1.js"type="text/javascript"></script><scriptsrc="~/signalr/hubs"type="text/javascript"></script><scripttype="text/javascript">$(function(){// Form의 Submit 이벤트 통제$("form").submit(function(){vartext=$('#messageText').val();// Hub를 통해 SignalR 통신으로 서버에 보낸다.messagesHub.server.send(text);$('#messageText').val('').focus();returnfalse;});// SiglalR/Hubs에서 Generated된 javascript 코드를 할당varmessagesHub=$.connection.messages;// SignalR에서 Hub를 통해 메세지를 받는다.messagesHub.client.addMessage=function(messageFromServer){$('<li>').text(messageFromServer).appendTo("#messages");}// SignalR 시작$.connection.hub.start();});</script>}
[코드13] SignalR이 브라우저에서 실행될 클라이언트 사이드 코드
1. <script src="~/Scripts/jquery.signalR-1.0.1.js" type="text/javascript"></script> : SignalR 관련 라이브러리를 추가하면 jQuery를 확장한 자바스크립트 라이브러리가 추가되어 있을 것이다. 웹 페이지에서 사용할 수 있도록 추가하는 것이다.
2. <script src="~/signalr/hubs" type="text/javascript"></script> : 서버 코드상에서 Hub를 상속받은 Messages 클래스에 대해서 Generated하여 자바스크립트 코드를 만든었으며 서버에서 추가한 메소드가 자바스크립트에서 실행이 되도록 하고 있다. 자세한 사항은 밑에서 한번더 다루도록 하겠다.
1. SignalRTestController : 단순히 웹 페이지를 보여주기 위한 코드 밖에 없다.
2. public class Messages : Hub : 이 코드가 SignalR의 핵심이다. 여기에서 추가로 코드가 자바스크립트로 Generated로 되어 브라우저에서도 사용할 수 있게 변환이 된다.
3. Clients.All.addMessage(message) : 받은 메시지를 연결된 모든 클라이언트에게 보내주는 코드다.
이제 코드 설명은 다 끝났지만 한간지 의문점이 남게 될 것이다. Generated되어 자동으로 실행이 되도록 한다고 하였지만 무엇이 어떻게 생성이 되고 실행이 되는지는 모른다. 일반적으로는 몰라도 SignalR에서 자동으로 뒷단에서 많은 일들을 하고 있기 때문에 편리하게 브라우저의 HTML5에 제한을 생각하지 않고 편리하게 사용만 하면 된다. 그렇지만 SignalR을 더 많이 이해하기 위해 자바스크립트 소스를 분석하게 되었다. 아래 '코드14'가 브라우저에서 Generated된 자바스크립트 코드다. 너무 길어 일괄적인 코드는 생략하고 중요한 코드만 남겨 두었다.
[코드15] /signaR/Hubs에서 만들어진 자바스크립트 코드
자동으로 생성된 코드는 서버에서 Hub를 상속 받은 Messages의 분석하여 Javascript를 만든 것이다. 서버의 'Send' 메소드가 자바스크립트의 send: function (message) { }로 변환된 것을 확인 할 수 있다. 이런 방식으로 서버의 코드를 자바스크립트에서 직접 사용할 수 있도록 Proxy를 만들어서 브라우저에 보내주는 것이다. 이제 모든 코드에 대해서 살펴 보았으니 결과를 확인해 보도록 하자.
[그림13] SingalR 실행 결과 화면 ( 위쪽이 Chrome, 아래쪽이 IE10 )
1. IE에서 메시지를 'IE10에서 보내는 SignalR 메시지' 입력하고 서버에 전송한다. - 실시간으로 크롬에서 확인된다.
2. 크롬에서 메시지를 'Chrome에서 보내는 SignalR 메시지' 입력하고 서버에 전송한다. - 실시간으로 IE에서 확인된다.
위와 같이 실시간으로 보내지는 메시지를 브라우저에서 받아 처리하도록 한다. 여기까지는 WebSocket와 별 다를게 없어 보인다. HTML5의 WebSocket를 지원하지 않는 브라우저를 재현하기 위해 IE의 '관리자 도구'를 이용하여 모드를 하위 버전으로 수정하여 테스트를 진행하여 서버와이 통신이 어떻게 변경이 되는지 확인해 보도록 하겠다.
IE에서 F12로 관리자 모드를 활성화 한다. 이곳에서 네트워크 탭으로 가서 캡춰 시작 버튼을 눌러 네트워크 트래픽이 잡아 내도록 한다. 그리고 브라우저 모드를 IE10에서 IE9으로 변경하면 아래와 같은 결과를 확인 할 수 있다.
[그림14] IE의 관리자 도구 화면
이제부터 브라우저는 IE10이지만 내부적으로는 IE9과 같은 방식으로 동작하게 된다. IE9은 WebSocket를 지원하지 않아 웹 소켓 통신을 하지 못하는데 어떻게 실시간으로 값을 받아 올 수 있을까? 이건 네트워크 트래픽에 잡힌 '/signalr/connect?transport=foreverFrame ...' Url 주소를 보면 해답을 찾을 수 있다. ForeverFrame으로 서버에 계속 연결되어 있는 상태가 되어 서버에서 Flush된 데이터를 브라우저에서 받는 것이다. 그래서 네트워크 결과에 보면 계속 '보류중' 이라고 나오는 것이다. 이렇게 SignalR을 사용하면 WebSocket만을 사용하여 개발하는 것 보다 브라우저의 제약에 좀더 자유로울 수 있게 되고 추상화된 API를 제공하므로 개발의 일관성을 유지할 수 있게 된다.
지금까지 브라우저에서 서버와 통신하는 여러가지 방법중에 대표적인 기술과 최신에 나온 WebSocket 그리고 통신 기술들을 아우르는 추상화 시킨 라이브러리인 SignalR이나 Socket.IO에 대해서 간략하게 나마 알아 보았다. 어떤 기술을 선택하여 개발할지는 각각의 개발 환경이나 제약 사항에 따라 취사 선택을 해야 할 것이다. 이제 부터는 각자 자신의 몫이다. 자신의 것으로 만들고 적용하여 보다 발전된 방향으로 가야하는 짐을 여러분에게 맡기는 것이다.
소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략하도록 하겠습니다. 포스트의 내용이 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 하기 위해 노력하였습니다. 실 개발에서도 적용할 수 있도록 간단하면서도 현실적인 예제 프로그램을 통해 각 소스를 만들고 이해 시키고자 하였으며 실무에 필요한 개발요구 사항들을 해결 하는데 도움이 되고자 노력하였습니다. 그리고 소스와 같이 있는 주석을 이용해 nDoc이나 별도의 자동 Document 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다. ※ DOC에 대한 프로그램 정보 Util link
이 포스트에 있는 내용이 언제나 확실한 정답은 아닙니다. 진실이라고 생각해 왔던 전제가 시간의 지남에 따라 들어나지 않았던 다른 이면 때문에 좋은 방향으로 이끌어 낼 수 있는 역할로 변환 되는게 역사적으로도 많은 증명 있었습니다. 그렇지만 저는 현재 상황에서 최선의 답을 찾고자 노력하였으며 이 글을 읽는 다른 분들에게 다음 길을 갈 수 있도록 도와주는 디딤돌이 되고자 노력하고자 포스팅을 통해 공유하고자 하는 것입니다. 그리고 프로그래머라는 타이틀을 달고 살아야 한다면 "왜"라는 의문을 항상 가지고 다니면서 자신의 위치에 안주하지 않고 항상 노력하는 모습으로 살아 가고자 합니다. 언제든 지적이나 오류가 있으면 피드백 부탁 드리겠습니다.
ing™
이제까지 단 방향 통신에 대해서 살펴 봤다면 지금 부터는 양 방향 통신을 지원하는 WebSocket에 대해서 알아 보고자 한다. 표준 WebSocket의 API는 W3C에서, 프로토콜은 IETF(Internet Engineering Task Force)에서 HTML5의 하위로 재정을 하고 있으며 지금도 현재 진행형이다. 그리고 WebSocket은 HTTP와 마찬가지로 80번 포트를 통해 웹 서버에 연결한다. HTTP 프로토콜의 버전은 1.1이지만 아래 헤더에서 보듯이 Upgrade 헤더를 이용하여 웹 서버에 접속한다.
[HTTP 헤더 1] Client to Server WebSocket 헤더 정보
[HTTP 헤더 2] Web Server to Client 헤더 정보
[HTTP 헤더 3] HTTP Connect 메소드 헤더 정보
브라우저는 "Upgrade: WebSocket" 헤더와 함께 랜덤하게 생성한 키를 서버에 보내고 웹 서버는 이 키로 토큰을 생성하여 브라우저에 돌려준다. 위 헤더 정보는 WebSocket지원 버전에 따라 헤더 정보는 달라 질 수 있다. 이런 방식으로 handshaking을 하여 WebSocket을 연결하면 Protocol Overhead 방식으로 웹 서버와 브라우저가 WebSocket 통신을 하게 된다. Protocol Overhead 방식은 메타 데이터와 네트워크 라우팅 기법으로 여러 TCP 커넥션을 생성하지 않고 하나의 80번 포트의 TCP 커넥션을 사용하여 연결하고, 별도의 헤더로 논리적인 데이터 흐름 단위를 사용하여 여러 개의 가상 커넥션을 사용하는 효과를 가져오는 방식이다. ( Protocal Overhead는 일종의 공유기라 생각하면 쉬울것이다. 하나의 공인 IP를 가지고 내부적으로 여러대의 내부 IP에서 나눠 사용하듯이 말이다.)이런 방식으로 HTTP와 같은 포트를 사용하여 방화벽이 있는 환경에서도 별도의 설정 없이도 WebSocket을 사용 할 수 있도록 지원하고 있다.
이제까지의 내용을 간단한 도식으로 WebSocket이 웹 서버와 통신하는 흐름을 '그림6'를 통해 간략적으로 살펴 보자.
[그림6] WebSocket 통신 방법
1. Client에서 서버에 연결 시도 한다.
2. 연결 시 헤더에 Upgrade정보를 보내며 서버와 Handshaking을 하여 연결 한다.
3. 서버에서 발생된 이벤트가 있으면 WebSocket을 통해 브라우저에 보낸다.
4. 연결 종료시까지 3단계를 수행 한다.
위 순서의 2번에 대해서 '그림7'과 같이 도해식으로 좀더 자세히 살펴 보자.
[그림7] WebSocket 도해식
1. Client가 서버에게 요청을 하면서 헤더에 Upgrade, WebSocket.Version, Key... 등을 같이 보내준다.
2. 서버는 자신이 응답할 수 있는 웹소켓이고 버전이 맞으면 Token을 내려 보낸다.
3. 클라이언트는 이 토큰을 가지고 웹 서버와 Protocal Overhead 방식으로 통신을 하게 된다.
이런 방식으로 브라우저와 웹 서버가 뒷단에서 수행하여 웹 소켓 연결이 되도록 한다. 이제 준비가 되었으니 가볍고 빠른 방식으로 웹 서버에서 필요한 데이터를 주고 받으면 될 것이다.
이제 클라이언트를 담당하는 브라우저에 대해서 점검해 봐야 할 것이다. 그렇지만 아직까지 모든 브라우저에서 지원하는 방식은 아니다. 아래 '그림8'와 같이 WebSocket를 지원하는 브라우저를 확인 해 보자.
IE는 10 이상부터 지원이 되고 있으며 파이어 폭스(19+)나, 크롬(25+)도 지원하고 있다. IE는 비교적 가장 최신 버전에서만 지원하고 있으므로 개발할 때 염두에 두고 진행해야 할 것이다. 한국의 제한된 시장에서는 아직도 IE 10 이하 버전을 사용하고 있는 사용자들이 많기 때문에 개발시 검토를 잘 하고 진행해야 할 것이다. 하지만 후에 검토할 signalR( 또는 Socket.IO)을 통해서 브라우저 호환성을 극복하여 브라우저마다 다른 호환 범위를 하나의 프로그래밍 개발 접근 방식으로도 해결할 수 있는 방법이 있으니 너무 일찍 좌절감을 맞보지 않기를 소망한다. 희망을 가지고 코드 사이드에서 WebSocket를 진행하도록 하겠다.
아래 '코드7'는 브라우저에서 WebSocket의 지원하는 메소드 및 간단한 코드 조각이다. (이 코드를 확장하여 개발 범위에 맞게 가감하여 사용하면 될 것이다.)
[코드7] 브라우저에서 WebSocket을 사용하는 메소드 예제
클라이언트에서의 웹 소켓과 관련된 코드는 '코드7'이 전부라고 알고 있으면 되겠다. 이 작은 코드가 웹 소켓에서 발생하는 모든 이벤트를 보여주고 있다. 이제 이 작은 코드 조각으로 웹 서버를 통해 어떻게 응용이 되는지 살펴 보자. 아래 '코드8'은 .Net 진영에서 제공되는 ASP.NET MVC와 Razor 뷰 문법으로 구성되었으며 서버 사이드의 자체 문법 보다는 전체적인 흐름을 파악하는데 중점을 두고 코드를 살펴 보기를 바란다.
<!-- WebSocket으로 받은 데이터를 반영할 수 있는 DOM --><olid="messages"></ol>@using (Ajax.BeginForm("PostMessage", newAjaxOptions())) {
<p>
Say : @Html.TextBox("messageText")
<inputtype="submit"/></p>
}
@section scripts{<scripttype="text/javascript">$(function(){$("form").submit(function(){varform=$(this);vartxtMessageText=$(form.find('#messageText'));$.post('@Url.Action("PostMessage")',{'messageText':txtMessageText.val()},function(){txtMessageText.val('');txtMessageText.focus();});returnfalse;});start();});varstart=function(){varwsImpl=window.WebSocket||window.MozWebSocket;// 웹 소켓 초기화window.ws=newwsImpl('ws://localhost:8181/','my-protocol');ws.onmessage=function(evt){$('<li>').text(evt.data).appendTo('#messages');};ws.onopen=function(){$('<li>').text('웹 소켓 연결 되었습니다.').appendTo("#messages");};ws.onclose=function(){$('<li>').text('웹 소켓이 닫혔습니다.').appendTo("#messages");}}</script>}
[코드8] WebSocket의 브라우저에서 실행될 클라이언트 사이드 코드
1. @using (Ajax.BeginForm("PostMessage", new AjaxOptions())) : form 테그를 완성하는 Razor 문법이다. 여기에서 '@' 구문은 Razor 문법의 시작을 알리는 예약어다.
2. $(function () { $("form").submit(function () { : 는 1번에서 선언된 테그에서 발생하는 submit 이벤트를 받아서 페이지 이동을 하지 못하도록 하고 사용자가 입력한 값을 ajax로 통신하여 서버에 submit하도록 한다.
3. var wsImpl = window.WebSocket || window.MozWebSocket : 웹 소켓 객체를 생성한다. ( https://developer.mozilla.org/en-US/docs/WebSockets 사이트에서 보면 Gecko 6.0에서 개명되어 WebSocket와 MozWebSocket이 일부 브라우저와 버전에 따라서 공전할 수 있기 때문에 사용한 코드다 )
4. window.ws = new wsImpl('ws://localhost:8181/', 'my-protocol') : 서버에서는 8181 포트로 초기화를 할 것이기에 이포트를 설정하였고 뒤에 인자는 임의의 값으로 설정하면 된다. 이 코드를 통해 웹 서버와 통신할 수 있도록 초기화를 한것이다. 이 부분에서 서버와의 통신은 80번으로 하지만 내부적으로는 8181 포트를 사용하는 것처럼 동작하게 되는 것이다.
5. onmessage, onopen, onclose : 이름으로 유추할 수 있듯이 서버에서 보내주는 메세지가 있거나 상태가 변경이 되면 해당 function이 실행이 되도록 이벤트를 연결한 것이다. 이벤트가 발생되어 실행이 되면 messages의 id를 가진 '<ol id="messages"></ol>' 테그의 하위 노드에 li가 추가되면서 메시지가 보여질 것이다.
var wsImpl = window.WebSocket || window.MozWebSocket;
1. 처음 Chrome 브라우저를 통해 접속을 하고 '첫번째 웹 소켓 통신'을 입력하고 버튼을 눌러 서버에 전송 하였다.
2. IE10으로 같은 주소를 접속하고 'IE에서 보내는 웹 소켓 메시지'를 입력하고 버튼을 눌러 서버에 전송 하였다.
- Chrome에서도 동일한 메시지가 실시간으로 확인 할 수 있다.
3. Chrome에서도 'Chrome에서 보내는 웹 소켓 메시지'를 입력하고 버튼을 눌러 서버에 전송 하였다.
- IE에서도 동일한 메시지가 실시간으로 확인 할 수 있다.
이번에는 WebSocket의 통신 흐름 잡아 내 보도록 하자. 아래 '그림10'처럼 F12를 눌러 개발자 도구를 활성화 시키고 'WebSocket'를 선택하고 브라우저에서 값을 입력하여 서버에 보내면 웹 소켓 통신으로 받은 패킷을 아래처럼 잡을 수 있다.
[그림10] Chrome에서 WebSocket의 패킷을 잡아낸 그림
이 방법으로 웹 소켓과의 통신을 추적할 수 있게 되었다.
이제 웹 소켓에 대해서 대략적으로나마 살펴 보았다. 생각보다 코드의 양은 많지 않았지만 생소할 수도 있어서 코드 주석과는 별개로 밑에 세부 설명을 추가 하였으니 좀더 쉽게 이해가 되었으면 하는 바램입니다. 눈으로만 지나치시지 마시고 한번씩이라도 실습을 통해 나의 경험으로 체득하심을 극히 권해 드리며 프로그램 만으로도 행복할 수 있는 세상이 오기를 바라며 이번 포스트를 마칩니다.
다음에 소개해 드릴 signalR은 지금보다 더 쉽고 편한 방법으로 개발 할 수 있는 라이브러리에 대해서 알아 보도록 하겠습니다. 흥미로운 signalR을 기대해 주세요~
Guillermo Rauch가 만든 Socket.IO( http://socket.io/ )는 WebSocket, FlashSocket, Ajax Long Polling, Ajax Multipart streaming, Forever Iframe, JSONP Polling을 하나의 API로 추상화 해서 제공하고 있다. 즉 브라우저와 웹 서버의 종류를 파악하여 가장 적합한 기술을 자동 선택하여 사용하는 방식을 제공한다. 간다히 케이스를 정하자면 WebSocket를 지원하지 않는 브라우저에서도 상관 없이 같은 개발 방식으로 개발할 수 있도록 지원한다는 것이다. 그렇지만 아직까지는 Node.js 플랫폼 위에서만 동작하는 제약이 있다.
[코드10] Socket.IOin Browser
[코드11] Socket.IO on Node.js
이로써 Socket.io를 사용할 준비는 모두 끝났다. 브라우저에서 페이지를 열면 콘솔에는 "{ server : 'hello world!' }"가서버 콘솔에는 "{ browser : 'data' }"가 출력되는 것을 확인할 수 있을 것이다. Socket.io에 대해서 깊게 알고 싶다면 "http://socket.io/"에서 가셔서 확인할 수 있을 것이다. 물론 Windows에서도 IISNode( https://github.com/tjanczuk/iisnode ) 모듈을 통해서 node에 대한 개발 및 테스트를 할 수 있다.
소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략하도록 하겠습니다. 포스트의 내용이 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 하기 위해 노력하였습니다. 실 개발에서도 적용할 수 있도록 간단하면서도 현실적인 예제 프로그램을 통해 각 소스를 만들고 이해 시키고자 하였으며 실무에 필요한 개발요구 사항들을 해결 하는데 도움이 되고자 노력하였습니다. 그리고 소스와 같이 있는 주석을 이용해 nDoc이나 별도의 자동 Document 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다. ※ DOC에 대한 프로그램 정보 Util link
이 포스트에 있는 내용이 언제나 확실한 정답은 아닙니다. 진실이라고 생각해 왔던 전제가 시간의 지남에 따라 들어나지 않았던 다른 이면 때문에 좋은 방향으로 이끌어 낼 수 있는 역할로 변환 되는게 역사적으로도 많은 증명 있었습니다. 그렇지만 저는 현재 상황에서 최선의 답을 찾고자 노력하였으며 이 글을 읽는 다른 분들에게 다음 길을 갈 수 있도록 도와주는 디딤돌이 되고자 노력하고자 포스팅을 통해 공유하고자 하는 것입니다. 그리고 프로그래머라는 타이틀을 달고 살아야 한다면 "왜"라는 의문을 항상 가지고 다니면서 자신의 위치에 안주하지 않고 항상 노력하는 모습으로 살아 가고자 합니다. 언제든 지적이나 오류가 있으면 피드백 부탁 드리겠습니다.
ing™
이번에는 SSE(Server Sent Events)에 대해서 알아 보자. SSE는 HTML5의 스펙으로서 단방향 푸시를 구현하기 위해 제안된 표준 기술이다. 앞의 Polling, LongPolling은 묵시적인 표준과 같은 기법의 범주에 속한다 할 수 있겠다. 그렇지만 SSE가 완전한 푸시를 구현한 스펙은 아니다. 소켓 통신과 같이 서버에서 클라이언트로 임의의 시간에 데이터를 보내주는 사항은 아니며 이전 기술과 같은 폴링 기법과 같은 유사한 방법으로 커넥션을 유지해 구현되고 있다. '그림6'로 전체 동작 방식을 이해해 보자.
[그림 6] SSE 동작 방식
1. 브라우저의 javascript에서 EventSource('url') 객체를 이용하여 웹서버와 통신 세션을 맺는다.
2. 서버에서 이벤트가 발생하면 개발자가 정의한(여기서는 Message로 하겠다) 해당 정보를 브라우저에 보내주고 커넥션은 유지한다.
3. 서버에서 이벤트가 두번째로 발생이 되면 유지되고 있는 커넥션을 통해 Message를 보내준다.
4. 연결 종료시 까지 3단계를 수행 한다.
5. 만약 중간에 어떤한 이유로 종료가 되었다면 EventSource 객체는 다시 통신 세션을 맺는다.
SSE는 지금까지 살펴본 유사 Polling 기법과는 다르게 서버와의 커넥션을 유지하여 서버에서 발생한 이벤트 정보를 브라우저에 보내주는 방식이다. 추가 이벤트 정보를 받을 때마다 유지된 커넥션을 통하므로 재 연결시 필요한 부가 정보(사용자 정보, 쿠키, 헤더,...) 없이 데이터를 받을 수 있게 된다.
<!-- Polling 하여 받은 데이터를 반영할 수 있는 DOM --><olid="messages"></ol>@section scripts{<scripttype="text/javascript">$(function(){if(typeof(EventSource)!=="undefined"){// 이벤트 소스를 선언한다. EventSource('/ControllerName/GetMesages');vareventSrc=newEventSource("@Url.Action("GetMessages")");// 이벤트 소스 객체에서 데이터를 받는 이벤트 처리 핸들러 선언eventSrc.addEventListener("open",function(evt){$("<li>").text('Opened!').appendTo("#messages");});// 이벤트 소스 객체에서 데이터를 받는 이벤트 처리 핸들러 선언eventSrc.addEventListener("message",function(evt){$("<li>").text(evt.data).appendTo("#messages");});// 이벤트 소스 객체에서 에러 이벤트 처리 핸들러 선언eventSrc.addEventListener("error",function(evt){console.log(evt);});}else{alert('EventSource 객체를 지웒하지 않는 브라우저 입니다.');}});</script>}
[코드5] SSE - Browser의 HTML과 Javascript
위 코드는 페이지가 로딩이 되면 EventSource 객체를 통해 웹 서버에 커넥션을 맺는다. 그리고 EventSource에서 제공하는 이벤트에 대해서 open, message, error에 대해 처리할 수 있는 핸들러를 각각 선언하여 연결한다. open 이벤트는 #messages 테그에 'Opened!'를 표시하도록 하고 message 이벤트는 서버에서 보내준 데이터 값을 표시하고, error 이벤트는 console에 표시하도록 하였다. 이제 아래 코드를 통해 서버쪽의 코드를 알아 보자.
///<summary>/// 초기화시 중복 초기화가 되지 않도록 잠금 역할하는 객체///</summary>privatestaticobject_blockCollectionLock=newobject();
///<summary>/// 메세지 컨테이너 역할///</summary>privatestaticBlockingCollection<string>_data=null;
///<summary>/// 생성자///</summary>publicEventSource2Controller()
{
InitSSE();
}
//// GET: /EventSource2/publicActionResultIndex()
{
returnView();
}
///<summary>/// 중간 정산 - 중간에 채워진 버퍼의 내용을 보낸다.///</summary>publicActionResultGetMessages()
{
// EventSource로 통신할 때는 ContentType을 'text/event-stream'으로 해야 한다.Response.ContentType="text/event-stream";
Response.Expires=-1;
varmessage=string.Empty;
while (true)
{
Thread.Sleep(1000);
if (_data.TryTake(outmessage, TimeSpan.FromMilliseconds(1000)))
{
// 종료하지 않고 일부 데이터만 웹 브라우저에 보냄
if (Response.IsClientConnected)
{
Response.Write("data: "+message+"\n\n");
Response.Flush();
}
}
else
{
// 서버에 데이터가 없으면 종료하고 커넥션을 닫는다.Response.Write("data: 종료\n\n");
Response.Flush();
Response.End();
break;
}
}
returnContent("", "text/event-stream");
}
///<summary>/// 초기화 함수///</summary>
[NonAction] // 웹에서 직접 호출해도 동작하지 않도록 해주는 AttributeprotectedvoidInitSSE()
{
_data=newBlockingCollection<string>();
_data.Add("started");
for (inti=0; i<10; i++)
{
_data.Add("item"+i.ToString());
}
_data.Add("ended");
}
[코드6] SSE - Server side 코드
'코드6'의 Server-side 코드는 이전 Polling 기법과는 다르게 다소 복잡하게 수행이 된다. 생성자에서 초기화 함수인 InitSSE()를 호출하여 초기화를 진행하고 Browser에서 EventSource를 통해 요청한 GetMessage()는 내부적으로 루프를 돌면서 수행하며 계산된 결과를 브라우저에 'Response.Write()'를 통해서 브라우저에 보내주고 있다. 이때 커넥션을 종료하지 않고 그대로 유지하며 지속적으로 클라이언트인 브라우저에 보내준다. 실제적인 비지니스 개발에서는 이 부분에서 DB를 폴링하여 적절한 값의 변화를 브라우저에 보내줘 클라이언트에서는 실시간으로 정보가 변경되는 것 같이 보여주도록 할 수 있다는 것이다. 이전 Polling과는 다른점은 EventSource를 통해 한번만 연결된 상태에서 보다 적은 통신 용량으로 같은 효과를 낼 수 있다는 것이다. 아래 '그림7'로 결과 화면을 확인해 보자.
[그림7] SSE - 결과 화면
소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략하도록 하겠습니다. 포스트의 내용이 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 하기 위해 노력하였습니다. 실 개발에서도 적용할 수 있도록 간단하면서도 현실적인 예제 프로그램을 통해 각 소스를 만들고 이해 시키고자 하였으며 실무에 필요한 개발요구 사항들을 해결 하는데 도움이 되고자 노력하였습니다. 그리고 소스와 같이 있는 주석을 이용해 nDoc이나 별도의 자동 Document 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다. ※ DOC에 대한 프로그램 정보 Util link
이 포스트에 있는 내용이 언제나 확실한 정답은 아닙니다. 진실이라고 생각해 왔던 전제가 시간의 지남에 따라 들어나지 않았던 다른 이면 때문에 좋은 방향으로 이끌어 낼 수 있는 역할로 변환 되는게 역사적으로도 많은 증명 있었습니다. 그렇지만 저는 현재 상황에서 최선의 답을 찾고자 노력하였으며 이 글을 읽는 다른 분들에게 다음 길을 갈 수 있도록 도와주는 디딤돌이 되고자 노력하고자 포스팅을 통해 공유하고자 하는 것입니다. 그리고 프로그래머라는 타이틀을 달고 살아야 한다면 "왜"라는 의문을 항상 가지고 다니면서 자신의 위치에 안주하지 않고 항상 노력하는 모습으로 살아 가고자 합니다. 언제든 지적이나 오류가 있으면 피드백 부탁 드리겠습니다.
ing™
이번에는 LongPolling에 대해서 알아 보자. 좀전에 Polling에 대해서 알게 되었다면 별로 어렵지 않게 이해를 할 수 있을 것으로 예상한다. 그냥 Polling은 주기적으로 서버에 요청하는 방식이지만 LongPolling 방식은 한번 요청하고 서버에 데이터가 있을때만 반환받는 방식이다. 아래 '그림3'으로 보면 좀더 쉽게 이해할 수 있을 것이다.
[그림3] LongPolling 방식
1. Request1로 서버에 요청하고 서버에서 Event( or Data )가 발생되면 해당 형식( 여기서는 Message라고 정의 )을 브라우저에 보내주고 브라우저 커넥션은 끊어진다.
2. 받은 데이터를 처리하고 곧바로 서버에 Request2를 요청하고 데이터가 발생할 때까지 대기한다.
3. 1단계를 다시 시작한다.
이와 같은 단계로 LongPolling 방식이 구동이 된다. Polling와 LongPolling 방식은 전체적으로 비슷한 방식을 개발할 수 있으나 다른점은 'LongPolling'이라는 이름처럼 길게 대기하여 서버에 응답요청을 한다는데 있다. 길게 기다리다가 "있으면 보내줘!"와 같은 컨셉이다. 이제 실제 코드로 알아가 보자.
<!-- Polling 하여 받은 데이터를 반영할 수 있는 DOM --><olid="messages"></ol>@using (Ajax.BeginForm("PostMessage", newAjaxOptions())) {
<p>
Say : @Html.TextBox("messageText")
<inputtype="submit"/></p>
}
<!--
위 코드는 아래의 HTML Tag로 변환된다.
<form action="/LongPolling/PostMessage" data-ajax="true" id="form0" method="post"> <p>
Say : <input id="messageText" name="messageText" type="text" value="" />
<input type="submit" />
</p>
</form>
-->
@section scripts{<scripttype="text/javascript">$(function(){// Begin the LongPolling loopgetNextMessage();// Form의 Submit 이벤트 통제$("form").submit(function(){varform=$(this);
// messageText의 Html tag에 입력된 값을 가져온다.
vartxtMessageText=$(form.find('#messageText'));
// Ajax로 서버에 입력한 값을 넘겨준다.$.post('@Url.Action("PostMessage")',{'messageText':txtMessageText.val()},function(){txtMessageText.val('');txtMessageText.focus();});// HTML의 Submit가 일어나지 않도록 한다.returnfalse;});});// 서버에 메시지가 있는지 문의 요청functiongetNextMessage(){$.post("@Url.Action("GetNextMessage")",function(message){$("<li>").text(message).appendTo("#messages");// 응답받은 즉시 다시 요청getNextMessage();});}</script>}
[코드3] LongPolling - Browser의 javascript 코드
위 코드에서는 '그림3'과 같이 사용자의 입력을 받는 부분이 있다.
[그림4] LongPolling 테스트 대기 화면
위 '코드3'의 전체적인 흐름을 파악해 보자
- HTML이 완성되면 getNextMessage()를 호출하여 서버에 요청을 한다.
- Html의 form에서 submit 버튼을 눌렀을 때 페이지 이동이 일어나지 않도록 처리한다.
- getNextMessage() 함수는 서버에 GetNextMessage()를 호출하고 반환 받을 때까지 대기한다.
- 사용자에게 데이터 입력을 받고 제출 버튼을 누르면 ajax의 Post 방식으로 PostMessage 함수를 호출하여 넘겨준다.
이제 서버측 '코드4'에 대해서 알아 보자
///<summary>/// 요청이 오면 임의의 시간 대기 후 응답///</summary>///<returns></returns>publicasyncTask<string>GetNextMessage()
{
returnawait_nextMessage.Task;
}
///<summary>/// Lock object///</summary>staticobject_nextMessageLock=newobject();
///<summary>/// for LongPolling object///</summary>staticTaskCompletionSource<string>_nextMessage=newTaskCompletionSource<string>();
publicvoidPostMessage(stringmessageText)
{
lock (_nextMessageLock)
{
// Concurrent 해결 하기 위해 임시 변수에 옮긴다.varoldNextMessage=_nextMessage;
_nextMessage=newTaskCompletionSource<string>();
oldNextMessage.SetResult(messageText);
}
}
[코드4] LongPolling - Server side 코드
1. GetNextMessage 함수가 호출이 되면 return await _nextMessage.Task 구문에서 리턴값이 있을 때까지 비 동기로 대기 한다.
2. PostMessage 함수가 호출 될 때 위에서 대기하고 있던 객체인 _nextMessage 객체에 SetResult(msg)에 값을 넘겨주면 1에서 대기하던 객체는 해당 값을 반환하도록 해주며 다시 대기하도록 한다. 이때 Lock를 통해 쓰레드 안정성을 확보하도록 하였다.
3. TaskCompletionSource<string> 객체는 입력값이 string이고 출력 값이 string인 객체로 선언되었으며 비 동기로 동작할 수 있도록 .Net Framework 4에서 부터 제공하는 클래스다.
4. public async Task<string> GetMessage()는 비 동기로 수행된다고 알려주는 C# 5.0 예약어다. 이 함수 안에는 반드시 await 예약어를 사용하여 비 동기로 사용하는 구문이 있어야 한다. - 이 부분은 node.js의 특징인 비 동기 방식을 .Net에서 지원하는 것이라 이해해도 되겠다.
이제 LongPolling에 대해 전체적인 흐름을 이해했으리라 생각이 든다. 지금까지 살펴보았다면 꼭 실습으로 한 번씩 테스트를 해 보기를 바라며 정 여건이 되지 않는다면 마지막 편에 제공되는 전체 소스파일을 다운바다 실행해 보기 바란다.
마지막으로 결과 화면으로 확인해 보자 아래 '그림5'를 통해 확인 할 수 있다.
[그림5] LongPolling 결과 화면
위 화면에서 제출을 누르면 “5.Step #5" 밑에 "6. Step #6"이 추가 됨을 확인 할 수 있을 것이다.
소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략하도록 하겠습니다. 포스트의 내용이 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 하기 위해 노력하였습니다. 실 개발에서도 적용할 수 있도록 간단하면서도 현실적인 예제 프로그램을 통해 각 소스를 만들고 이해 시키고자 하였으며 실무에 필요한 개발요구 사항들을 해결 하는데 도움이 되고자 노력하였습니다. 그리고 소스와 같이 있는 주석을 이용해 nDoc이나 별도의 자동 Document 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다. ※ DOC에 대한 프로그램 정보 Util link
이 포스트에 있는 내용이 언제나 확실한 정답은 아닙니다. 진실이라고 생각해 왔던 전제가 시간의 지남에 따라 들어나지 않았던 다른 이면 때문에 좋은 방향으로 이끌어 낼 수 있는 역할로 변환 되는게 역사적으로도 많은 증명 있었습니다. 그렇지만 저는 현재 상황에서 최선의 답을 찾고자 노력하였으며 이 글을 읽는 다른 분들에게 다음 길을 갈 수 있도록 도와주는 디딤돌이 되고자 노력하고자 포스팅을 통해 공유하고자 하는 것입니다. 그리고 프로그래머라는 타이틀을 달고 살아야 한다면 "왜"라는 의문을 항상 가지고 다니면서 자신의 위치에 안주하지 않고 항상 노력하는 모습으로 살아 가고자 합니다. 언제든 지적이나 오류가 있으면 피드백 부탁 드리겠습니다.
ing™
Polling에 대해서 알아보기 전에 우선은 Ajax와 인터랙티브 웹 페이지에 대해 간략히 알아 보자. Ajax 통신은 브라우저에서 제공해주는 특별한 객체를 통해(IE - new ActiveXObject("Microsoft.XMLHTTP"), Other - new XMLHttpRequest()) 사용자의 행위에 따라 페이지의 새로 고침 없이 Background에서 서버에서 원하는 데이터를 가져와 페이지의 일부분만을 수정할 수 있는 메커니즘을 말한다. 이런 개발 방식으로 개발된 웹 페이지를 접한 사용자들은 기존의 페이지에서는 느낄 수 없었던 사용자 경험을 얻을 수 있었다. 이 후 수많은 사이트에서 이 개발 방식으로 웹 페이지를 제공하게 되었다. 이런 개발 방식이 조금 더 응용되어 Polling 기법으로 사용자의 행위 없이도 서버에서 변경된 정보를 실시간 - 사용자의 입장에서는 실시간이나 개발자 입장에서는 실시간 지향적 이라 하겠다 - 적으로 업데이트를 할 수 있게 되었다. 간단하게 예를 들면 브라우저에서 서버에게 주기적으로 "데이터가 있어?"라고 물어 보고 서버에서 데이터의 여부에 따라 다르게 대답 하는 것이라 하겠다. 브라우저에서는 받은 데이터로 웹 페이지의 일부분만을 업데이트하여 사용자에게 보여주므로 빠른 응답을 사용자에게 경험하게 할 수 있다. 대표적인 애플리케이션은 웹 채팅이다. 상대방에서 보낸 메시지 여부를 주기적인 Polling으로 알아내어 사용자에게 보여주는 것이다.
Polling의 사전적인 의미를 알아보자. N사이트의 국어사전의 뜻을 보면 다음 표와 같이 풀이하고 있다.
이 뜻풀이처럼 데이터가 필요한 곳에서 제공자에게 송신을 요구하는 패턴을 Polling 방식이라고 할 수 있겠다. 즉 원하는 곳에서 당겨가는 방식이다. 그렇다면 웹 페이지와 서버 간에는 어떻게 Poll이 이루어지고 있는지를 아래 '그림1'을 통해서 전체적인 흐름을 알아 가 보자.
[그림1] Polling 방식
웹 페이지에서 Polling을 구현하는 방식은 setTimeout() 함수를 이용해 주기적인 시간 간격으로 서버에 요청하여 원하는 정보를 얻어오도록 하고 있다.
'그림1'에서와 같이 첫번째 요청처럼 브라우저에서 Ajax로 'Request1'을 요청하면 서버에서는 유효한 데이터가 있는지 조회하고 없으면 개발자가 정의한 형식(여기서는 Empty라고 정의)으로 리턴받고 브라우저 커넥션은 끊어진다.
setTimeOut()함수로 'Request2'요청이 발생할 당시 서버에서 이벤트가 발생하여 유효한 데이타가 있으면 Empty와 대신 개발자가 정의한 형식(여기서는 Message라고 정의)으로 리턴받고 브라우저 커넥션은 끊어진다.
1단계를 다시 시작하여 서버에서 반영된 유효한 값을 브라우저에 업데이트 한다.
위와 같은 방식으로 서버의 변경된 데이터를 브라우저에 지속적으로 반영하여 실시간 반응을 지향하는 웹 페이지를 만들었다.
이제 예제를 통해서 실제적인 코드로 살펴 볼 것이다. ( 실행해 볼 수 있는 소스코드를 마지막 포스트에서 첨부할 것 이니 다운받아 확인할 수 있습니다. )
<!-- Polling 하여 받은 데이터를 반영할 수 있는 DOM --><olid="messages"></ol>
@section scripts{<scripttype="text/javascript">$(function(){// Begin the Polling loopgetNextMessage();});functiongetNextMessage(){// Http post method request
//@Url.Action("GetNextMessage") -> /현재 페이지/GetNextMessage로 치환된다$.post("@Url.Action("GetNextMessage")",function(message){$("<li>").text(message).appendTo("#messages");});// Http get method request//$.get("@Url.Action("GetNextMessage")", function (message) {// $("<li>").text(message).appendTo("#messagees");//});setTimeout(getNextMessage,1000);}</script>}
[코드1] Polling - Browser의 HTML과 javascript 코드
( ASP.NET MVC4의 Razor 문법으로 작성된 코드다. )
위 코드는 페이지가 로딩이 되면 getNextMessage() 함수를 호출하여 서버에 데이터를 요청하고 <ol> 노드에 <li> 노드를 추가하는 코드다. ( 서버 요청을 HttpPost 또는 HttpGet으로 할 수 있다 ) 그리고 setTimeout를 통해 1초후에 다시 getNextMessage()를 호출하도록 되어 있다. 별도의 문제나 행위가 발생하지 않는 한 무한 호출이 되는 단순한 구조로 구성 되어 있다. 이제 아래 코드를 통해 서버쪽의 코드를 알아 보자.
///<summary>/// 요청이 오면 즉시 응답///</summary>///<returns></returns>publicstringGetNextMessage()
{
intwaitingCount=0;
varnow= DateTime.Now;
return"Response at "+now.ToString() +"."+now.Millisecond+" 서버 대기 시간 : "+waitingCount+"ms";
}
[코드2] Polling - Server side 코드
( 서버 사이트 코드는 ASP.NET MVC4 기반에서 작성이 되었다. )
위 코드는 따로 설명을 할 필요도 없을 정도로 간단하여 짧게 설명을 하도록 하겠다. GetNextMessage()함수는 호출이 되면 현재 시간을 알아내 string으로 반환하도록 되었다. 이제 위 결과 화면을 아래 그림을 통해 확인해 보자.
[그림2] Realtime Apps - Polling 실행 결과 화면
( 위 화면은 Visual studio에서 ASP.NET MVC4 템플릿에서 기본 제공되었다.)
'그림2'의 결과 화면을 보면 1초마다 호출된 결과를 화면에 뿌려주는 것을 확인할 수 있다. 이제 코드와 결과 화면까지 확인을 하였다. 이제 Polling이 어떤 방식으로 구성되고 개발되는지 대략적으로 알 수 있을 것이라고 기대한다. 위 예제는 MS 진영에서 개발 및 테스트를 할 수 있지만 Java나 PHP, 기타 Ruby와 같은 곳에서도 비슷한 방식으로 구현이 될 것이므로 이해 하는 데에 크게 무리가 없을 것이라 생각한다.
Tip!
Request가 지속적으로 일어 날 때마다 서버에서는 유효한 사용자의 요청인지를 알아내기 위해 내부적으로 HTTP Handshanking이 지속적으로 일어나기 때문에 오버헤드가 발생한다. 그래서 예측할 수 없는 웹 서비스를 제공하는 서버에서는 성능에 대한 고려를 충분히 해야 한다.
소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략하도록 하겠습니다. 포스트의 내용이 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 하기 위해 노력하였습니다. 실 개발에서도 적용할 수 있도록 간단하면서도 현실적인 예제 프로그램을 통해 각 소스를 만들고 이해 시키고자 하였으며 실무에 필요한 개발요구 사항들을 해결 하는데 도움이 되고자 노력하였습니다. 그리고 소스와 같이 있는 주석을 이용해 nDoc이나 별도의 자동 Document 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다. ※ DOC에 대한 프로그램 정보 Util link
Real time Apps #1 Polling, LongPolling, Server Sent Events, Websocket, SignalR
실시간 인터랙티브 웹 페이지(또는 앱, 이하 웹 페이지로 통일)에 대해서 익히 들어 봤을 것이다. 사용자의 행동(클릭, 스크롤,...)으로 웹 브라우저에서 서버의 변경된 데이터를 사용자에게 보여 줄 수 있는 웹 페이지 말이다. 사용자의 발전되는 경험으로 인해 오래전부터 인터랙티브형 웹 페이지에 대해 요구되고 있었으며, Ajax 방법론이 대두 되면서 사용자가 만족할 만한 인터랙티브 웹 페이지를 만들 수 있었다. 그렇지만 앞으로의 개발 방향이 기존의 레거시 시스템을 웹을 지원하는 방향으로 흘러가고 있으며 Active-X, Flash, Silverlight를 제외 하고 표준 HTML 과 CSS, Javascript 만으로 C/S 프로그램이 하던 일을 대체해야 한다는 시대적 흐름이 형성 되었다. 그리고 시대적으로 새로운 디바이스(이기종 스마트폰, 이기종 타블렛, Other...)가 출시되었으며 새로운 규격의 브라우저에서도 호환되게 개발해야 한다. 개발자는 이러한 여러 가지 제약 사항을 이겨내고 만족 스러운 인터랙티브형 웹페이지를 구현하기란 쉽지 않다.
그렇지만 개발자들은 고객의 요구 사항을 최대한 만족 시키기 위해 부단히 노력하여 여러 가지 시도로 인터랙티브형 웹 페이지가 되도록 노력하였다. 그래서 Polling, LongPolling등과 같은 방법으로 인터랙티브형 웹 페이지가 되도록 개발하게 되었다. 하지만 처음 설계될 당시의 HTTP 프로토콜과 HTML의 한계성을 가지고 있기 때문에 개발자는 뒤에서 컨트롤하고 사용자에게는 감추는 역할을 더 많이 신경 쓰게 된 것이다. 세계의 여러 개발자들이 이런 문제점을 개선하고자 HTML5와 ECMAScript 6(ECMAScript는 표준화된 스크립트 프로그래밍 언어를 말한다. - 위키피디아 click) 표준이 정립이 되고 있으며 Windows, Linux 같은 또 다른 하나의 개발 플랫폼으로 HTML이 진화를 하고 있다.
HTML5에 대해서 좀더 이야기를 하고 싶으나 주제에 벗어나므로 간단히 정리하고자 한다. HTML5를 짧게 수식 같이 정리하면 아래 '표1'과 같을 것이다.
HTML5 = Javascript + CSS + Templates
[표1] HTML5 정의 표
(이것은 개인적인 정의일 뿐 W3C 공식적인 정리는 아님을 알려 드린다.)
Javascript와 CSS에 대해서는 대부분 알 고 있을 것이라 생각하고 Template에 대해서만 더 설명을 더 하겠다.
Javascript
사용자의 행위에 대해서 처리
CSS
HTML의 레이아웃 및 디자인
Template
HTML Tag의 유동적인 데이터를 템플릿 엔진을 통해 DOM(Document Object Model) 관리가 되도록 한다. Template engine은 JsRender(구 jquery-template), HandleBars와 같이 여러 라이브러리를 통해 얻을 수 있으며 HTML의 대부분 작업을 담당한다.
간단하게나마 HTML5에 대해서 마무리를 하고 이제 본론으로 돌아와 인터랙티브 웹 페이지를 가능 토록하는 방법 중에 서버와의 통신으로 데이터를 가져오는 기법이란 주제로 좁혀 다루고자 한다. 아래 목차대로 하나씩 짚어 나가면서 진행해 보도록 하겠다.
본격적으로 살펴보기에 앞서서 아래 '표1'과 같이 데이터 통신에 관한 기술 정리를 간단하게나마 해 보았으니 참조만 하자.
기술 규격
기술 명칭
COMET
Polling
LongPolling
Hidden iframe
XMLHttpRequest
XMLHttpRequest long polling
Script tag long polling
HTML 5
SSE(Server Sent Events - Streamming) - Not Socket
WebSocket - HTML5
SignalR- hybrid
Socket.IO - hybrid
※ hybrid : 하위 브라우저 호환을 지원하는 기능으로 WebSocket를 지원하지 않는 브라우저에서는 다른 하위 통신 방식으로 데이터 요청을 지원해준다.
ASP.NET MVC에서 Knockoutjs를 사용하는 프로젝트에서 UI 컨트롤들을 쉽게 KnockOut와 바인딩이 되도록 Generated 된 코드를 만들어 주는 라이브러리다. 서버의 Model을 기반으로 브라우저에서 사용하는 ViewModel과 View Data를 자동으로 만들어 주고 있다. 사용방법은 아래 코드와 같이 사용하면 된다.
아래 코드는 KnockOutMVC 프레임웍에서 샘플 코드인 HelloWorld이다.
[코드] Model
[코드] Razor
[코드] Controller
[코드] HTML (autogenerated)
이와 같은 HTML의 Knockoutjs에서 사용하는 View데이터와 ViewModel을 서버에서 자도으로 만들어서 내보내 준다. 이제 웬만한 기능을 모델 설계 만으로 브라우저까지 일관되게 내려 받을 수 있게 되었다. ViewModel을 연결 시켜주는 노가다성 작업을 상당부분 줄여 줘 야근모드가 없어 지지 않을까 생각해 본다.
위와 같은 코드의 예제 말고도 다른 예제들이 있다. 관심이 있다면 직접 확인해 보도록 하자.
SignalR Cross browser Compatibility over WebSocket
SignalR은 Microsoft에서 WebSocket을 지원할 수 있도록 해주는 프레임 웍이다. SignalR은 WebSocket 프로토콜 위에서 돌아 가는 방식이며 WebSocket을 지원하지 않는 브라우저에서도 호환될 수 있도록 지원하고 있다. 그냥 단순히 WebSocket을 사용하는 웹 페이지를 만든다면 HTML5를 지원하는 브라우저 여부를 판단하여 그렇지 않은 브라우저는 다른 방식으로 구현을 해야 할 것이다. 그렇지만 SignalR을 사용하여 편리하고 일관되게 하위 호환성을 확보 할 수 있을 것이다.
SignalR을 지원하는 브라우저에 대해서 알아 보자
jQuery는 1.6.4 이상 지원
Microsoft Internet Explorer versions 8, 9 and 10. Desktop, and Mobile 지원.
Mozilla Firefox: current version - 1, both Windows and Mac versions.
Google Chrome: current version - 1, both Windows and Mac versions.
Safari: current version - 1, both Mac and iOS versions.
Opera: current version - 1, Windows only.
Android browser
jQuery 1.6.4이상의 버전과 브라우저만 있으면 SignalR이 제공해주는 일관된 방법으로 개발 할 수 있을 것이다.