728x90

재밌네... ㅎㅎ

별도의 비표준 플러그인 없이,  웹에서 채팅을 이렇게 쉽게 구현할 수 있는 시대라니...
6년전, 웹 소켓에 대해 나름 정리한 적이 있다.

 
이미 그 시대는 오래전(?)에 도래했건만, 개인적으로 방치해 두다가, 어제 밤 문득...

웹 소켓과 node.js를 사용해서 채팅 샘플을 구현해 보고 싶다는 생각이 스쳐 지나갔다.

socket.io라는 자바스크립트 라이브러리가 존재한다더니, 이건 뭐.. 다른건 볼 필요도 없네.

socket.io는 Node.js 기반으로 소켓 통신이 가능하도록 해 주는 확장 라이브러리인데, HTML5의 정식표준인 WebSocket을 지원하지만, 미지원 환경에 대한 호환성을 위해 Comet 같은 기술도 하나의 API로 추상화 시킨 양방향 통신 라이브러리이다.

socket.io는 WebSocket이 지원되지 않는 환경에서 실시간 양방향 통신을 구현하기 위해 다음과 같은 fallback 기술을 사용하고 있다.

- FlashSocket, AJAX Long Polling, AJAX Multi part Streaming, IFrame, JSONP Polling

기본적으로 Node.js 설치해 주고, express와 socket.io를 npm을 통해 다음과 같이 설치한다.
- npm install express
- npm install socket.io

이제 환경 설정은 끝이다. 자바스크립트 기반 개발환경은 이렇듯 심플하다. 내가 좋아하는 이유 중 주요한 팩터이다.

* socket.io를 이용한 웹 채팅 구현하기
먼저 다음과 같은 3개의 파일을 준비한다.

1. chatServer.js
- 채팅 서버 역할을 하는 자바스크립트 파일로, Node.JS에 의해 호스팅되며 socket.io 라이브러리를 통해 소켓 서버를 구현한다. 또한 부가적으로 http 리소스 요청에도 응답할 수 있도록 한다.

2. chatClient.js
- 채팅 서버와 통신을 하는 클아이언트 자바스크립트 파일로, 역시 socket.io 라이브러리를 사용하며 채팅을 위한 간단한 DOM 조작을 수행한다.

3. chatClient.html
- chatClient.js가 정의된 html 파일이며, 채팅창과 텍스트 박스로 간단히 구성된 뷰를 제공한다.

먼저 chatServer.js 파일의 코드부터 보자.

var app = require("express")();
var url = require("url");

//루트에 대한 get 요청에 응답
app.get("/", function(req, res){
 console.log("get:chatClient.html");
 //최초 루트 get 요청에 대해, 서버에 존재하는 chatClient.html 파일 전송
 res.sendFile("chatClient.html", {root: __dirname});
});

//기타 웹 리소스 요청에 응답
app.use(function(req, res){
 var fileName = url.parse(req.url).pathname.replace("/","");
 res.sendFile(fileName, {root: __dirname});
 console.log("use:", fileName); 
});

//http 서버 생성
var server = require('http').createServer(app);
server.listen(3000);
console.log("listening at http://127.0.0.1:3000...");

//클로저를 사용해, private한 유니크 id를 만든다
var uniqueID = (function(){
 var id = 0;
 return function(){ return id++; };
})();

//서버 소켓 생성
var socket = require('socket.io').listen(server);
//소켓 Connection 이벤트 함수
socket.sockets.on('connection', function(client){
  //클라이언트 고유값 생성 
  var clientID = uniqueID();
  console.log('Connection: '+ clientID);
 
  //서버 receive 이벤트 함수(클라이언트에서 호출 할 이벤트)    
  client.on('serverReceiver', function(value){
    //클라이언트 이베트 호출     
    socket.sockets.emit('clientReceiver', {clientID: clientID, message: value});  
  });

});

다음으로 chatClient.js 소스이다.

window.onload = function(){ 
 //클라이언트 소켓 생성
 var socket = io.connect('ws://127.0.0.1:3000');

 //DOM 참조
 var div = document.getElementById('message');
 var txt = document.getElementById('txtChat');
 //텍스트 박스에 포커스 주기 
 txt.focus();
 
 //텍스트 박스에 이벤트 바인딩
 txt.onkeydown = sendMessage.bind(this); 
 function sendMessage(event){     
  if(event.keyCode == 13){
   //메세지 입력 여부 체크   
   var message = event.target.value;
   if(message){
     //소켓서버 함수 호출  
     socket.emit('serverReceiver', message);
     //텍스트박스 초기화
     txt.value = '';
   }
  }
 };
 
 //클라이언트 receive 이벤트 함수(서버에서 호출할 이벤트)
 socket.on('clientReceiver', function(data){  
   //console.log('서버에서 전송:', data);   
   //채팅창에 메세지 출력하기
   var message = '['+ data.clientID + '님의 말' + '] ' + data.message;
   div.innerText += message + '\r\n';
   //채팅창 스크롤바 내리기  
   div.scrollTop = div.scrollHeight;   
 });
};

 

 마지막으로 chatClient.html 파일이다.

