본문 바로가기

Node

Node.js - Socket.IO 모듈을 이용한 채팅 프로그램

* 단방향성: 클라이언트의 요청에 대해서 응답만을 하는 방식

 

웹은 일반적으로 클라이언트에서 서버로 가는 단방향성이지만, 채팅과 같은 실시간 양방향 애플리케이나 쪽지와 같이 서버에서 클라이언트로 알림을 보내줘야 하는 요구 사항이 생겼다.

Socket.IO 자바스크립트 모듈로,  클라이언트로의 푸쉬(알림)를 지원하는 모듈이다. (양방향성이 가능해짐)

WebSocket 지원하지 않는 어느 브라우져라도 푸쉬 메시지를 일관된 모듈로 보낼  있는 장점이 있다

이번 예제 point

- 특정 클라이언트 소켓에 메세지를 어떻게 보내는지를 알 수 있다

서버쪽 코드 

- app.js

 

socketIO를 지원하는 서버 생성

// httpServer 생성
var server = http.createServer(app).listen(app.get("port"),function(){
	console.log("서버를 시작했습니다");
	database.init(app,config); 
});

// httpServer를 socketIO를 지원하는 서버로 업그레이드를 시킴
var io = socketio.listen(server); // 클라이언트가 보내온 요청을 처리하기 위해 socketio의 listen() 메소드를 호출
console.log("socket.io 요청을 받아들일 준비가 됨"); // 채팅 서버할 준비가 됐다는 뜻

 

② 사용자ID와 socket_id 매핑테이블 선언 

특정 사용자에게만 메세지를 보내려면 io.sockets.socket(socket_id).emit 메소드를 사용해야 한다. 

해당 클라이언트의 socket_id를 알아야 하며, 해당 클라이언트의 loginID를(socket_id말고) 통해서 특정 사용자에게 메세지를 보낼 것이기 때문에 loginID 에서 socket_id로의 맵핑 테이블이 필요하다.

var loginID = {};

 

③ 클라이언트가 socket.io 채널로 접속이 되었을때에 대한 이벤트를 정의

