2023.05.25 socket 채팅방 개설

이무헌·2023년 7월 21일
0

node.JS

목록 보기
8/10
post-thumbnail

1.socket으로 채팅방 만들기

  • app.js
    // 채팅방 만들기
    // 방을 따로 나눠서
    // 유저간에 귓속말
    
    const e = require("express");
    const path = require("path");
    const socketIo = require("socket.io");
    // 모듈 express socket.io ejs
    // 서버 대기상태
    // view엔진 세팅
    // socket 연결
    const app = e();
    
    app.set("views", path.join(__dirname, "page"));
    app.set("view engine", "ejs");
    app.use(e.urlencoded({ extended: false }));
    const server = app.listen(8080, () => {
      console.log("gogo");
    });
    
    app.get("/", (req, res) => {
      res.render("main");
    });
    
    let userId = [];
    let roomUser = [];
    const io = socketIo(server);
    
    io.sockets.on("connection", (socket) => {
      // 유저 접속시 배열에 유저의 아이디를 추가
      userId.push(socket.id);
      console.log(userId);
      socket.on("joinRoom", (room, name) => {
        // 방에 유저가 접속하면
        // join 메서드로 방에 입장 시킨다.
        // 방의 개념
    
        socket.join(room);
        // 현재 방에 있는 클라이언트에게 이벤트 푸쉬
        // 어느 바에 누가 접속했는디
        io.to(room).emit("joinRoom", room, name, socket.id);
      });
      socket.on("leaveRoom", (room, name) => {
        // 유저가 방에서 나가면
        // 유저가 방에서 제외되게 해주고
        socket.leave(room);
        // 어느 방에서 누가 나갔는지 해당 방에 있는 유저들에게 이벤트 푸쉬
        io.to(room).emit("leaveRoom", room, name);
      });
    
      socket.on("disconnect", () => {
        userId = userId.filter((a) => {
          return a != socket.id;
        });
        console.log("현재 접속중인 유저 id", userId);
      });
      socket.on("chat", (room, name, msg) => {
        io.to(room).emit("chat", name, msg);
      });
      socket.on("chat2", (id, name, msg) => {
        io.to(id).emit("chat", name, "귓속말:" + msg);
      });
    
      socket.on("addToTab", (num, room, name) => {
        if (roomUser[num] == undefined) {
          roomUser[num] = [{ name, id: socket.id }];
        } else {
          roomUser[num].push({ name, id: socket.id });
        }
        io.to(room).emit("addToTab", roomUser, num);
      });
    
      socket.on("subTab", (num, name,room) => {
        roomUser[num] = roomUser[num].filter((a) => {
          return a.name != name;
        });
        io.to(room).emit('subTab',roomUser,num);
      });
    });
    • joinRoom이벤트가 실행되면 socket에 join으로 room을 매개변수로 전달한다.
    • 이렇게 하면 to로 해당 room으로 emit을 실행 할 수 있다.
    • 즉, room은 단톡방 같은 개념이다
    • roomUser는 해당 톡방의 유저목록이다.
    • 프론트에서는 5개의 창이 있을 때(유저도 5명), 변수가 공유되지 않는다. 그러므로 서버단에서 로직을 처리하자
    • addToTab과subToTab으로 roomUser 배열을 조정하여 해당하는 톡방에 보낸다.
  • main.ejs
    <!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" />
        <title>Document</title>
        <script src="/socket.io/socket.io.js"></script>
        <style>
          body {
            position: relative;
            height: 100vh;
            list-style: none;
          }
          .content {
            position: absolute;
            /* 무조건 가운데로 */
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 500px;
            height: 500px;
            border: 1px solid;
          }
    
          #send {
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            border: 1px solid;
            box-sizing: border-box;
            display: flex;
          }
          #send #msg {
            border: 0;
            box-sizing: border-box;
            padding: 10px;
            width: 85%;
          }
    
          #send #sendBtn {
            background-color: skyblue;
            border: none;
            box-sizing: border-box;
            padding: 10px;
            width: 15%;
            cursor: pointer;
            z-index: 10;
          }
    
          #message {
            margin: 0;
            padding: 0;
          }
          #login {
            width: 300px;
            height: 300px;
            display: flex;
            align-items: center;
            justify-content: center;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
          }
    
          .join_text {
            background-color: gray;
            list-style: none;
          }
          .leave_text {
            background-color: greenyellow;
            border: 1px solid skyblue;
            list-style: none;
          }
          #main {
            display: none;
          }
    
          .tab {
            width: 200px;
            height: 600px;
            border: 1px solid;
            position: absolute;
            right: 0;
          }
          .tab li {
            list-style: none;
            left: 0;
            margin: 5px;
            cursor: pointer;
          }
        </style>
      </head>
      <body>
        <div class="content">
          <div>귀여운 동물 카톡</div>
          <div id="login">
            <p>login</p>
            <input type="text" id="username" />
            <button id="loginBtn">connect</button>
          </div>
          <div id="main">
            <select name="" id="rooms">
              <option value="cat">고양이 방</option>
              <option value="dog">강아지 방</option>
            </select>
            <div id="send">
              <input type="text" id="msg" />
              <button id="sendBtn">send</button>
            </div>
            <div id="send2">
              <input type="text" id="msg2" />
              <button id="sendBtn2">indivisual message</button>
            </div>
            <ul id="message"></ul>
          </div>
        </div>
        <div class="tab">
          <ul class="user-list"></ul>
        </div>
      </body>
      <script>
        window.onload = () => {
          let userId = [];
          loginBtn.onclick = () => {
            login.style.display = "none";
            main.style.display = "block";
            const name = username.value;
            console.log(rooms.options);
            console.log(rooms.selectedIndex);
            // 선택한 인덱스의 옵션 value 값
            let room = rooms.options[rooms.selectedIndex].value;
            let lastRoom = 0;
            const socket = io.connect();
            socket.emit("joinRoom", room, name);
    
            rooms.onchange = (e) => {
              // 이벤트가 일어난 태그
              let el = e.target;
              console.log(el.selectedIndex);
              //   해당 유저가 room에서 떠남
              socket.emit("leaveRoom", room, name);
              socket.emit("subTab", lastRoom, name, room);
              room = rooms.options[rooms.selectedIndex].value;
              lastRoom = rooms.options[rooms.selectedIndex].value;
              socket.emit("joinRoom", room, name);
            };
    
            socket.on("chat", (name, msg) => {
              message.innerHTML += `
                <li > 
                ${name}:${msg}
                </li>
    `;
            });
    
            socket.on("joinRoom", (room, _name, id) => {
              message.innerHTML += `
              
              <li class='join_text'> 
                ${_name}님이 ${room}에 들어왔음
                </li>
    
              `;
              console.log("몇 번?");
              // 유저 추가 요청
              if (name == _name)
                socket.emit("addToTab", rooms.selectedIndex, room, name);
            });
            // 탭에 유저 추가
            socket.on("addToTab", (roomUser, room) => {
              console.log(roomUser, "추가된 유저");
              console.log(room);
    
              document.querySelector(".user-list").innerHTML = "";
    
              roomUser[room].forEach((a) => {
                console.log(a.id, "아이디");
                document.querySelector(".user-list").innerHTML += `
                  <li id=${a.id}> 
                    이름은 ${a.name},아이디: ${a.id}
                    </li>
                  `;
              });
              setTimeout(() => {
                roomUser[room].forEach((a) => {
                  document
                    .querySelector(`#${a.id}`)
                    .addEventListener("click", () => {
                      msg2.value = a.id;
                      msg2.placeholer = a.id;
                      console.log(msg2.value, "asd");
                    });
                }, 200);
              });
            });
            // 유저 빼기
            socket.on("subTab", (roomUser, room) => {
              roomUser[room].forEach((a) => {
                document.querySelector(".user-list").innerHTML = "";
                document.querySelector(".user-list").innerHTML += `
                  <li id=${a.id}> 
                    이름은 ${a.name},아이디: ${a.id}
                    </li>
                  `;
              });
            });
    
            socket.on("leaveRoom", (room, name) => {
              message.innerHTML += `
    <li class='leave_text'>
        ${name}님이 ${room}에서 나갔습니다.
        </li>
    `;
            });
            sendBtn.onclick = function () {
              socket.emit("chat", room, name, msg.value);
              msg.value = "";
            };
            sendBtn2.onclick = function () {
              socket.emit("chat2", msg2.value, name, msg.value);
              msg2.value = "";
            };
          };
        };
      </script>
    </html>
    • joinRoom 으로 유저가 join할 때 현재 유저 목록(tab)에 유저를 추가한다.
    • 이 때, a,b,c유저가 있을 때 c유저가 들어가도 a,b 유저의 joinRoom또한 실행되므로 이를 방지하기위해
      socket의 이름과 현재 브라우저의 이름이 같을 때만 addToTab을 emit으로 주게 만들었다.

2.느낀점

💡 저번 시간을 보충해서 room개념이 들어갔다. 단지 개인이 아닌 그룹에게 emit을 주는 거지만, 이를 배열로 처리하면서 대용량 데이터의 처리에 대한 고민을 하게 되는 계기가 되었다.
profile
개발당시에 직면한 이슈를 정리하는 곳

1개의 댓글

comment-user-thumbnail
2023년 7월 21일

글 잘 봤습니다, 많은 도움이 되었습니다.

답글 달기