[WebSocket/Socket IO] 채팅 서비스 구현 9. 공개 채팅방 찾고 알림 전송하기

2022. 12. 7. 09:00·개발/Projects

이제 채팅방에서 사용되는 기본 기능을 구현하는 마지막 포스팅입니다. 이번에는 공개 방과 비공개 방을 구분하고, 공개 방에 관한 정보를 업데이트 해보도록 하겠습니다.

공개 방과 비공개 방의 개념

사실 공개 방과 비공개 방의 개념이 낯선 것일 수도 있는데요. 왜냐하면 이제까지는 이를 구분하지 않고 방을 만들고 여기에 참여했기 때문입니다.

 

앞서 채팅 서비스를 구현하며 소켓의 id로 사용자를 구분했던 것을 기억하시나요? 해당 id는 소켓에 접속하는 사용자에게 부여되는 고유한 아이디로 접속과 실은 동시에 자신만의 프라이빗한 비공개 방에 접속하게 되는 것과 같은 이치입니다. 반면, 우리가 공개 방이라고 정의하는 것에는 이러한 소켓 아이디가 존재하지 않습니다.

공개 방과 비공개 방 구분하기

그렇다면 공개 방과 비공개 방을 어떻게 구분할 수 있을까요? 바로 아래와 같이 rooms과 sids 속성을 통해 이를 구분할 수 있습니다. 앞서 파악한 것처럼 모든 소켓은 기본적으로 sid를 갖지만, 공개 방은 비공개 방에 포함되어 있는 sid가 없습니다. 이를 활용하여 다음과 같이 공개 방의 키를 추출할 수 있습니다.

// server.js

// 공개 방 찾기
function updatePublicRoom() {
  const rooms = ioServer.sockets.adapter.rooms;
  const sids = ioServer.sockets.adapter.sids;

  let publicRooms = [];

  rooms.forEach((_, key) => {
    if (sids.get(key) === undefined) {
      publicRooms.push(key);
    }
  });
}

위 코드는 아래와 같이 리팩토링할 수 있습니다. 동일하게 공개 방의 키를 추출하는 로직입니다.

// server.js

// 공개 채팅 방 찾기
function updatePublicRoom() {
  const {
    sockets: {
      adapter: { rooms, sids },
    },
  } = ioServer;

  let publicRooms = [];

  rooms.forEach((_, key) => {
    if (sids.get(key) === undefined) {
      publicRooms.push(key);
    }
  });
}

공개 채팅 방 알림 메시지 보내기

이제 해당 공개 채팅 방에 관한 정보를 서버 전체에 알림 메시지로 전달해보겠습니다. home.pug에서 다음과 같이 화면을 업데이트 해줍니다.

// home.pug

doctype html
html(lang="en")
  head
    meta(charset="UTF-8")
    meta(http-equiv="X-UA-Compatible", content="IE=edge")
    meta(name="viewport", content="width=device-width, initial-scale=1.0")
    link(rel="stylesheet", href="https://unpkg.com/mvp.css")
    title Zoom
  body
    header
      h1 Let's Chat Together!
    main
      div#openPublicRoom
        h2 Available Rooms
        ul
          li 🚪
      div#nickname
        form
          h2 Set a Nickname
          input(type="text", placeholder="Please enter your nicknamee", required, autofocus)
          button Save
      div#room
        form
          h2 Enter a Room
          input(type="text", placeholder="Please type Room name", required)
          button Enter the Room
      div#chat
        h2 Room Name
        ul
        form
          input(type="text", placeholder="You can type here", required)
          button Send

    script(src="/socket.io/socket.io.js") 
    script(src="/public/js/app.js")

방 변경 사항을 업데이트 할 프론트 로직을 추가해줍니다. 아래는 프론트 app.js의 현재까지의 최종 코드입니다.

// app.js

const socket = io();

const room = document.querySelector("#room");
const roomForm = room.querySelector("form");
const chat = document.querySelector("#chat");
const chatForm = chat.querySelector("form");
const nicknameSet = document.querySelector("#nickname");
const nicknameForm = nickname.querySelector("form");
const openPublicRoom = document.querySelector("#openPublicRoom");
const openPublicRoomList = openPublicRoom.querySelector("ul");

function sendMessage(message) {
  const ul = chat.querySelector("ul");
  const li = document.createElement("li");
  li.innerText = message;
  ul.appendChild(li);
}

function handleSendMessage(event) {
  event.preventDefault();

  const message = chatForm.querySelector("input");
  socket.emit("message", message.value, sendMessage);
  message.value = "";
}

room.hidden = true;
chat.hidden = true;
openPublicRoom.hidden = true;

function showRoom(roomName) {
  room.hidden = true;
  nicknameSet.hidden = true;
  chat.hidden = false;
  openPublicRoom.hidden = false;

  const roomNameHeader = chat.querySelector("h2");
  roomNameHeader.innerText = `Room: ${roomName}`;

  chatForm.querySelector("input").focus();

  chatForm.addEventListener("submit", handleSendMessage);
}

function handleRoomName(event) {
  event.preventDefault();

  const input = roomForm.querySelector("input");
  const roomName = input.value;

  socket.emit("room", roomName, showRoom);
}

function saveNickname(nickname) {
  room.hidden = false;
  nicknameSet.hidden = true;
  chat.hidden = true;

  roomForm.querySelector("input").focus();

  roomForm.addEventListener("submit", handleRoomName);
}

function handleNickName(event) {
  event.preventDefault();

  const input = nicknameForm.querySelector("input");
  const nickname = input.value;

  socket.emit("nickname", nickname, saveNickname);
}

