이 포스트에 있는 내용이 언제나 확실한 정답은 아닙니다. 진실이라고 생각해 왔던 전제가 시간의 지남에 따라 들어나지 않았던 다른 이면 때문에 좋은 방향으로 이끌어 낼 수 있는 역할로 변환 되는게 역사적으로도 많은 증명 있었습니다. 그렇지만 저는 현재 상황에서 최선의 답을 찾고자 노력하였으며 이 글을 읽는 다른 분들에게 다음 길을 갈 수 있도록 도와주는 디딤돌이 되고자 노력하고자 포스팅을 통해 공유하고자 하는 것입니다. 그리고 프로그래머라는 타이틀을 달고 살아야 한다면 "왜"라는 의문을 항상 가지고 다니면서 자신의 위치에 안주하지 않고 항상 노력하는 모습으로 살아 가고자 합니다. 언제든 지적이나 오류가 있으면 피드백 부탁 드리겠습니다.
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