빠르게 훝어보는 node.js
#11 - Socket.IO (3/4)
조대협 (http://bcho.tistory.com)
앞서 작성한 대화방 http://bcho.tistory.com/896 에 이어서 이번에는 1:1 귓속말 대화가 가능한 기능을 추가해보자
귓속말이 가능한 대화방
이번에는 특정 사용자가 다른 사용자에게 귓속말을 보내는 기능을 가지는 대화방을 만들어보자. 이를 통해서 특정 클라이언트 소켓에 메세지를 어떻게 보내는지를 배울 수 있다.
이 대화방의 기능은 다음과 같다.
대화방에 입장하는 손님들은 자동으로 대화명을 부여 받는다.
사용자는 대화명을 바꿀 수 있다.
사용자는 대화 수신자의 대화명을 선택하여 특정 사용자에게 귓속말을 보낼 수 있다.
코드를 보면서 설명하도록 하자.
app.js
var express = require('express'); var routes = require('./routes'); var http = require('http'); var path = require('path');
var app = express(); app.use(express.static(path.join(__dirname, 'public')));
var httpServer =http.createServer(app).listen(3000, function(req,res){ console.log('Socket IO server has been started'); }); // upgrade http server to socket.io server var io = require('socket.io').listen(httpServer);
var socket_ids = []; var count = 0;
function registerUser(socket,nickname){ // socket_id와 nickname 테이블을 셋업 socket.get('nickname',function(err,pre_nick){ if(pre_nick != undefined ) delete socket_ids[pre_nick]; socket_ids[nickname] = socket.id socket.set('nickname',nickname,function(){ io.sockets.emit('userlist',{users:Object.keys(socket_ids)}); });
}); }
io.sockets.on('connection',function(socket){ socket.emit('new',{nickname:'GUEST-'+count}); registerUser(socket,'GUEST-'+count); count++;
socket.on('changename',function(data){ registerUser(socket,data.nickname); }); socket.on('disconnect',function(data){ socket.get('nickname',function(err,nickname){ if(nickname != undefined){ delete socket_ids[nickname]; io.sockets.emit('userlist',{users:Object.keys(socket_ids)});
}// if }); }); socket.on('send_msg',function(data){ socket.get('nickname',function(err,nickname){
data.msg = nickname + ' : '+data.msg; if(data.to =='ALL') socket.broadcast.emit('broadcast_msg',data); // 자신을 제외하고 다른 클라이언트에게 보냄 else{ socket_id = socket_ids[data.to]; if(socket_id != undefined){ io.sockets.socket(socket_id).emit('broadcast_msg',data); }// if } socket.emit('broadcast_msg',data); }); }); }); |
먼저 Express에서 socket.io를 사용할 준비를 하고
특정 사용자 즉 클라이언트에게만 메세지를 보내려면
io.sockets.socket(socket_id).emit
메세드를 사용해야 한다. 해당 클라이언트의 socket_id를 알아야 하며, 우리는 대화명(이하 nickname)을 통해서 특정 사용자에게 메세지를 보낼 것이기 때문에 nickname 에서 socket_id로의 맵핑 테이블이 필요하다.
var socket_ids = []
에서 socket_ids는 nickname to socket.id에 대한 맵핑 정보를 저장한다.
io.sockets.on('connection',function(socket){
socket.emit('new',{nickname:'GUEST-'+count});
registerUser(socket,'GUEST-'+count);
count++;
클라이언트가 접속되면 new 라는 이벤트를 통해서 nickname을 생성해서 보낸다. nickname은 사용자가 접속한 순서대로 GUEST-0,GUEST-1,… 식으로 순차적으로 이름을 배정한다.
다음으로, 새로운 사용자가 추가되었음을 알리고, 현재 사용자 리스트들을 보내야 하는데, 이를 registerUser 메서드에서 수행한다.
function registerUser(socket,nickname){
// socket_id와 nickname 테이블을 셋업
socket.get('nickname',function(err,pre_nick){
if(pre_nick != undefined ) delete socket_ids[pre_nick];
socket_ids[nickname] = socket.id
socket.set('nickname',nickname,function(){
io.sockets.emit('userlist',{users:Object.keys(socket_ids)});
});
});
}
먼저 register user에서는 socket.get을 이용하여 현재의 클라이언트 소켓의 nickname을 pre_nick이라는 변수로 읽어온다. 대화명이 바뀔 경우 기존에 socket_ids에 기존의 대화명으로 저장되어 있는 socket.id를 삭제하기 위함이다. 기존의 데이타를 삭제 하였으면, socket_ids에 nickname을 Key 값으로 하여, socket.id를 저장한다. 다음. nickname을 해당 socket에 set명령을 이용해서 저장한후에, userlist라는 이벤트를 통해서, 현재 접속된 사용자의 nickname들을 보낸다. 이 nickname들은 socket_ids의 property의 키들로 저장이 되었기 때문에, Object.keys(socket_ids)를 이용하여 nickname 리스트를 추출할 수 있다.
마찬가지로, 대화명이 변경되었을 때에도 registerUser 함수를 이용하여 전체 사용자의 리스트를 업데이트 하여 클라이언트에게 보내준다.
socket.on('changename',function(data){
registerUser(socket,data.nickname);
});
만약에 해당 클라이언트가 브라우져를 닫았을 경우에는 대화방을 떠난 것으로 간주하여, ‘disconnect’이벤트에 대해서,
socket.on('disconnect',function(data){
socket.get('nickname',function(err,nickname){
if(nickname != undefined){
delete socket_ids[nickname];
현재 socket의 nickname을 읽어서 socket_ids에서 해당 nickname에 해당 하는 데이타를 삭제한 후에,
io.sockets.emit('userlist',{users:Object.keys(socket_ids)});
를 이용하여, 업데이트된 사용자 목록을 다시 클라이언트들이 업데이트 하도록 이벤트를 보낸다.
마지막으로 클라이언트로 부터 대화메세지를 다른 사용자에게 대화 메세지를 보내는 “send_msg” 이벤트가 들어왔을때
socket.on('send_msg',function(data){
socket.get('nickname',function(err,nickname){
data.msg = nickname + ' : '+data.msg;
에서 처리하는데, 현재 클라이언트의 nickname을 socket.get을 이용해서 읽어와서 보낼 메세지문자열을 “대화명”+”대화내용”으로 만들어서 저장한다.
만약에 전체 메세지인 경우 data.to 가 ‘ALL’로 들어오는데, 이경우 broadcast를 하고
if(data.to =='ALL') socket.broadcast.emit('broadcast_msg',data);
특정 nickname이 data.to에 들어오는 경우 귓속말로 간주하여, nickname을 이용해서 socket_ids로 부터 해당 nickname을 사용하는 클라이언트의 socket.id를 가져온후
socket_id = socket_ids[data.to];
if(socket_id != undefined){
다음으로, 해당 socket_id로 메세지를 보낸다.
io.sockets.socket(socket_id).emit('broadcast_msg',data);
다음은 위의 서버를 사용하기 위한 HTML 클라이언트 폼이다.
index.html
<html> <head>
<title></title> <script src="/socket.io/socket.io.js"></script> <script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
</head> <body> <b>Send message</b><p> Name <input type="text" id="nickname" /> <input type="button" id="changename" value="Change name"/><br> To <select id="to"> <option value="ALL">ALL</option> </select> Message <input type="text" id="msgbox"/> <br> <span id="msgs"></span>
<script type="text/javascript"> var socket = io.connect('http://localhost'); $('#changename').click(function(){ socket.emit('changename',{nickname:$('#nickname').val()}); }); $("#msgbox").keyup(function(event) { if (event.which == 13) { socket.emit('send_msg',{to:$('#to').val(),msg:$('#msgbox').val()}); $('#msgbox').val(''); } }); socket.on('new',function(data){ console.log(data.nickname); $('#nickname').val(data.nickname); });
// 새로운 사용자가 들어오거나, 사용자가 이름을 바꿨을때 "To" 리스트를 변경함 socket.on('userlist',function(data){ var users = data.users; console.log(users); console.log(data.users.length); $('#to').empty().append('<option value="ALL">ALL</option>'); for(var i=0;i<data.users.length;i++){ $('#to').append('<option value="'+users[i]+'">'+users[i]+"</option>"); } });
socket.on('broadcast_msg',function(data){ console.log(data.msg); $('#msgs').append(data.msg+'<BR>'); }); </script> </body> </html> |
앞에서 작성한 채팅 프로그램과 크게 다를바는 없으나, new 이벤트와 userlist 이벤트 핸들러가 추가되었다.
‘new’ 이벤트는 채팅방에 들어왔을때, 서버로 부터 대화명과 함께 보내지며, 클라이언트에서는 이 대화명을 받아서, “대화명 창”부분에 세팅 한다.
socket.on('new',function(data){
console.log(data.nickname);
$('#nickname').val(data.nickname);
});
‘userlist’ 이벤트는 현재 대화방에 있는 사용자들의 nickname을 모두 받은 후에, 대화 상대를 지정하는 HTML <select> box 부분에 대화명들을 넣어준다.
socket.on('userlist',function(data){
var users = data.users;
console.log(users);
console.log(data.users.length);
$('#to').empty().append('<option value="ALL">ALL</option>');
for(var i=0;i<data.users.length;i++){
$('#to').append('<option value="'+users[i]+'">'+users[i]+"</option>");
}
});
다음은 위의 예제를 실행한 화면이다. 아래와 같이 TO 부분에서 사용자를 지정하고 메세지를 보내면 특정 사용자에게만 메세지가 전달됨을 확인할 수 있다.
다음에는 대화방의 기능을 추가하여, socket.io의 room 개념에 대해서 알아보도록 한다.
'클라우드 컴퓨팅 & NoSQL > Vert.x & Node.js' 카테고리의 다른 글
빠르게 훝어 보는 node.js - #13 Socket.IO 클러스터링 (1) | 2014.05.06 |
---|---|
빠르게 훝어 보는 node.js - #12 Socket.IO 4/4 - 채팅방 기능 추가하기 (4) | 2014.04.24 |
빠르게 훝어 보는 node.js - #10 Socket.IO 2/4 API 요약 (9) | 2014.04.24 |
빠르게 훝어 보는 node.js - #9 Socket.IO 1/4 - socket.io 기본 및 채팅 만들기 (16) | 2014.04.22 |
무료 node.js 개발툴. (0) | 2014.04.21 |