[포스코x코딩온] 웹개발자 풀스택 부트캠프8기
[포스코x코딩온]Socket.IO를 활용한 채팅 만들기 완성
항상 발전하는 개발자
2023. 8. 31. 00:56
728x90
Socket.IO를 활용한 채팅 만들기 완성
1. 채팅방 입장 시 입장 문구 띄우기
이러한 결과를 만들기 위한 코드
클라이언트
- 입장 전에 채팅방이랑 사용자 이름을 서버로 전송합니다.
<form id="room">
<input type="text" id="roomName" placeholder="채팅방 만들기" />
<input type="text" id="userName" placeholder="사용자 이름 입력" />
<button>만들기</button>
</form>
<script>
///폼 이벤트
roomForm.addEventListener("submit", (e) => {
e.preventDefault();
const roomName = roomForm.querySelector("#roomName");
const userName = roomForm.querySelector("#userName");
if (roomName.value === "" || userName.value === "") {
alert("방이름과 닉네임 적어주세요");
return;
}
socket.emit("create", roomName.value, userName.value, () => {
const main = document.querySelector("#main");
const body = document.querySelector("#body");
main.hidden = true;
body.hidden = false;
myName = userName.value;
});
});
</script>
서버
- 받아온 채팅방으로 join을 하고 socket.room이랑 socket.user에 데이터 저장하고 입장문구를 보내줍니다.
socket.on("create", (roomName, userName, cb) => {
//join(방이름) 해당 방이름으로 없다면 생성. 존재하면 입장
//socket.rooms에 socket.id값과 방이름 확인가능
socket.join(roomName);
//socket은 객체이며 원하는 값을 할당할 수 있음
socket.room = roomName;
socket.user = userName;
socket.to(roomName).emit("notice", `${socket.user}님이 입장하셨습니다`);
클라이언트
- 입장문구를 받아와서 화면에 띄워줍니다.
//입장 메세지 이벤트
socket.on("notice", (message) => {
const div = document.createElement("div");
const p = document.createElement("p");
p.textContent = message;
div.appendChild(p);
notice.appendChild(div);
});
2. 채팅하기
이번 코드는 코드를 보면서 이해하면 되기래 코드만 올리겠습니다. CSS도 같이 있습니다.
클라이언트
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="/socket.io/socket.io.js"></script>
<title>Document</title>
<style>
#body {
width: 100%;
height: 70vh;
position: relative;
background: #75e3c4;
}
#chat {
position: absolute;
bottom: 0px;
width: 100%;
display: flex;
justify-content: space-between;
}
#chat input {
width: 80%;
}
.my-chat {
display: flex;
justify-content: end;
padding: 2px 0px;
}
.my-chat p {
margin: 0;
padding: 10px;
background: yellow;
margin-right: 10px;
border-radius: 10px;
}
.other-chat {
display: flex;
justify-content: start;
padding: 2px 0px;
}
.other-chat p {
margin: 0;
padding: 10px;
background: white;
margin-left: 10px;
border-radius: 10px;
}
#notice {
display: flex;
flex-direction: column;
text-align: center;
}
#notice p {
margin: 0;
}
.secret-chat p {
background: pink;
}
</style>
</head>
<body>
<div id="main">
<form id="room">
<input type="text" id="roomName" placeholder="채팅방 만들기" />
<input type="text" id="userName" placeholder="사용자 이름 입력" />
<button>만들기</button>
</form>
<ul id="lists"></ul>
</div>
<div id="body" hidden>
<div id="msg">
<div id="notice"></div>
<!-- <div class="my-chat">
<p>채팅테스트(나)</p>
</div>
<div class="other-chat">
<p>채팅테스트(상대방)</p>
</div> -->
</div>
<form id="chat">
<select id="userList"></select>
<input type="text" id="message" placeholder="메세지 입력" />
<button>입력</button>
</form>
</div>
<script>
const socket = io();
const roomForm = document.querySelector('#room');
const chatForm = document.querySelector('#chat');
const msg = document.querySelector('#msg');
const notice = document.querySelector('#notice');
let myName;
//룸리스트
socket.on('roomList', (roomLists) => {
console.log(roomLists);
const lists = document.querySelector('#lists');
lists.textContent = '';
roomLists.forEach((roomList) => {
const li = document.createElement('li');
li.textContent = `${roomList} 와(과) 닉네임 입력 후 입장`;
lists.appendChild(li);
});
});
//사용자 리스트
socket.on('userList', (userLists) => {
console.log(userLists);
const lists = document.querySelector('#userList');
lists.textContent = '';
let options = `<option value="all">전체</option>`;
for (let i of userLists) {
options += `<option value="${i.key}">${i.name}</option>`;
}
lists.innerHTML = options;
});
//메세지 띄우기
socket.on('newMessage', (message, nick, bool) => {
console.log(message, nick);
const div = document.createElement('div'); //밖div
const p = document.createElement('p'); //안쪽p
console.log('닉', myName);
if (myName === nick) {
div.classList.add('my-chat');
} else {
div.classList.add('other-chat');
}
if (bool) {
div.classList.add('secret-chat');
}
//채팅텍스트
p.textContent = `${nick} : ${message}`;
div.appendChild(p);
msg.appendChild(div);
});
//입장 메세지 이벤트
socket.on('notice', (message) => {
const div = document.createElement('div');
const p = document.createElement('p');
p.textContent = message;
div.appendChild(p);
notice.appendChild(div);
});
///폼 이벤트
roomForm.addEventListener('submit', (e) => {
e.preventDefault();
const roomName = roomForm.querySelector('#roomName');
const userName = roomForm.querySelector('#userName');
if (roomName.value === '' || userName.value === '') {
alert('방이름과 닉네임 적어주세요');
return;
}
socket.emit('create', roomName.value, userName.value, () => {
const main = document.querySelector('#main');
const body = document.querySelector('#body');
main.hidden = true;
body.hidden = false;
myName = userName.value;
});
});
//메세지 보내기
chatForm.addEventListener('submit', (e) => {
e.preventDefault();
const user = document.querySelector('#userList');
const message = document.querySelector('#message');
console.log(user.value);
const msg = {
nick: myName,
user: user.value,
message: message.value,
};
socket.emit('sendMessage', msg);
message.value = '';
});
</script>
</body>
</html>
서버
const http = require("http");
const express = require("express");
const SocketIO = require("socket.io");
const PORT = 8000;
const app = express();
const server = http.createServer(app);
const io = SocketIO(server);
app.set("view engine", "ejs");
app.get("/", (req, res) => {
res.render("index");
});
app.get("/:room", (req, res) => {
const room = req.params.room;
});
function getUsersInRoom(room) {
const users = [];
const clients = io.sockets.adapter.rooms.get(room);
//console.log(clients);
if (clients) {
clients.forEach((socketId) => {
const userSocket = io.sockets.sockets.get(socketId);
//사용자에게 메세지를 보내기 위해서 객체형태로 변경
//key: 소켓아이디, value:이름
const info = { name: userSocket.user, key: socketId };
users.push(info);
});
}
return users;
}
const roomList = [];
io.on("connection", (socket) => {
//socket!//
//socket은 접속한 웹페이지, io는 접속해있는 모든 웹페이지
//웹 페이지가 접속이되면 고유한 id값이 생성됨. socket.id로 확인가능
//console.log(io.sockets);
//채팅방 목록 보내기
socket.emit("roomList", roomList);
//채팅방 만들기 생성
socket.on("create", (roomName, userName, cb) => {
//join(방이름) 해당 방이름으로 없다면 생성. 존재하면 입장
//socket.rooms에 socket.id값과 방이름 확인가능
socket.join(roomName);
//socket은 객체이며 원하는 값을 할당할 수 있음
socket.room = roomName;
socket.user = userName;
socket.to(roomName).emit("notice", `${socket.user}님이 입장하셨습니다`);
//채팅방 목록 갱신
if (!roomList.includes(roomName)) {
roomList.push(roomName);
//갱신된 목록은 전체가 봐야함
io.emit("roomList", roomList);
}
const usersInRoom = getUsersInRoom(roomName);
io.to(roomName).emit("userList", usersInRoom);
cb();
});
socket.on("sendMessage", (message) => {
if (message.user === "all") {
io.to(socket.room).emit(
"newMessage",
message.message,
message.nick,
false
);
} else {
io.to(message.user).emit(
"newMessage",
message.message,
message.nick,
true
);
//자기자신한테 메세지 띄우기
socket.emit("newMessage", message.message, message.nick, true);
}
});
socket.on("disconnect", () => {
if (socket.room) {
socket.leave(socket.room);
}
});
});
server.listen(8000, () => {
console.log(`http://localhost:${PORT}`);
});
이번 Socket.IO를 통해 채팅을 만들면서 느낀점이 소켓통신을 통해 실시간으로 서비스하는 무언가를 만들 수 있겠다는 점과 통신을 위해 하나씩 하나씩 보면서 하면 코드를 금방 작성한다는 점이다.
또, 이번에 사용한 Socket.IO 함수 말고도 더 많은 함수들이 http://socket.io/공식문서에 많이 있으니 자주 참조하면 되겠다는 것을 알게 되었다.
내일부터 프로젝트가 시작되는데 이제까지 배운 부분을 잘 사용하여 좋은 프로젝트를 만들어야겠다.
728x90