4. Code Arena, Code Chat 공통 - asdfgl98/Project-CodeBuddy GitHub Wiki
- CodeChat, CodeArena 두 가지의 서비스가 따로 운영되기 때문에 서버를 분리할 필요가 있었습니다.
- Socket.io의 NameSpace를 사용해 Code Arena와 Code Chat 두 개의 서버를 분리하여 각각 사용할 수 있게 구현하였습니다.
Name Space 분리 코드
server.js (line 18)
// 네임스페이스로 io 서버 분리 /CodeChat, /CodeArena
const ChatNamespace = io.of("/CodeChat");
const ArenaNamespace = io.of("/CodeArena");
server.js (line 56)
// 네임스페이스 사용했기 때문에 파일 경로 지정
app.use(
"/CodeChat/public/CodeChat",
express.static(path.join(__dirname, "public/CodeChat"), {
setHeaders: (res, filePath) => {
const mimeType = mime.getType(filePath);
res.setHeader("Content-Type", mimeType);
},
})
);
// 네임스페이스 사용했기 때문에 파일 경로 지정
app.use(
"/CodeArena/public/CodeArena",
express.static(path.join(__dirname, "public/CodeArena"), {
setHeaders: (res, filePath) => {
const mimeType = mime.getType(filePath);
res.setHeader("Content-Type", mimeType);
},
})
);
Code Chat NameSpace 접속 구분 코드
** server.js (line 84)
// "Chat" namespace에 접속한 클라이언트 처리-----------------------------------------------------
ChatNamespace.on("connection", (socket) => {
console.log("Code 네임스페이스에 클라이언트가 연결되었습니다.");
// 이하 생략
Code Arena NameSpace 접속 구분 코드
**server.js (line 347)
// -------------------------------------------------------- CodeArena 시작 ----------------------------------------------------------------------------------
// "Arena" namespace에 접속한 클라이언트 처리
ArenaNamespace.on("connection", (socket) => {
console.log("Arena 네임스페이스에 클라이언트가 연결되었습니다.");
// 이하생략
NameSpace.gif
- 사용자가 CodeArena/Chat 페이지에 접속하면 socket.io 문법인 emit과 on을 통해 접속 이벤트를 감지하고,
axios를 이용해 DB에 있는 채팅방 목록을 요청하여 반환받은 데이터로 리스트를 생성합니다. - 사용자가 방 생성, 입장, 퇴장 시 추가 및 수정된 정보를 axios로 전달하여 DB에 저장 및 수정하고 NameSpace에 접속해있는 모든 유저에게
새롭게 갱신된 방 목록을 전송하여 실시간으로 리스트를 수정합니다.
방 정보를 매개변수로 입력 받아서 리스트를 최신화 하는 함수
codeArenaList.js (line 113)
//최신화 함수
const updateArenaRoom = (roomList) => {
const $board_list = document.getElementById("board-list");
const $board_table = $board_list.querySelector(".board-table");
const $tbody = $board_table.querySelector("tbody");
const $tr = document.querySelectorAll("tr");
if (clickEventHandler) {
$tbody.removeEventListener("click", clickEventHandler);
}
roomList.forEach((roomInfo) => {
const newRow = document.createElement("tr");
newRow.id = "room_" + roomInfo.ROOM_NUMBER;
// 방 정보를 td에 추가
newRow.innerHTML = `
<td id="room-Num">${roomInfo.ROOM_NUMBER}</td>
<td class="item ${roomInfo.ROOM_LANG}">${roomInfo.ROOM_LANG}</td>
<th>
<a class="room-link room-${roomInfo.ROOM_NUMBER}" data-roomnumber="${roomInfo.ROOM_NUMBER}" data-roomname="${roomInfo.ROOM_NAME}" data-roomhost="${roomInfo.ROOM_HOST}">${roomInfo.ROOM_NAME} ( ${roomInfo.USER_COUNT} / 4 )</a>
<p>테스트</p>
</th>
<td>${roomInfo.ROOM_HOST}</td>
`;
// 새로운 행을 테이블의 맨 위에 추가
$tbody.prepend(newRow);
// axios.get("/room/createRoom", { room: "hi" }).then((res) => {
// currentNickname = res.data;
// });
clickEventHandler = handleClick;
$tbody.addEventListener("click", clickEventHandler);
$c_content_num.textContent = `${roomInfo.USER_COUNT}/4`; // 채팅방 펼쳤을 때 인원 수
$mini_room_users.textContent = `${roomInfo.USER_COUNT}/4`; // 채팅방 접었을 때 인원 수
});
};
사용자 접속시 채팅방 리스트 최신화 이벤트 감지
codeArenaList.js (line 154)
// 사용자 접속시 채팅방 리스트 최신화
arenaSocket.on("updateRoomList", () => {
const $board_list = document.getElementById("board-list");
const $board_table = $board_list.querySelector(".board-table");
const $tbody = $board_table.querySelector("tbody");
const $trs = $tbody.querySelectorAll("tr");
axios.get("/codeArena/arenaList", { re: "hi" }).then((res) => {
let roomList = JSON.parse(res.data);
$trs.forEach(($tr) => {
$tr.remove();
});
updateArenaRoom(roomList);
});
});
방 생성시 채팅방 리스트 최신화
codeArenaList.js (line 169)
// 방 생성시 채팅방 리스트 최신화(기존의 테이블 tr 모두 삭제 후 최신화)
arenaSocket.on("updateRoomList2", () => {
const $board_list = document.getElementById("board-list");
const $board_table = $board_list.querySelector(".board-table");
const $tbody = $board_table.querySelector("tbody");
const $trs = $tbody.querySelectorAll("tr");
axios.get("/codeArena/arenaList", { re: "hi" }).then((res) => {
let roomList = JSON.parse(res.data);
$trs.forEach(($tr) => {
$tr.remove();
});
updateArenaRoom(roomList);
});
});
codeArenaList.js (line 196)
//방장이 방 생성시 database에 방 정보 입력 및 방 입장 처리
arenaSocket.on("host_enterRoom", (data) => {
let nickName = data[0].createdBy;
let roomName = data[0].room_name;
let roomNum = data[0].room_number;
const addRoomToTable = (updateRooms) => {
axios.post("/codeArena/updateroom", { updateRooms }).then((res) => {
let roomInfo = JSON.parse(res.data);
});
};
addRoomToTable(data);
enterRoom(roomName, roomNum, nickName);
});
CodeArena.gif
CodeChat.gif
- 사용자가 방에 입장할 때 사용자에 대해 socket 내부에서 접속자 이름, 방 번호(고유 값)를 별도로 저장합니다.
- 사용자가 메세지 전송을 하면 server에서 사용자 이름과 메세지 내용을 수신하고, 사용자가 속해있는 방 번호로 구분하여
해당 방에 있는 인원 중 메세지를 전송한 사용자에게 my_message 이벤트를, 메세지를 전송한 사용자를 제외한 모두에게 other_message 이벤트를 전송합니다. - 각각의 이벤트를 받은 사용자의 클라이언트 화면에 메세지가 출력됩니다.
- my_message : 우측 파란색 메세지 박스 (내 메세지)
- other_message : 좌측 주황색 메세지 박스 (상대 메세지)
메세지 전송 함수
codeArenaList.js (line 518)
// 메세지 전송 함수
const handleMessageSubmit = (event) => {
event.preventDefault();
const message = $form_input.value; // 메시지 입력값 가져오기
// 메세지의 공백이나 줄바꿈을 빈 문자열로 바꿔서 빈문자열만 있으면 보내지않기
let checkMessage = message.replace(/\s| /gi, '');
// 메세지 공백 아닐 때 메세지 보내기
if(checkMessage !== ""){
arenaSocket.emit("new_message", { currentNickname, message: message });
}
$form_input.value = ""; // 입력 창 초기화
scrollToBottom()
};
서버에서 채팅 메세지 수신
server.js (line 414)
// 아레나 채팅 메세지 수신
socket.on("new_message", ({ currentNickname, message: message }) => {
let roomNum = socket.room_number;
socket.emit("my_message", { currentNickname, message: message });
socket.broadcast
.to(roomNum)
.emit("other_message", { currentNickname, message: message });
});
server로부터 메세지 수신 시 메세지div 생성
codeArenaList.js (line 544)
// 내 메세지 div 생성
arenaSocket.on("my_message", ({ currentNickname, message }) => {
const $div = document.createElement("div");
const $Div = document.createElement('div')
$Div.id ='my_M'
$div.id = "my_message"
$div.textContent = `${message}`;
$Div.appendChild($div);
$c_main_content.appendChild($Div);
scrollToBottom()
});
// 상대방 메세지 div 생성
arenaSocket.on("other_message", ({ currentNickname, message }) => {
const $div = document.createElement("div");
$div.id = "other_message"
$div.textContent = `${currentNickname} : ${message}`;
$c_main_content.appendChild($div);
scrollToBottom()
});