<script src="chatClient.js"></script>
<script src="/socket.io/socket.io.js"></script>

<div id="message" style="background-color:#F5F5F5; width:400px; height:200px; OVERFLOW-Y:auto; word-wrap: break-word"></div>

<div id="chatInput">  
 메시지를 입력하세요 <input type='text' id='txtChat' name="txtChat"> 
</div>

 

* 실행흐름
1) 서버 실행
- Node의 쉘기능을 이용해 다음과 같이 chatServer.js를 실행한다. 실행하면 다음 그림과 같이 console 로그가 프롬프트 창에 찍힌다.

 

2) 클라이언트 실행
- 클라이언트에서는 단순히 브라우저로 지정된 url로 접근하기만 하면 된다.

브라우저로 chatServer.js 서버로 접속하면, 루트 get요청(http://127.0.0.1:3000)에 따른 chatClient.html 파일이 클라이언트로 다운로드 되고, 이 HTML파일이 브라우저에서 실행되면서 chatClient.js 파일에 대한 요청이 다시 이뤄져 자바스크립트 코드가 다운로드 되고 실행된다. 이 클라이언트 자바스크립트가 실행되어 소켓서버와 클라이언트는 서로 연결이 이뤄진다.

다음 그림은 브라우저에서 해당 url로 접근했을 때 서버측 로그화면이다.

 


* 결과화면

- 총 4개의 사용자가 접근하는 시나리오로 가정하여, 4개의 브라우저를 동시에 사용한다. 각 브라우저에서 해당 url로 접근하고 각자 채팅을 해 본다. 이때 각 클라이언트 구분은 서버측 코드에서처럼 임의의 순처적 고유값을 부여한 숫자로 사용한다.

다음 화면은 브라우저 4개를 띄우고, 채팅을 해본 모습니다. 혼자놀기에 좋다 ㅡ,ㅡ;


지금까지 알아본 샘플 채팅은 채팅 구현을 위한 가장 기본적이면서도 필수적으로 구현되어야 하는 것들만 다뤘다. 실제 업무에서 채팅을 구현한다면 성능과 안정성 등을 위해 다양한 방어코드, 추가기능 코드, 최적화 기법 등의 추가 작업이 필요할 것이다.

기본적인 흐름과 코드를 알았으니, 살을 붙여 나가면 될 일이다. 

'모바일 > Javascript' 카테고리의 다른 글

[Node.js] CORS 설정(Cross Domain 요청 허용)  (0) 2016.07.15
[AngularJS] REST API 통신(with Node.js)  (0) 2016.07.15
[AngularJS] Route  (0) 2016.07.14
[AngularJS] Directive  (0) 2016.07.13
[AngularJS] $scope과 controller-as 문법  (0) 2016.07.12

[HTML5] Web Socket (웹 소켓)

Posted in 모바일/HTML5 // Posted at 2010. 9. 30. 13:44
728x90

실시간 (양방향) 통신을 위한 웹의 노력
개인적인 생각으로 HTML5의 새로운 스펙중에 사용자가 가장 흥미로워 한 것이 Canvas 라면

개발자가 가장 흥미로워 한 것은 바로 웹 소켓(Web Socket)이지 않나 싶다

필자 역시 순수 웹 환경에서 연결 지향 양방향 통신을 지원하는 웹 소켓이 가장 눈에 띄는 것 중 하나였다. 과거 순수 웹 환경에서 채팅과 같은 실시간 응용프로그램을 위한 얼마나 많은 시도들이 있었던가...

이제 박물관(?)에서나 볼 법한 숨긴 frame(or iframe) 을 통한 반복적인 재요청은 당시만 해도 웹에서 실시간 효과를 낼 수 있는 참신한 아이디어로 부상한 적이 있었다

이후 Ajax의 등장으로 비동기로 반복 요청을 할 수 있어 그나마 조금은 개선 되었다
그러나 여전히 클라이언트의 비 효율적인 재요청을 피할 수는 없었다

그리고 이후 Comet 의 등장으로 서버 데이터 수신 후 재 요청이 가능해져 불필요한 반복 요청의 비효율성은 개선되었다.

그러나 이 모든 것은 '폴링(polling)' 방식이다
즉 데이터 수신을 위해 서버가 클라이언트에게 전송해 주는 푸시(push)방식이 아니라 클라이언트가 서버에에게 요청하는 폴링(polling) 방식이었다

비교적 최적의 대안이었던 Comet 역시 무의미한 반복 요청을 피하기 위한 연결유지 기법이 적용되었지만 일정 시간 이후에는 연결을 종료하고 다시 연결해야 한다. 그래서 Comet을 Long-Polling 라 한다

참고: [HTML5] Server-Sent Events

웹의 진정한 실시간 (양방향) 통신, 웹 소켓(Web Socket)의 등장
초기 웹의 탄생 목적은 문서 전달과 하이퍼링크를 통한 문서 연결이었다
웹을 위한 HTTP 프로토콜은 이러한 목적에 매우 부합하는 모델이다
그러나 시대가 변하고 환경이 발전할 수록 웹이 더 이상 문서공유에만 집중할 수 없었다
갈수록 동적인 표현과 뛰어난 상호작용이 요구되었고 이로 인해 여러 새로운 기술이 탄생되었다

플래시(플렉스), 자바애플릿(자바FX), ActiveX , 실버라이트 등을 들 수 있다
하지만 이들은 웹에서 화려한 동작과 뛰어난 상호작용을 보장하지만 순수 웹 환경이 아니라 별도의 런타임을 플러그 인 형태로 브라우저에 설치해야 사용 가능하다

HTML5는 그 주요 목적 중 하나인, 플러그 인 없는 일관되고 표준화된 웹 응용 환경이라는 기치하에 많은 참신한 스펙들이 개발되었다.

그 중 순수 웹 환경에서 실시간 양방향 통신을 위한 스펙이 바로 '웹 소켓(Web Socket)' 이다
웹 소켓은 웹 서버와 웹 브라우저가 지속적으로 연결된 TCP 라인을 통해 실시간으로 데이터를 주고 받을 수 있도록 하는 HTML5의 새로운 사양이다. 따라서 웹 소켓을 이용하면 일반적인 TCP소켓과 같이 연결지향 양방향 전이중 통신이 가능하다

이와 같은 특징으로 웹에서도 채팅이나 게임, 실시간 주식 차트와 같은 실시간이 요구되는 응용프로그램의 개발을 한층 효과적으로 구현할 수 있게 되었다

웹 소켓과 Ajax 속도 비교
한 일본 사이트에서 웹 소켓과 (Ajax의 통신 객체인) XMLHttpRequest 의 속도 비교를 확인할 수 있는 페이지를 제공하고 있어 화제가 되고 있다
=> http://bloga.jp/ws/jq/wakachi/mecab/wakachi.html (크롬 or 사파리에서 실행)


서버에 존재하는 1,981(공백포함) 길이의 일본문자를 132개로 끊어서 가져오는 데모인데, 각각 웹 소켓과 여러개의 Ajax(병렬 Ajax)를 이용해 각각 호출해서 실행 속도를 비교했다

테스트 할 때마다 실행 속도의 차이가 조금씩 나지만 웹 소켓이 대략 50배 이상 좋은 성능을 보이고 있다


물론 이 사이트의 테스트 시나리오에 따르면, 한번에 모든 문자를 가져오는 것이 아니라 132개로 나눠진 단락들을 가져오는 것이다 보니 한번 연결된 통신 라인으로 데이터만 가져오는 웹 소켓에 비해 매번 헤더정보를 실어서 새로 요청 하는 Ajax가 더욱 느리게 느껴질 수 있다

하지만 이것은 '수 차례 송/수신을 하는 시나리오' 를 가정한 만큼 테스트 시나리오에 문제 제기를 할 필요는 없어 보인다. 웹 소켓을 사용하는 환경의 거의 대부분이 이런 시나리오이기 때문에 더욱 그렇다

어쨋던 확실히 실시간 통신환경에서는 Ajax 보다는 웹 소켓이 월등하다는 것을 알 수 있다

웹 소켓이 필요한 다섯가지 징후?
Peter Lubbers 라는 개발자가 'Five Signs You Need HTML5 WebSockets' 이라는 글을 포스팅 했다
HTML5의 웹 소켓을 이용하면 좋을 5가지 경우를 조목조목 설명하고 있다
뭐.. 결론은 웹 소켓 좋다는 거다 ^^ . 글에서 상단의 Summary 만 가져와 본다

  1. Your web application has data that must flow bi-directional simultaneously.
  2. Your web application must scale to large numbers of concurrent users.
  3. Your web application must extend TCP-based protocols to the browser.
  4. Your web application developers need an API that is easy to use.
  5. Your web application must extend SOA over the Web and in the Cloud.

대략 난잡번역 해 보면,
1. 실시간 양방향 데이터 통신이 필요한 경우
2. 많은 수의 동시 접속자를 수용해야 하는 경우
3. 브라우저에서 TCP 기반의 통신으로 확장해야 하는 경우
4. 개발자에게 사용하기 쉬운 API가 필요할 경우
5. 클라우드 환경이나 웹을 넘어 SOA 로 확장해야 하는 경우

그리고 각 경우에 대한 설명이 자세히 나열되어 있으니 스스로 번역해서 음미 바란다
(누가 좀 안해주나 ^^;)


지원되는 브라우저 현황
IE와 오페라를 제외한 사파리,크롬,파이어폭스 최신버전에서 웹 소켓을 지원한다


그림1. 브라우저별 Web Sockeet 지원 현황 (출처: http://caniuse.com/)


웹 소켓 구현하기
웹 소켓 역시 일반적인 TCP 소켓 통신처럼 웹 소켓 역시 서버와 클라이언트간 데이터 교환이 이루어지는 형태이다. 따라서 다른 HTML5 스펙과는 달리 클라이언트 코드만으로 실행 가능한 예를 만들 수는 없다

즉 클라이언트에서는 웹 소켓이 제공하는 자바스크립트 API를 이용해 서버에 연결하고 데이터를 송/수신하는 코드를 구현해야 하며 서버에서는 웹 소켓 프로토콜에 맞는 전용 장치가 구축되어 있어야 한다

웹 소켓 클라이언트
웹 소켓이 제공하는 클라이언트 측 API는 매우 심플하다. 기본적인 서버 연결, 데이터 송신, 데이터 수신만 정의하면 나머지는 일반적인 스크립트 로직일 뿐이다

: 서버연결

웹 소켓이 동작하기 위해서 제일 처음 서버와 연결이 되어야 한다. HTML5가 제공하는 WebSocket 객체를 통해 서버 연결을 수행한다. 일반 통신은 ws, 보안 통신은 wss 프로토콜을 이용한다
기본 포트 역시 http,https와 동일한 80,443을 이용한다
var wSocket = new WebSocket("ws://yourdomain/demo");

: 데이터 송신
서버와 연결이 되면 이제부터 데이터를 주고 받을 수 있게 된다. WebSocket 객체의 send 함수로 데이터를 서버로 송신할 수 있다
wSocket.send("송신 메시지");

: 데이터 수신
서버에서 푸시(전송)하는 데이터를 받으려면 message 이벤트를 구현하면 된다
wSocket.onmessage function(e){ //매개변수 e를 통해 수신된 데이터를 조회할 수 있다 

클라이언트 API는 이 세가지가 핵심이다. 추가로 아래와 같은 이벤트도 제공된다
- open 이벤트: 연결이 설정되면 발생
- close 이벤트: 연결이 끊어지면 발생

웹 소켓을 이용하는 클라이언트 코드의 전체 모습은 대략 다음과 같다

<script>
  var wSocket = new WebSocket("ws:yourdomain/demo");
 
  wSocket.onmessage = function(e){  alert(e.data);  }  

  wSocket.onopen = function(e){ alert("서버 연결 완료"); } 
  wSocket.onclose = function(e){ alert("서버 연결 종료"); }  

  function send(){ //서버로 데이터를 전송하는 메서드
    wSocket.send("Hello");
  }
</script>

보는 바와 같이, 소켓 통신을 위한 클라이언트 측 코드는 매우 심플하며 직관적이다. 하지만 말했듯이 클라이언트 코드 만으로 데모는 실행되지 않는다. 클라이언트와 통신하는 서버가 존재해야 하는데 아래에 계속 이어진다

웹 소켓 서버
웹 소켓은 일반적인 TCP 소켓과는 다른 프로토콜로 설계되었다. 따라서 기존 TCP 서버를 그대로 이용할 수 없고 새로 구현해야 하는데 웹 소켓 서버 사양에 맞도록 구현해야 한다.

웹 소켓 서버를 위한 다양한 오픈소스가 온라인에서 제공되고 있는데,
pywebsocket , phpwebsocket, jWebSocket, web-socket-ruby, Socket.IO-node 와 같은 모듈을 이용하면 웹 소켓 서버를 쉽게 구축할 수 있다

이 글에서는 jWebSocket을 이용해서 웹 소켓 서버를 구축하고 제공되는 데모를 실행해 보도록 하자

jWebSocket 라이브러리는 http://jwebsocket.org/
 사이트를 통해 다운로드 받을 수 있는데
자바로 구현된 웹 소켓 서버모듈인 jWebSocketServer와 자바스크립트로 구현된 웹 소켓 클라이언트 데모인 jWebSocketClient 을 다운받으면 된다. 참고로 jWebSocket 라이브러리의 전체 소스코드는 jWebSocketFullSource를 다운받아 볼 수 있다

jWebSocketServer 구동
우선 웹 소켓 서버를 구동 시켜야 하는데, 아파치 웹서버나 톰켓을 이용하여 구동하거나 Stand-Alone 으로도 구동시킬 수 있다. 이 글에서는 간단한 테스트를 위해 Stand-Alone로 구동시켜 보도록 하자

jWebSocketServer 가 자바로 구현되었기 때문에 자바 가상 머신이 설치되어 있어야 한다
서버에 최소 1.5 버전 이상의 자바런타임을 설치하도록 하자
그리고 기본적인 자바 환경 변수인 JAVA_HOME 과 java.exe를 Path에 등록한다

이제 자바를 위한 기본 설정이 마무리 되었으니 jWebSocketServer를 구동시키면 된다
Stand-Alone로 구동시키기 때문에 별도의 웹 서버는 필요하지 않고 배치파일을 실행하는 것으로 대신할 수 있다. 다운받는 jWebSocketServer을 압축해제 하고 bin 폴더에 있는 jWebSocketServer.bat 파일을 명령프롬프트에서 실행하도록 하자. 배치파일을 실행하면 설정을 위한 일련의 작업들이 진행되고 최종적으로 다음과 같은 모습으로 웹 소켓 서버의 구동이 시작된다


이후 웹 소켓 서버와의 통신로그와 디버그 메세지들이 이 창에 계속 기록된다
배치파일을 실행하는 동안 서버가 구동되는 것이기 때문에 실행창을 닫으면 서버도 종료됨을 기억하자

다음의 퀵스타트를 참고 하자
http://code.google.com/p/jwebsocket/wiki/QuickStart

jWebSocketClient 데모 테스트 하기
서버가 준비되었으니 웹 소켓 통신을 수행하는 클라이언트 데모를 확인해 보자
jWebSocketClient 를 다운받아서 압축해제 하면 다양한 데모가 미리 준비되어 있다
우리는 여기서 채팅데모인 chat.htm 을 실행해 보자

웹 소켓을 지원하는 브라우저를 두 개 띄우고 chat.html을 실행한다. 아래 그림은 크롬 브라우저의 실행 모습이다. 두 개의 브라우저에서 각각 로그인 한 뒤, 채팅하는 모습이다


채팅 외에도 다양한 데모가 있으니 참고 바라며 웹 소켓서버 통신의 기술적 구현을 배우기 위해 코드를 꼼꼼히 살펴보는 것도 좋을 것이다.

참고로 jWebSocket 에서 서버 연결을 위해 다음과 같은 url을 정의하고 있다
var lURL = jws.JWS_SERVER_URL + "/;prot=json,timeout=360000";
...
JWS_SERVER_URL: "ws://" + ( self.location.hostname ? self.location.hostname : "localhost" ) + ":8787"

ws 프토토콜로 localhost, 8787포트로 연결하며 추가로 json 포맷과 타임아웃이 설정되었다

마지막으로 WebSocket 은 다른 HTML5 사양보다 프로그램적인 성격이 강하다.
특히 클라이언트 코드만으로 해결될 수 있는 것이 아니라 좀 더 복잡해 보일 수 있다.
또한 많은 수의 클라이언트를 수용하기 위해서는 소켓 서버의 성능과 가용성이 확보되어야 하기 때문에 보다 신중한 접근이 필요하다 하겠다. 글에서 소개한 웹 소켓 서버 라이브러리를 적절히 이용하거나 오픈 소스를 분석해서 내공을 키우기 바란다

2016.07.15 추가>
- Node.js기반 socket.io 라이브러리를 이용해 간단한 웹 채팅을 구현한 다음 포시팅도 참고하기 바란다.
- [NodeJS] socket.io를 활용한 웹채팅 구현



참고>

Firefox의 웹소켓(WebSocket) 기능
http://code.google.com/p/websocket-sample/
http://dev.w3.org/html5/websockets/
http://www.websockets.org/about.html
http://www.codeproject.com/KB/webservices/c_sharp_web_socket_server.aspx