TIL #17 Socket.IO Adapter[22.07.17]

Ellie·2022년 7월 20일
1

TodayILearned

목록 보기
17/24
post-custom-banner

저번 시간에 이어 Socket.io의 Adapter 기능에 대해 정리해보려고 한다.

❓ Adapter는 뭔가요 ?

An Adapter is a server-side component which is responsible for broadcasting events to all or a subset of clients.

When scaling to multiple Socket.IO servers, you will need to replace the default in-memory adapter by another implementation, so the events are properly routed to all clients.

Adapter는 모든 클라이언트나 일부 클라이언트에 이벤트를 중계해주는 서버측 컴포넌트입니다.

Socket서버가 여러개로 늘어날 때, 기본 메모리 Adapter를 다른 Adapter로 교체해야하며, 그래야 이벤트들이 모든 클라이언트에서 적절하게 라우팅될 수 있습니다.

출처: socket.io - Adapter

위는 내가 임의로 번역해봤다. 짧은 설명이지만 socket에 대한 역할을 잘 설명해놓았다고 생각했다.

내가 이해한 바로는, socket.io에서 Adapter는 다른 서버들 사이에 실시간 어플리케이션을 동기화하는 역할을 맡는다.

원래 서버의 메모리에서 기본 Adapter를 사용하고 있다.(socket.io의 Adapter가 아님! 기본제공되는 것). 서버는 클라이언트와의 커넥션들을 메모리에 저장하며, 데이터베이스에는 아무것도 저장하고 있지 않다.

또한 서버는 모든 클라이언트에 커넥션을 열어놓아야 하기 때문에 많은 브라우저, 창에서 커넥션이 들어오면 서버는 그 연결들을 감당하지 못해 두 세개씩 늘어나기 시작한다.

그렇게 늘어난 서버는 더이상 같은 memory pool을 사용하지 않는다. 각 서버는 다른 메모리를 사용하고 있다. 이 뜻은, 어떤 두명의 사용자가 있다고 가정했을 때 그 두명의 유저는 똑같은 클라이언트 화면을 보고 있지만 다른 서버에 연결되어 있는 상태가 된다는 뜻이다. 그렇기 때문에 실시간으로 데이터를 주고 받기 어렵게 된다. 왜냐하면 다른 서버에 연결되어 있으니까!

바로 이런 경우에 adapter를 사용해야 한다.

adapter는 만약 서버 A에 있는 클라이언트를 이용하는 유저가 서버 B에 있는 유저에게 무언가를 보내고 싶을 때 동기적으로 보낼 수 있게 해준다.

위에는 MongoDB를 예시로 들었는데, 서버A에서 전송한 데이터를 서버A의 Mongo adapter를 거쳐 MongoDB를 거처 다시 서버B의 Mongo adapter를 거쳐 서버 B로 데이터를 전달해 클라이언트에 나타내는 것이다. 정말 콘센트 줄을 연결해 데이터를 주고받는 듯한 느낌이다.

그래서 adapter누가 연결되었는지, 현재 어플리케이션에 room이 얼마나 있는지를 알려줄 수 있다.


🔎 Adapter 사용해보기

rooms: Map(2) {
  //... 현재 애플리케이션에 있는 모든 room들을 볼 수 있다.
}
sids: Map(2) {
  //... socket id들을 볼 수 있다.
}

socket는 그 자체로 방을 하나 갖고 있다. 따라서 socket id는 세션의 클라이언트 식별자, 즉 유저 아이디임과 동시에 socket의 private한 방의 아이디라고 이해할 수 있다.

위에서 adapter를 콘솔로 찍어봤을때 rooms는 이 socket.id들인 private room과 join을 통해 만들어진 public room이 함께 모여있는 객체이다.

sids는 socket id만 모여있는 객체이니 rooms는 sids를 포함한다고 볼 수 있다.

// server 
// 현재 서버 안에 있는 모든 방의 배열 얻기
function publicRooms() {
  const {
    sockets: {
      adapter: { sids, rooms },
    },
  } = wsServer;

  const publicRooms = [];

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

// 현재 서버 개수 알아내기
function countRoom(roomName) { 
  return wsServer.sockets.adapter.rooms.get(roomName)?.size;
}
  
wsServer.on("connection", (socket) => {
  socket.on("join_room", (roomName) => {
    socket.join(roomName);
    wsServer.to(roomName).emit("join_room", countRoom(roomName));
    // 자신을 포함한 모두에게 메시지 전달
    wsServer.sockets.emit("room_change", publicRooms());
  });
  socket.on("disconnecting", () => {
    socket.rooms.forEach((room) =>
      socket.to(room).emit("bye", countRoom(room) - 1)
    );
  });
  socket.on("disconnect", () => {
    wsServer.sockets.emit("room_change", publicRooms());
  });

✅ wsServer.sockets.emit()

애플리케이션에 연결된 모든 socket에 메시지를 보낸다. 예를 들어 다른 방안에 있지만 다른 클라이언트에서 생성된 방에 대한 알림을 받을 수 있다. 위에서는

wsServer.sockets.emit("room_change", publicRooms());

로 현재 서버에 있는 모든 방을 배열로 전달할 수 있다.

이렇게 adapter를 이용해 서버가 다르더라도 같은 화면을 볼 수 있게 구성해놓았다면, 이번에는 클라이언트 파일에서 이 데이터를 전달받아 생성되어 있는 방들을 화면에 보여주고 싶다면 이렇게 짜면 된다.

  • 클라이언트에서 생성된 방 리스트 보여주기
socket.on("room_change", (rooms) => {
  const roomList = welcome.querySelector("ul");
  roomList.innerHTML = "";
  // 만약 배열 요소가 하나 있었는데 나가서 다시 빈배열을 전달받으면 화면에 반영되지 않는다. 
  // 빈배열이지만 화면에는 방이 하나 있는 것처럼 나오는데, 이유는 아무 코드가 없다면 빈배열일때 아무것도 하지 않기 때문이다. 
  // 따라서 아래와 같은 코드가 필요하다.
  if(rooms.length === 0) {
    return;
  }
  rooms.forEach(room => {
    const li = document.createElement("li");
    li.innerText = room;
    roomList.append(li);
  }
});

✅ socket.to(roomName).emit()

방에서 자신을 제외한 모두에게 메시지 전달

✅ wsServer.to(roomName).emit()

방에서 자신을 포함한 모든 유저에게 메시지 전달

  • 유저가 퇴장했을 때 방에 메시지 남기기
function addMessage(message) {
  const ul = call.querySelector("ul");
  const li = document.createElement("li");
  li.innerText = message;
  ul.appendChild(li);
}

socket.on("bye", (user, newCount) => {
  const h3 = call.querySelector("h3");
  h3.innerText = `Room ${roomName} (${newCount})`;

  addMessage(`${user} left.`);
});

참고

노마드 코더 줌 클론코딩
socket.io 공식문서: adapter
socket.io 공식문서: serveradaptervalue

profile
정말로 아는 것인지 항상 의심하기
post-custom-banner

0개의 댓글