ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 항해 99 5기 TIL_82
    항해 99 2022. 4. 2. 16:11

    ▶ Today I Learned

     

    <실전 프로젝트>

     

    [다른 방의 사람에게 채팅이 보이거나 알람 발생]

     

    문제 상황 

    a와 b가 방을 만든 후 c가 a의 방에 들어간 다음 나와서 b의 방으로 들어감,

    여기서 a가 채팅을 치면 c는 b의 방에 있는데도 그 채팅이 보이고, a가 방을 나가면 그 방을 나갔단 알람이 c에게는 보임

    => c가 이전에 들어갔던 모든 참가자의 정보와 연결된 채 그대로 넘어옴,

    c가 이전에 들어갔던 방에서 제대로 빠져나오지 못한 것 같음

     

    => 우리가 짰던 코드 에서 특정 유저가 방을 나가면 방목록에서 그 유저의 소켓아이디를 제거하긴 함,

    but 이것과 별개로 socket에는 io.sockets라는 곳에 별도로 소켓 아이디와 방 목록 같은 게 있었던 것이 기억남

    => 그 socket 서버 자체의 방목록에서 특정 유저가 빠지지 않았을 지도 모른다는 결론에 도달

     

    => 이를 확인하기 위해 코드에 socket.leave(roomId)를 추가하고

    소켓이 연결되고 빠져나가는 부분, 메시지를 전달하는 부분에 여러 콘솔을 찍음

    특히 sids 라는 콘솔을 찍었는데, sids는 key값에는 socket id가 적혀있고 value에는 그 socket id가 접속해 있는 방의 이름이 적혀있다.

    그래서 특정 소켓아이디가 특정방에 있다면 그 방의 아이디가 소켓 아이디 옆에 나란히 붙게 된다. 속해 있는 방이 없다면 옆에는 방번호가 없다.

     

    socket.js 코드

    const app = require("./app");
    const server = require("http").createServer(app);
    const io = require("socket.io")(server, {
      cors: {
        origin: "*",
      },
    });
    
    
    // sids는 key값에는 socket id가 적혀있고 value에는 그 socket id가 접속해 있는 방의 이름이 적혀있다.
    
    const {
      sockets: {
          adapter: { sids, rooms },
      },
    } = io;
    
    //controller
    const RoomController = require("./controllers/roomController");
    
    const users = {};
    const socketToRoom = {};
    const socketToNickname = {};
    const socketToUser = {};
    
    io.on("connection", (socket) => {
      let roomId;
      let userId;
    
      let time = 0;
      let categoryId;
      let date;
    
    	// console.log("전역변수 roomId 이다ㅏㅏㅏㅏㅏㅏㅏㅏ", roomId)
    
      socket.on("join room", async (payload, done) => {
        roomId = payload.roomId;
        userId = payload.userId;
        categoryId = payload.categoryId;
        date = payload.date;
    
    	  // console.log("join room 했을 때 roomId", roomId)
        if (!roomId) {
          socket.emit("no data");
        }
    
        if (users[roomId]) { // 기존 참가자 있음
          
          if (users[roomId].length >= 4) { // 참가자 풀방
            return done();
          }
    
          users[roomId].push(socket.id);
        } else {
          // 첫 참가자
          users[roomId] = [socket.id];
        }
       console.log("그 유저의 socket.id", socket.id);
        // console.log("rooms socket join 하기 전", rooms);
        // console.log("users 정보, socket join 하기 전, socket leave 하기 전", users)
        console.log("socket join 하기 전 sids, sids는 key값에는 socket id가 적혀있고 value에는 그 socket id가 접속해 있는 방의 이름이 적혀있다.",sids );
        socket.join(roomId);
        // console.log("rooms socket join 하고난 후후", rooms);
        console.log("socket join 하고난 후 하고난 후 하고난 후 하고난 후",sids );
    
        socketToRoom[socket.id] = roomId; // 각각의 소켓아이디가 어떤 룸에 들어가는지
        socketToNickname[socket.id] = payload.nickname;
        socketToUser[socket.id] = {
          roomId: payload.roomId,
          id: payload.userId,
          nickname: payload.nickname,
          profileImg: payload.profileImg,
          masterBadge: payload.masterBadge,
          statusMsg: payload.statusMsg,
        }
    
        let others = users[roomId].filter((socketId) => socketId !== socket.id);
    
        const otherSockets = others.map((socketId) => {
          return {
            socketId,
            nickname: socketToNickname[socket.id],
          };
        });
        const otherUsers = others.map((socketId) => {
          return socketToUser[socketId]
        });
        console.log(otherUsers, "otherUsers");
        console.log("socket leave 하기전, otherUsers 하고난 후 socketToRoom socketToRoom socketToRoom",socketToRoom)
    
        socket.emit("send users", { otherSockets, otherUsers });
      });
    
      socket.on("send signal", (payload) => {
        const callerNickname = socketToNickname[payload.callerId];
        const userInfo = socketToUser[socket.id];
    
        io.to(payload.userToSignal).emit("user joined", {
          signal: payload.signal,
          callerId: payload.callerId,
          callerNickname,
          userInfo,
        });
      });
    
      socket.on("returning signal", (payload) => {
        io.to(payload.callerId).emit("receive returned signal", {
          signal: payload.signal,
          id: socket.id,
        });
      });
    
      socket.on("send_message", (payload) => {
        socket.broadcast.to(payload.roomId).emit('receive_message', payload);
    
    	  console.log("send_message할 때 roomId", roomId)
        console.log("send_message 할 때 그 유저와 속해있는 방", sids);
      });
      socket.on("send_emoji", (payload) => {
        socket.broadcast.to(payload.roomId).emit('receive_emoji', payload);
      });
    
      socket.on("save_time", (payload) => {
        time = payload; // data received for every 1500 seconds
      });
    
      // 타이머
      socket.on("start_timer", (payload) => {
        socket.broadcast.to(payload.roomId).emit("start_receive", payload);
      });
    
      socket.on("stop_time", (roomId) => {
        socket.broadcast.to(roomId).emit("stop_receive");
      });
    
      socket.on("reset_time", (roomId) => {
        socket.broadcast.to(roomId).emit("reset_receive");
      });
    
      socket.on("check absence", () => {
        socket.emit("resend check absence", { socketId: socket.id, roomId: socketToRoom[socket.id] });
      });
    
      socket.on("quit room", async () => {
        const data = {
          roomId,
          userId,
          time: time,
          categoryId: categoryId,
          date: date,
        };
    
        await RoomController.delete.participant(data);
    
    	  console.log("quit room 다 하고나서 roomId", roomId)
    
        
    
        if(users[roomId]) {
          users[roomId] = users[roomId].filter((id) => id !== socket.id);
        };
        const userInfo = socketToUser[socket.id];
    
        socket.broadcast.to(roomId).emit("user left", {
          socketId: socket.id,
          userInfo,
        });
    
        
        console.log("leave 하기 전",sids);
        socket.leave(roomId)
    
        console.log("socket leave 하고 난 후 socketToRoom socketToRoom socketToRoom",socketToRoom)
        // console.log("users 정보, socket leave 하고난 후", users)
        // console.log("rooms socket join 하고난 후 socket leave까지 하고난 후", rooms);
    
        // console.log("io.sockets io.sockets io.sockets io.sockets io.sockets", io.sockets)
    
        console.log("leave 하고나서 leave 하고나서 leave 하고나서",sids);
    
        delete socketToNickname[socket.id];
        delete socketToUser[socket.id];
      });
     
      socket.on("disconnecting", async () => {
        console.log(socket.id, socketToNickname[socket.id], "님의 연결이 끊겼어요.");
      });
    });
    
    
    
    module.exports = { server };

     

    콘솔에 찍어낸 결과

     

    그 유저의 socket.id qBDmASj9xD23Oz83AAA1
    socket join 하기 전 sids, sids는 key값에는 socket id가 적혀있고 value에는 그 socket id가 접속해 있는 방의 이름이 적혀있다. Map(5) {
      'j5l6MbzJRYTRnjC2AAAT' => Set(1) { 'j5l6MbzJRYTRnjC2AAAT' },
      'SYgXAgWPUkVP6c5CAAAV' => Set(1) { 'SYgXAgWPUkVP6c5CAAAV' },
      'SaoACILZxfUi5HSVAAAb' => Set(2) { 'SaoACILZxfUi5HSVAAAb', '150' },
      'bOa7LZztq8Uv-iAQAAAj' => Set(2) { 'bOa7LZztq8Uv-iAQAAAj', '145' },
      'qBDmASj9xD23Oz83AAA1' => Set(1) { 'qBDmASj9xD23Oz83AAA1' }
    }
    socket join 하고난 후 하고난 후 하고난 후 하고난 후 Map(5) {
      'j5l6MbzJRYTRnjC2AAAT' => Set(1) { 'j5l6MbzJRYTRnjC2AAAT' },
      'SYgXAgWPUkVP6c5CAAAV' => Set(1) { 'SYgXAgWPUkVP6c5CAAAV' },
      'SaoACILZxfUi5HSVAAAb' => Set(2) { 'SaoACILZxfUi5HSVAAAb', '150' },
      'bOa7LZztq8Uv-iAQAAAj' => Set(2) { 'bOa7LZztq8Uv-iAQAAAj', '145' },
      'qBDmASj9xD23Oz83AAA1' => Set(2) { 'qBDmASj9xD23Oz83AAA1', '150' }
    }
    
    ...
    
    socket leave 하기전, otherUsers 하고난 후 socketToRoom socketToRoom socketToRoom {
      SaoACILZxfUi5HSVAAAb: '150',
      'bOa7LZztq8Uv-iAQAAAj': '145',
      m3r6zHbKPb6nVrkdAAAz: '150',
      qBDmASj9xD23Oz83AAA1: '150'
    }
    
    
    SYgXAgWPUkVP6c5CAAAV undefined 님의 연결이 끊겼어요.
    send_message할 때 roomId 145
    send_message 할 때 그 유저와 속해있는 방 Map(4) {
      'j5l6MbzJRYTRnjC2AAAT' => Set(1) { 'j5l6MbzJRYTRnjC2AAAT' },
      'SaoACILZxfUi5HSVAAAb' => Set(2) { 'SaoACILZxfUi5HSVAAAb', '150' },
      'bOa7LZztq8Uv-iAQAAAj' => Set(2) { 'bOa7LZztq8Uv-iAQAAAj', '145' },
      'qBDmASj9xD23Oz83AAA1' => Set(2) { 'qBDmASj9xD23Oz83AAA1', '150' }
    }
    
    
    data { roomId: '145', userId: 7, time: 13, categoryId: 3, date: 1 }
    
    quit room 다 하고나서 roomId 145
    leave 하기 전 Map(4) {
      'j5l6MbzJRYTRnjC2AAAT' => Set(1) { 'j5l6MbzJRYTRnjC2AAAT' },
      'SaoACILZxfUi5HSVAAAb' => Set(2) { 'SaoACILZxfUi5HSVAAAb', '150' },
      'bOa7LZztq8Uv-iAQAAAj' => Set(2) { 'bOa7LZztq8Uv-iAQAAAj', '145' },
      'qBDmASj9xD23Oz83AAA1' => Set(2) { 'qBDmASj9xD23Oz83AAA1', '150' }
    }
    
    socket leave 하고 난 후 socketToRoom socketToRoom socketToRoom {
      SaoACILZxfUi5HSVAAAb: '150',
      'bOa7LZztq8Uv-iAQAAAj': '145',
      m3r6zHbKPb6nVrkdAAAz: '150',
      qBDmASj9xD23Oz83AAA1: '150'
    }
    
    leave 하고나서 leave 하고나서 leave 하고나서 Map(4) {
      'j5l6MbzJRYTRnjC2AAAT' => Set(1) { 'j5l6MbzJRYTRnjC2AAAT' },
      'SaoACILZxfUi5HSVAAAb' => Set(2) { 'SaoACILZxfUi5HSVAAAb', '150' },
      'bOa7LZztq8Uv-iAQAAAj' => Set(1) { 'bOa7LZztq8Uv-iAQAAAj' },
      'qBDmASj9xD23Oz83AAA1' => Set(2) { 'qBDmASj9xD23Oz83AAA1', '150' }
    }

     

    socket.join을 하고 나서와 socket.leave를 했을 때 145번 방의 소켓 bOa7LZztq8Uv-iAQAAAj의 우측에 145가 붙었다 잘 사라지는 것을 확인했다.

    이후 테스트해보았을 때 더 이상 위의 문제는 발생하지 않았다.

     

    이렇게 또 한 건 해결! ;) 뿌듯하다.

     

     

    ▶ 느낀 점

     

    오늘은 위의 문제를 스스로 분석도 해보고 이를 토대로 팀원 분과 의견을 나누기도 하며 실마리를 잡아 문제를 해결했다.

    이런 트러블 슈팅을 위해 집념을 가지고 해결하는 경험은 매우 귀중하다.

    결국에 내가 문제를 해결할 수 있다는 자신감을 심어주며 내 개발 능력 향상에도 큰 도움이 되기 때문이다.

    앞으로도 이렇게 문제를 해결해보아야지!

     

    +

     

    팀원들과 그간 코드에 대한 코드 리뷰를 했는데 정말 알찬 시간이었다.

    실전 프로젝트를 시작하고 알찬 순간 Top3에 꼽힐 만큼이었다.

    다른 분들이 해당 코드를 짤 때 어떤 사고방식을 가지고 짜셨는지를 알 수 있었고 내가 짰던 코드를 상기하며 설명해드리는 과정에서 스스로

    머릿속으로 한번 더 정리할 수 있었다. 

    특히 코드를 사전에 한번 씩 읽어보며 분석한 상태로 직접 코드를 짜신 분의 설명을 들으니 단번에 이해가 되고 귀에 쏙쏙 내용들이 들어왔다.

    덕분에 현재 코드를 어떻게 개선하면 좋을지에 대한 힌트, 그리고 더 놓은 이해도를 얻게 되었다.

    앞으로도 코딩을 하다가도 페어 프로그래밍이나 코드리뷰를 해주고 받는 것을 해보아야 겠다.

    물론 좋은 코드를 짜기 위한 스스로의 노력도 틈틈이 할 것이다.

     

    ▶ 공부 시 참고 링크들

     

    https://taehyeki.tistory.com/73?category=928019 

     

    Room chat 2

    Adapter는 기본적으로 다른 서버들 사이에 실시간 어플리케이션을 동기화 해준다. 우리는 서버의 메모리에서 Adapter를 사용하고 있다. 우리가 서버를 종료하고 다시 시작할 때 모든 room과 message와 s

    taehyeki.tistory.com

     

    '항해 99' 카테고리의 다른 글

    항해 99 5기 TIL_84  (0) 2022.04.03
    항해 99 5기 TIL_83  (0) 2022.04.03
    항해 99 5기 TIL_81  (0) 2022.04.01
    항해 99 5기 TIL_80  (0) 2022.03.31
    항해 99 5기 TIL_79  (0) 2022.03.30
Designed by Tistory.