* 단방향성: 클라이언트의 요청에 대해서 응답만을 하는 방식
웹은 일반적으로 클라이언트에서 서버로 가는 단방향성이지만, 채팅과 같은 실시간 양방향 애플리케이나 쪽지와 같이 서버에서 클라이언트로 알림을 보내줘야 하는 요구 사항이 생겼다.
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기능 - 채팅 프로그램이기 때문에 클라이언트가 서버로 메세지를 보내면 서버에서 해당 메세지를 그대로 클라이언트에게 다시 보내준다.
'Node' 카테고리의 다른 글
[드림코딩 앨리 강좌] 노드로 배우는 백엔드 A-Z (0) | 2022.04.24 |
---|---|
[드림코딩 앨리 강좌] 타입스크립트 + OOP 마스터 과정 - Chap 4. 객체지향 프로그래밍 (0) | 2022.03.29 |
Passport 모듈로 회원가입 및 로그인 하기 - Passport 모듈화 적용 (0) | 2019.11.11 |
Passport 모듈로 회원가입 및 로그인 하기 (모듈화 적용 X) (1) | 2019.11.11 |
Node.js 에 View Template 적용하기 (1) - Semantic UI (미완) (0) | 2019.11.08 |