본문 바로가기

Back-End/Node.js

Socket.IO

반응형

웹소켓

웹소켓은 HTML5 표준 기술로, 사용자의 브라우저와 서버 사이의 동적인 양방향 연결 채널을 구성한다. Websocket API를 통해 서버로 메세지를 보내고, 요청 없이 응답을 받아오는 것이 가능하다. 현재 API는 W3C에서 관장하고 있으며 프로토콜은 IETF에서 관리하고 있다.

웹소켓은 별도의 포트를 사용하지 않고 HTTP와 같은 80번 포트를 사용하는데, 이로 인해 클라이언트인 웹 브라우저뿐만 아니라 웹 서버도 기능을 지원하고 있어야 한다.

 

웹소켓의 사용 이유

초창기 WEB1.0은 인터넷에 접속한 사용자에게 콘텐츠를 전달하는 역할만 했다. 하지만 *Ajax와 같은 기술이 나타나면서  서버와 클라이언트 간의 상호작용을 하는 웹 서비스가 등장했다. CERN과 같이 초창기의 연구기관에서 사용했던 것과 달리 점차 웹은 일상생활 깊숙히 파고들어, 네이티브 애플리케이션을 대체할 수 있는 수준에 이르렀다.

*Ajax : Asynchronous Javascript And Xml(비동기식 자바스크립트와 xml). Javasciprt의 라이브러리로, Javascript를 사용한 비동기 통신, 클라이언트와 서버간에 xml 데이터를 주고 받는 기술.

특히 전형적인 브라우저 렌더링 방식은 HTTP 요청에 대해 HTTP 응답을 받아 브라우저의 화면을 모두 지우고 받은 내용을 새로 표시하는 방식이었다. 하지만 이는 브라우저가 HTTP 요청을 보내고 웹 서버가 이 요청에 대한 HTTP 응답을 보내는 단방향 메시지 교환 방식이다.

이런 불편함과 사용자가 긴밀히 상호작용하는 웹 페이지를 더 쉽게 만들고자 개발된 것이 바로 브라우저와 웹 서버 사이의 자유로운 양방향 메시지 송수신 방법인 HTML5 표준안의 일부인 웹 소켓 API이다. 하지만 웹소켓 프로토콜은 아직 확정된 완성본이 아닌 버전으로 계속 나오고 있다. 따라서 아직 브라우저별로 지원하는 웹소켓 버전도 다르다.

 

서버/클라이언트 통신

웹소켓을 이용하면 실시간 웹 애플리케이션과 이를 위한 웹 서버 간의 관계가 더 추상화된다.

 Client와 서버가 연결된 후부터 HTTP 요청/응답과는 상관없이 서버와 양방향 통신이 가능하다.

 

웹소켓 지원 브라우저

크롬, 사파리, 파이어폭스, 오페라, 그리고 각종 모바일 브라우저에서도 웹소켓을 사용할 수 있다.

Socket.IO

노드의 장점 중 하나인 실시간 웹 애플리케이션 개발에 대해 알아보자.

Socket.IO는 거의 모든 웹 브라우저와 모바일 장치를 지원하는 실시간 웹 애플리케이션 지원 라이브러리다. 자바스크립트로 구현되어 있고, 현존하는 대부분 실시간 웹 기술들을 추상화해놨다. 즉, Socket.IO는 자바스크립트를 이용하여 브라우저 종류에 상관없이 실시간 웹을 구현할 수 있도록 한 기술이다.

그러나 웹소켓은 HTML5의 기술이기 때문에 오래된 버전의 웹 브라우저는 웹소켓을 지원하지 않습니다. 특히 자동 업데이트가 되지 않는 익스플로러 구 버전 사용자들은 웹소켓으로 작성된 웹페이지를 볼 수 없지요. 따라서 이를 해결하기 위해 나온 여러 기술 중 하나가 Socket.io이다.

Socket.IO는 웹 브라우저와 웹 서버의 종류와 버전을 파악하고 가장 적합한 기술을 선택해 사용한다. 만약 브라우저에 FlashSocket이라는 기술을 지원하는 플러그인이 설치되어 있다면 이를 사용하고, 플러그인이 없다면 AJAX Long Polling 방식을 사용한다. 즉, 웹페이지가 열리는 브라우저가 웹소켓을 지원하면 웹소켓 방식으로 동작하고, 지원하지 않는 브라우저라면 일반 http를 이용해서 실시간 통신을 흉내낸다.

 

Socket.IO 설치

Socket.IO도 웹소켓과 마찬가지로 브라우저에서 자바스크립트를 사용한다. 현재 공식적으로는 노드만 지원한다.

npm install socket.io -g

 

 

이벤트 주고받기

Socket.IO는 비동기 통신을 위한 라이브러리이므로 이벤트 기반의 처리에 의존하고 있다.

서버든 클라이언트든 socket 객체를 이용하여 이벤트 발생시에 어떤 작업을 수행할지에 대해 .on을 통해 기술할 수 있다. 클라이언트가 서버에게, 서버가 클라이언트에게 메시지를 전달하고자 한다면 .emit()을 기술할 수 있다.

//Event 발생 시 수행할 내용, 즉 메시지 수신
socket.on("이벤트 이름", function() {
    //callback function
});

//Event 발생, 즉 메시지 송신
socket.emit("이벤트 이름", [보낼 메시지/데이터]);

 

이해를 돕기 위해 아래 서버 스크립트를 작성해보았다.

서버코드: server.js

//server.js

var app = require('http').createServer(handler),
    io = require('socket.io').listen(app),
    fs = require('fs');

app.listen(3000);

