Socket.io 채팅 애플리케이션 (5) - 채팅 기능 구현 (채팅방에 들어가보자 - 서버)
지난 시간에
지난 시간에는 채팅 창 ui 를 꾸며 보았다. 이제 채팅 관련 기능을 구현해야하는데 분량이 많으므로 5차시에 걸쳐 구현하도록 하겠다.
- 채팅방에 들어가보자 (server / client)
- 들어갔으니 채팅을 치자 (server / client)
- 거의 다했으니 마무리를 하자
이번 차시에서는 채팅방에 들어가보자.
서버측 구현
채팅 방에 들어가는 플로우(흐름) 은 아주 간단하다. 이해하기 쉽게 클라이언트측 동작은 앞에 (c)
, 서버측 동작은 앞에 (s)
를 붙히겠다.
- (c) 채팅방 목록중에 아무거나 클릭하면
join room
이벤트 발생 - (s)
join room
이벤트 발생시 기존에 있던 방에서 나가고 새로운 방에 입장socket.join()
- (s) 이제 방에 새로운 멤버가 들어왔으니 member list 업데이트 이벤트 발생
userlist
- (c)
userlist
이벤트 발생시memberWrap
에 있는 데이터를 서버한테 받은 데이터로 갱신 - (s) 유저가
disconnect
할때나 로그아웃할 때도userlist
update 이벤트 발생 - (c) 유저가 들어오거나 떠날 때 공지해주기
우선 서버측 부터 구현할 것 이므로 2, 3, 5 를 다같이 구현해보자.
2. (s) join room
이벤트 발생시 기존에 있던 방에서 나가고 새로운 방에 입장 socket.join()
# /app.js
io.sockets.on('connection', function(socket){
----------
socket.on("login user", function (data, cb) {
if (loginCheck(data)) {
onlineUsers[data.id] = {roomId: 1, socketId: socket.id};
socket.join('room1');
cb({result: true, data: "로그인에 성공하였습니다."});
updateUserList(0, 1, data.id); // 인자 설명은 3번에서...
} else {
cb({result: false, data: "등록된 회원이 없습니다. 회원가입을 진행해 주세요."});
return false;
}
});
socket.on('join room', function (data) {
let id = getUserBySocketId(socket.id);
let prevRoomId = onlineUsers[id].roomId;
let nextRoomId = data.roomId;
socket.leave('room' + prevRoomId);
socket.join('room' + nextRoomId);
onlineUsers[id].roomId = data.roomId;
updateUserList(prevRoomId, nextRoomId, id);
});
});
사용자가 로그인을 했을때 default
로 입장되는 방은 Everyone
이고 이방의 roomId
는 1 이므로 로그인 할때 socket.join('room1');
을 이용해 Everyone
방에 입장하였다.
그리고 join room
이벤트가 발생하면 기존에 있던 방을 떠나고 새로운 방에 join
한다. 그리고 onlineUsers
에 있는 roomId
를 갱신해준다.
updateUserList();
함수는 3번 에서 구현할 것이다.
지금까지 작성한 코드중에 이해되지 않은 코드가 있으면 댓글로 작성해주세요......
3. (s) 이제 방에 새로운 멤버가 들어왔으니 member list 업데이트 이벤트 발생 userlist
# /app.js
io.sockets.on('connection', function(socket){
----------
function updateUserList(prev, next, id) {
if (prev !== 0) {
io.sockets.in('room' + prev).emit("userlist", getUsersByRoomId(prev));
io.sockets.in('room' + prev).emit("lefted room", id);
console.log("prev"+ prev);
}
if (next !== 0) {
io.sockets.in('room' + next).emit("userlist", getUsersByRoomId(next));
io.sockets.in('room' + next).emit("joined room", id);
console.log("next"+ next);
}
}
function getUsersByRoomId(roomId) {
let userstemp = [];
Object.keys(onlineUsers).forEach((el) => {
if (onlineUsers[el].roomId === roomId) {
userstemp.push({
socketId: onlineUsers[el].socketId,
name: el
});
}
});
return userstemp;
}
}
자신이 떠난 방과 자신이 새로 들어온 방에만 이벤트를 보내 userlist 를 업데이트하는 코드이다.
값이 0인것을 검사한 이유는 로그인이나, 로그아웃, disconnect
와 같은 경우는 떠난 방, 들어온 방중 하나가 없다. 그래서 그 값을 0으로 처리해서 이 코드에서 검사를 해야 한다.
in({지정한 방이름}).emit("이벤트명", "보낼 데이터")
지정한 방에 연결된 socket 들에게만 이벤트가 발생한다. 위에서는 userlist
라는 이벤트를 각 방에 보내고 있다. 데이터는 getUsersByRoomId
라는 함수를 이용해 현재 접속중인 user
들중 roomId
가 일치하는 사람들의 배열을 보내고 있다.
Object.keys
는 해당 Object
의 키값을 배열로 만드는 것이고 foreach
를 통해 roomId
검증을 진행하고 있다.
5. (s) 유저가 disconnect
할때나 로그아웃할 때도 userlist
update 이벤트 발생
# /app.js
socket.on('logout', function () {
if (!socket.id) return;
let id = getUserBySocketId(socket.id);
let roomId = onlineUsers[id].roomId;
delete onlineUsers[getUserBySocketId(socket.id)];
updateUserList(roomId, 0, id);
});
socket.on('disconnect', function () {
if (!socket.id) return;
let id = getUserBySocketId(socket.id);
if(id === undefined || id === null){
return;
}
let roomId = onlineUsers[id].roomId || 0;
delete onlineUsers[getUserBySocketId(socket.id)];
updateUserList(roomId, 0, id);
});
disconnect
와 logout
에도 updateUserList()
를 호출하게 한다. 조금 특이 한점은 disconnect
에서 id 를 검증한다. logout
같은 경우는 로그인을 해야만 할 수 있는 것이여서 onlineUsers
를 따로 검사하지 않아도 되지만 disconnect
는 로그인을 하지낳고 나가는 경우 때문에 onlineUsers
의 유효성 검사를 했다.
이번에 작성한 전체 코드
# /app.js
var express = require('express'),
port = process.env.PORT || 3000,
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
users = {
'test': {
id: 'test',
pw: 'test'
}
},
onlineUsers = {};
app.use(express.static('public'));
app.get('/', function (req, res) {
res.redirect('/chat');
});
app.get('/chat', function (req, res) {
res.sendfile(__dirname + '/chat.html');
});
server.listen(port, () => {
console.log(`server open ${port}`);
});
io.sockets.on('connection', function (socket) {
socket.on("join user", function (data, cb) {
if (joinCheck(data)) {
cb({
result: false,
data: "이미 존재하는 회원입니다."
});
return false;
} else {
users[data.id] = {
id: data.id,
pw: data.pw
};
cb({
result: true,
data: "회원가입에 성공하였습니다."
});
}
});
socket.on("login user", function (data, cb) {
if (loginCheck(data)) {
onlineUsers[data.id] = {
roomId: 1,
socketId: socket.id
};
socket.join('room1');
cb({
result: true,
data: "로그인에 성공하였습니다."
});
updateUserList(0, 1, data.id);
} else {
cb({
result: false,
data: "등록된 회원이 없습니다. 회원가입을 진행해 주세요."
});
return false;
}
});
socket.on('logout', function () {
if (!socket.id) return;
let id = getUserBySocketId(socket.id);
let roomId = onlineUsers[id].roomId;
delete onlineUsers[getUserBySocketId(socket.id)];
updateUserList(roomId, 0, id);
});
socket.on('disconnect', function () {
if (!socket.id) return;
let id = getUserBySocketId(socket.id);
if(id === undefined || id === null){
return;
}
let roomId = onlineUsers[id].roomId || 0;
delete onlineUsers[getUserBySocketId(socket.id)];
updateUserList(roomId, 0, id);
});
socket.on('join room', function (data) {
let id = getUserBySocketId(socket.id);
let prevRoomId = onlineUsers[id].roomId;
let nextRoomId = data.roomId;
socket.leave('room' + prevRoomId);
socket.join('room' + nextRoomId);
onlineUsers[id].roomId = data.roomId;
updateUserList(prevRoomId, nextRoomId, id);
});
function updateUserList(prev, next, id) {
if (prev !== 0) {
io.sockets.in('room' + prev).emit("userlist", getUsersByRoomId(prev));
io.sockets.in('room' + prev).emit("lefted room", id);
}
if (next !== 0) {
io.sockets.in('room' + next).emit("userlist", getUsersByRoomId(next));
io.sockets.in('room' + next).emit("joined room", id);
}
}
function loginCheck(data) {
if (users.hasOwnProperty(data.id) && users[data.id].pw === data.pw) {
return true;
} else {
return false;
}
}
function joinCheck(data) {
if (users.hasOwnProperty(data.id)) {
return true;
} else {
return false;
}
}
function getUserBySocketId(id) {
return Object.keys(onlineUsers).find(key => onlineUsers[key].socketId === id);
}
function getUsersByRoomId(roomId) {
let userstemp = [];
Object.keys(onlineUsers).forEach((el) => {
if (onlineUsers[el].roomId === roomId) {
userstemp.push({
socketId: onlineUsers[el].socketId,
name: el
});
}
});
return userstemp;
}
});
헷갈리 것 같아 준비했다. 위에 코드를 이해 했으면 그냥 복붙해도 상관없다.
다음 시간에
다음 시간에는 이제 서버로 부터 받은userlist
를 화면에 바인딩하는 작업을 해보자.
'Side Project' 카테고리의 다른 글
[PROJECT] Socket.io 를 이용한 채팅 애플리케이션 (7) - 채팅 기능 구현 (들어갔으니 채팅을 치자 - 서버) (0) | 2018.09.28 |
---|---|
[PROJECT] Socket.io 를 이용한 채팅 애플리케이션 (6) - 채팅 기능 구현 (채팅방에 들어가보자 - 클라이언트) (0) | 2018.09.28 |
[PROJECT] Socket.io 를 이용한 채팅 애플리케이션 (4) - 채팅 창 웹 디자인 (0) | 2018.09.27 |
[PROJECT] Socket.io 를 이용한 채팅 애플리케이션 (3) - 회원관리 기능 구현 (0) | 2018.09.27 |
[PROJECT] Socket.io 를 이용한 채팅 애플리케이션 (2) - 회원관리 웹 디자인 (0) | 2018.09.20 |