io.sockets.on('connection',function(socket){

같이 클라이언트가 접속이 되면, callback 수행하는데 이때, 연결된 클라이언트의 socket 객체를 같이 넘긴다

이 socket 객체 안에는 접속한 클라이언트의 ip,port번호가 담겨있다

io.sockets.on("connection",function(socket){
	
	console.log("connection info :",
			socket.request.connection._peername);
	
	socket.remoteAddress =
		socket.request.connection._peername.address; //ip
	
	socket.remotePort =
		socket.request.connection._peername.port; // port
	
	// 클라이언트로부터 login 이벤트를 받았을 때 처리 
	socket.on("login",function(login){
		
		console.log("서버가 login 이벤트를 받았습니다"); 
		console.log("접속한 소켓 id: " + socket.id); 
		// QR_qVIARIFJIEJF -> 이런 복잡한 숫자값이 나옴
				
		//매핑정보를 담는 loginID에 login.id를 Key 값으로 하여, socket.id를 저장
		loginID[login.id] = socket.id; 
		
		// 클라이언트에서 받은 데이터를 socket 객체에 속성으로 추가 -> 내가 고유값을 직접 설정하는 과정
		socket.loginID = login.id;
		socket.loginAlias = login.alias;
		
		console.log("접속한 클라이언트 id 갯수: " + 
				Object.keys(loginID).length); 
		// 이 login.id들은 loginID의 property의 key들로 저장이 되었기 때문에,
		// Object.keys(loginID)를 이용하여 key들의 갯수를 추출할 수 있다		
				
		// 응답 메세지 전송 
		sendResponse(socket,"login","200",
				socket.loginID + "(" + socket.loginAlias + ")가 로그인 되었습니다");
		
	});
	
	// 클라이언트로부터 logout 이벤트를 받았을 때 처리 
	socket.on("logout",function(logout){
		
		// 그 사이에 socket이 죽었을 수도 있으니깐 체크
		if (socket == undefined) { 
			alert("서버가 연결되어 있지 않습니다");
			return;
		}			
		
		sendResponse(socket,"logout","444",logout.id + "가 로그아웃 되었습니다");
		
		delete loginID[logout.id];
		// loginID에 담긴 key값중 로그아웃한 사용자id 에 해당 하는 데이터를 삭제
		// loginID의 key값에는 현재 로그인중인 사용자id들이 담겨 있음
		
	});	
	
	// 클라이언트로부터 message 이벤트를 받았을 때 처리
	socket.on("message",function(message){
		
		console.log("서버가 message를 받았습니다"); 
		console.dir(message); // 받은 msg를 찍어보기
		
		//나를 포함한 모든 클라이언트에게 메세지를 전달
		if(message.receiver == "All") { 
			// emit 메소드를 이용하여 이벤트를 전송하는 작업
			// "message"라는 이벤트 명으로 message를 전송시킴
			io.sockets.emit("message",message);
			
		} else { 
			
			// 클라이언트가 요구한 특정 대상자에게만 메세지 전달
			if(loginID[message.receiver]) {
				
				// 위와의 차이점은 연결된(connected) 사용자한테만 보낸다는 코드가 들어가있음
				io.sockets
				.connected[loginID[message.receiver]]
				.emit("message",message);
				
				sendResponse(socket,"message","200",
						message.receiver + "에게 메세지를 전송했습니다");
							
			} else { // 보내려는 대상이 로그인 하지 않은 상태일 경우				
				sendResponse(socket,"message","404",
						"상대방의 로그인 ID를 찾을 수 없습니다");				
			}
		}
		
	});
	
});

 

채팅창에서 글을 쓰고 sendButton 을 누르면 서버로 “message” 라는 이벤트를 보내도록 작성해놓았다.

그러면 서버쪽에서는 다음과 같이 socket.on("message", 라는 메소드를 이용하여 해당 이벤트에 따른 처리 한다. 

이때 들어오는 데이터는 채팅 문자열이 {key:value} 형식으로 message라는 변수를 통해서 아래와 같이 들어오는데,

 

들어오자마자 이 변수 message를 message라는 이벤트 명으로 다른 클라이언트들과 자신에게 다시 보낸다.

socket.emit("message",message);

* socket.emit :  자신의 클라이언트(웹)에게 이벤트를 보내는 메소드

 

 

응답 메세지 전송 메소드 (따로 빼놓음)

function sendResponse(socket,command,code,message) {
	
	var statusObj = {command:command, code:code, 
			message:message};
	
	socket.emit("response",statusObj);
	
}

클라이언트(웹)쪽 코드

 

socket.io를 사용하기 위해 lib 위치 선언

<script type = "text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.dev.js"></script>

 

 socket.io 서버로 연결

	function connectToServer() {

		var options = {
			"forceNew" : true
		}; // 강제로 세션을 연결해라 (connect 하나 만들라)
		var url = "http://" + host + ":" + port;

		socket = io.connect(url, options);

		//연결이 되면, connect라는 이벤트가 만들어짐
		socket.on("connect", function() {
			print("웹 소켓이 서버에 연결 되었습니다:" + url);

			//서버로부터 'message'라는 이벤트가 들어오면, (on 메소드 이용)
			socket.on("message", function(message) {

				print("<p>수신 메세지: "
              			 + message.sender + ", "  // 보내는 사람
               			 + message.receiver + "," // 받는 사람
               			 + message.command + ","  // 데이터 종류 구분
              			 + message.data + "</p>"); // 데이터 내용
			});

			socket.on("response", function(response) {

				print("응답 메세지를 받았습니다: " + response.command + ", "
						+ response.code + ", " + response.message);

				if (response.code == 444) {
					socket.close();
				}

			});

		});

		socket.on("disconnect", function() {
			print("웹 소켓이 연결이 종료 되었습니다:");
		});

	}

서버로부터 들어온 문자열을 <div> 영역에 append 한다

	function print(data) {

		$("#result").append("<p>" + data + "</p>");
	}

 

서버로 이벤트 전송하기

 

sendButton을 눌렀을 경우 - "message" 이벤트를 서버에 전송

loginButton을 눌렀을 경우 - "login" 이벤트를 서버에 전송

logoutButton을 눌렀을 경우 - "logout" 이벤트를 서버에 전송

(기본 로직은 다 똑같음)

$(function(){

		$("#sendButton").bind("click",function(event){
			
			var sender = $("#senderId").val();
			var receiver = $("#receiverId").val();
			var msg = $("#msg").val();
			
			var output = {sender:sender, receiver:receiver
			,command:"chat", type:"text", data:msg};
			
			if(socket==undefined) { // undefined : 아예 반환값이 없는 경우
				alert("서버가 연결되어 있지 않습니다");
				return;		
			}
			
			// 서버에 "message" 이벤트를 보냄
			socket.emit("message",output); 
			
		});	
        
        // 로그인 버튼 클릭
		$("#loginButton").bind("click", function(event) {
			
			var id = $("#id").val();
			var pwd = $("#pwd").val();
			var alias = $("#alias").val();
			var today = $("#today").val();
			
			// 로그인한 아이디를 
			// 보내는 이의 input 란에 넣어주면 됨 
			$("#senderId").val(id);
			
			// 서버로 보낼 데이터
			var output = {id:id, pwd:pwd, alias:alias, today:today};
			
			// 보내기 전에 체크
			if (socket == undefined) { 
				alert("서버가 연결되어 있지 않습니다");
				return;
			}	
			
			socket.emit("login", output);
		});
		
		
		$("#logoutButton").bind("click", function(event) {
			
			if (socket == undefined) { 
				alert("서버가 연결되어 있지 않습니다2");
				return;
			}	
			
			var id = $("#id").val();
			var output = {id:id};
			socket.emit("logout",output);
			
			// 초기화
			$("#id").val();
			$("#pwd").val();
			$("#alias").val();
			$("#today").val();
					
		});
        
	});

 

echo기능 - 채팅 프로그램이기 때문에 클라이언트가 서버로 메세지를 보내면 서버에서 해당 메세지를 그대로 클라이언트에게 다시 보내준다.