function handler (req, res) {
	fs.readFile('index.html', function (err, data) {
		if (err) {
			res.writeHead(500);
			return res.end('Error loading index.html');
		}
		res.writeHead(200);
		res.end(data);
	});
}

io.on('connection', function (socket) {  // 1
	socket.emit('news', { serverData : "서버 작동" });
	
	socket.on('client login', function (data) {  // 2
		console.log(data);
	});
		
	socket.on('disconnect', function(){  // 3
		console.log('접속이 종료되었습니다.');
	});

});
  • //1번 전까지의 코드는 접속했을 때 index.html 페이지를 불러오고, 에러가 났을 때 처리해준다.
  • //1의 connectionsocket.io의 기본 이벤트로, 사용자가 웹페이지를 열면 자동으로 발생하는 이벤트다. 이때 이벤트 안의 함수에는 접속한 사용자의 socket이 파라미터로 전달되는데, 접속한 각 클라이언트에 관련한 이벤트들을 작성하려면 이 connection 리스너 함수안에서 socket을 사용하면 된다.
  • connection안의 각 이벤트를 작성할 때는 socket.on('event 이름', 함수) 형식으로 작성한다. 함수 대신 전달하고 싶은 값이 있다면 변수를 넣어줘도 된다.
  • socket.emit 은 event를 발생시키는 함수다. 이렇게 서버쪽에서 이벤트를 발생시키면 클라이언트 페이지의 해당 이벤트 리스너에서 처리되게 된다. 위 코드는 emit를 이용해 "news" 이벤트를 발생시켰다. socket.emit를 이용하면 해당 socket을 통해 상대편으로 전달하고, io.emit을 이용하면 서버가 현재 접속해있는 모든 클라이언트에게 이벤트를 전달한다. 이 코드에서는 사용자가 맨 처음 접속했을 때 해당 socket에만 news 이벤트가 발생하게 되고, serverData라는 변수를 문자열에 넣어 전달해주었다.
  • //2의 client login라는 이벤트를 열어 콘솔 창에 전달받은 data를 찍어주었다.
  • //3의 disconnect도 connection처럼 socket.io의 기본이벤트인데, 사용자의 접속이 끊어지면 자동으로 발생한다. 단, disconnect 이벤트는 개별 클라이언트가 접속이 끊어졌을 때 발생하는 이벤트이므로 io.on이 아닌 socket.on으로 작성해주어야 한다.

index.html

<!-- index.html -->

<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost:3000');
//구름ide 이용자는 localhost가 아닌 자신의 url을 입력. ex) http://test-1.run.goorm.io/ 

socket.on('news', function (data) {
	console.log(data);
	socket.emit('client login', { clientData : '클라이언트 접속' });
});
	   
</script>

여기서 이벤트 처리는 news 이벤트 딱 하나 뿐이다. news 이벤트가 발생하면 클라이언트 페이지에서는 콘솔 창에 전달받은 data를 출력해준다. 여기서 주의할 점은, 서버의 콘솔창에 찍히는 것이 아닌, 클라이언트 페이지의 콘솔 창에 찍힌다. 따라서 웹 페이지의 개발자 도구를 클릭하면 출력된 값을 볼 수 있다.

 

결과

사용자가 페이지에 접속하면 서버로부터 'news' 이벤트를 불러온다. 그래서 {serverData: "서버작동"} 데이터를 받아오고, 이를 클라이언트 페이지의 콘솔 창에 띄우게 된다.

이어서 client login 이벤트를 발생시키면서 clientData라는 변수에 문자열('클라이언트 접속')을 넣어 전달해준다. 따라서 이 clientData가 server.js의 client login 이벤트로 전달된 후, 콘솔 창에 찍히게 된다.

url 버튼을 클릭해 서버로 접속하면 { cilentData : '클라이언트 접속'} 메시지가 콘솔 창에 출력되고, 웹 창을 끄면 접속이 종료되었습니다. 메시지가 출력된다.

 

 

 

메시지 브로드캐스팅

자신을 제외한 다수의 클라이언트에게 같은 메시지를 보내고자할 때 사용한다.

아래 코드는 다른 클라이언트들에게 브로드캐스팅하는 소스 코드이다. 단순히 .emit()앞에 broadcast라는 플래그만 추가해주면 된다.

Message Broadcasting

var io = require('socket.io').listen(80);

io.sockets.on('connection', function(socket){
    socket.broadcast.emit('user connected');
});

 

express와 Socket.IO 함께 사용하기

Socket.IO는 우리가 앞서 살펴본 express와 쉽게 연동하여 사용할 수 있다. 아래는 express와 연동한 Socket.IO의 기본적인 소스 코드다.

서버측: app.js

var app = require('express')()
    , server = require('http').createServer(app)
    , io = require('socket.io').listen(server);

server.listen(80); //Web Socket은 80번 포트 사용

app.get('/',function(req, res){
    res.sendfile(__dirname + '/index.html');
});

io.sockets.on('connection', function(socket){
    socket.emit('news', {hello: 'world'});
    socket.on('my other event', function(data){
        console.log(data);
    });
});

클라이언트 측: index.html

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('http://localhost');
    socket.on('news',function(data){
        console.log(data);
        socket.emit('my other event', {my:'data'});
    });
</script>

express를 이용하여 server.js는 이전 코드에 비해 간결하고 효율적으로 바뀌었다. 또한 express에서 제공하는 API를 사용하면서 Socket.IO를 이용하여 실시간 웹과 관련된 기능을 추가할 수 있었다. 반면 클라이언트 측 코드는 변화없이 동일하다.

 

 

 

반응형