nicknameForm.addEventListener("submit", handleNickName);

socket.on("greeting", (nickname) => {
  sendMessage(`${nickname} has joined!`);
});

socket.on("goodbye", (nickname) => {
  sendMessage(`${nickname} has left!`);
});

socket.on("sendMessage", sendMessage);

// 방 변경 사항 받기
socket.on("roomUpdate", (rooms) => {
  const li = openPublicRoomList.querySelector("li");
  li.innerText = `🚪 ${rooms}`;
});

다음으로 server.js에서 방 업데이트 사항을 전송할 수 있도록 설정해줍니다. 아래는 백엔드 측의 현재까지의 최종 코드입니다.

// server.js

import http from "http";
import { Server } from "socket.io";
import express from "express";

const app = express();

app.set("view engine", "pug");
app.set("views", __dirname + "/views");
app.use("/public", express.static(__dirname + "/public"));

app.get("/", (_, res) => res.render("home"));
app.get("/*", (_, res) => res.redirect("/"));

const httpServer = http.createServer(app);
const ioServer = new Server(httpServer);

// 공개 방 업데이트 로직
function updatePublicRoom() {
  const {
    sockets: {
      adapter: { rooms, sids },
    },
  } = ioServer;

  let publicRooms = [];

  rooms.forEach((_, key) => {
    if (sids.get(key) === undefined) {
      publicRooms.push(key);
    }
  });

  return publicRooms;
}

ioServer.on("connection", (socket) => {
  socket.onAny((event) => {
    console.log(`Socket Event: ${event}`);
  });
  socket.on("room", (roomName, showRoom) => {
    socket.join(roomName);
    showRoom(roomName);
    console.log(socket.rooms);
    socket.to(roomName).emit("greeting", socket["nickname"]);
    ioServer.sockets.emit("roomUpdate", updatePublicRoom()); // 입장하며 방에 업데이트하기

    socket.on("message", (message, sendMessage) => {
      message = `${socket["nickname"]}: ${message}`;
      socket.to(roomName).emit("sendMessage", message, sendMessage(message));
    });
  });

  socket.on("disconnecting", () => {
    socket.rooms.forEach((room) =>
      socket.to(room).emit("goodbye", socket["nickname"])
    );
    ioServer.sockets.emit("roomUpdate", updatePublicRoom()); // 퇴장하며 방에 업데이트하기
  });

  socket.on("nickname", (nickname, saveNickname) => {
    socket["nickname"] = nickname;
    console.log(`설정한 닉네임: ${socket["nickname"]}`);
    saveNickname(nickname);
  });
});

httpServer.listen(8000);

최종적으로 구현한 채팅 서비스의 모습은 아래와 같습니다!

'개발 > Projects' 카테고리의 다른 글

[WebSocket/Socket IO] 채팅 서비스 구현 10. 관리자 패널 추가하기  (0) 2022.12.07
[WebSocket/Socket IO] 채팅 서비스 구현 8. 닉네임 설정하기  (0) 2022.12.07
[WebSocket/Socket IO] 채팅 서비스 구현 7. 메시지 전송 및 수신  (0) 2022.12.06
'개발/Projects' 카테고리의 다른 글
  • [WebRTC] 1. 영상 채팅 화면과 소리 출력하기
  • [WebSocket/Socket IO] 채팅 서비스 구현 10. 관리자 패널 추가하기
  • [WebSocket/Socket IO] 채팅 서비스 구현 8. 닉네임 설정하기
  • [WebSocket/Socket IO] 채팅 서비스 구현 7. 메시지 전송 및 수신
휘Hwi
휘Hwi
여행, 사진, 개발, 책 이야기를 기록하는 여행자 휘의 블로그 𓂍
Klook.com
  • 휘Hwi
    휘: 끝나지 않은 이야기
    휘Hwi
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 기록
        • 일상 에세이
        • 인사이트
        • 투자와 재테크
        • 코인 이야기
        • 아카이빙
        • 집무실 레터
        • 사랑에 대하여
        • 번역 이야기(完)
        • 프리랜서 일지(完)
      • 여행
        • 🌎 세계 여행기 S1 (完)
        • 🌊 삼삼한 여행기 (完)
        • 🚶 온더로드
        • 🇯🇵 일본
        • 🏝️ 발리
        • 🇻🇳 베트남
        • 🇱🇰 스리랑카
        • 🇮🇳 인도
        • 🇹🇭 태국
        • 🇸🇬 싱가포르
        • 🇦🇺 호주
        • 🇭🇰 홍콩
        • 🇰🇷 한국
        • 🍚 여행자의 한 끼
        • ℹ️ 여행 정보
      • 사진
        • 사진가
        • 사진 이론과 생각
        • 사진 관련 정보
      • 영상
        • 파이널컷 모션 공부
        • 고프로 GoPro
        • 영상 관련 정보
      • 책
        • 책 읽고 쓰기
      • 개발
        • 티스토리
        • Internet
        • HTML
        • CSS
        • JavaScript
        • Typescript
        • React
        • Node.js
        • Express
        • NestJS
        • Python
        • Django
        • MySQL
        • MongoDB
        • AWS
        • Deployment
        • Terminal
        • Git
        • Glossaries
        • Articles
        • Projects
        • TIL;
      • 미분류
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
휘Hwi
[WebSocket/Socket IO] 채팅 서비스 구현 9. 공개 채팅방 찾고 알림 전송하기
상단으로

티스토리툴바