프로그래밍 기초

Socket.IO 소개 1 - Server

hs-archive 2023. 7. 27. 21:02

들어가기 전에...

실시간 통신에 대해 먼저 배운 뒤 해당 글을 보면 더 좋습니다.

 

소개

Socket.IO는 low-latency, 양방향, 이벤트 기반 통신을 가능하게 하는 라이브러리입니다. WebSocket 프로토콜 위에 구축되었으며 HTTP long polling 또는 자동 재연결에 대한 폴백과 같은 추가 보장을 제공합니다.

 

다음은 Socket.IO를 사용한 간단한 양방향 통신 구현의 예입니다.

// Server Side
import { Server } from "socket.io";

const io = new Server(3000);

io.on("connection", (socket) => {
  // client에게 메시지를 보냅니다.
  socket.emit("hello from server", 1, "2", { 3: Buffer.from([4]) });

  // client가 보낸 메시지를 받습니다.
  socket.on("hello from client", (...args) => {
    // ...
  });
});



// Client Side
import { io } from "socket.io-client";

const socket = io("ws://localhost:3000");

// server에게 메시지를 보냅니다.
socket.emit("hello from client", 5, "6", { 7: Uint8Array.from([8]) });

// 서버가 보낸 메시지를 받습니다.
socket.on("hello from server", (...args) => {
  // ...
});

 

기능

다음은 plain Websockets을 통해 Socket.IO에서 제공하는 기능입니다.

 

HTTP long-polling fallback

WebSocket 연결을 설정할 수 없는 경우 연결은 HTTP long-polling으로 대체됩니다. 이 기능은 WebSocket에 대한 브라우저의 지원이 부족하거나 없을 때 유용합니다. 사실 현재 대부분의 브라우저가 WebSocket을 지원(97% 이상)합니다. 그렇지만, 일부 WebSocket 연결을 설정할 수 없는 사용자가 있기 때문에 여전히 유용합니다.

 

Automatic reconnection

일부 특정 조건에서 서버와 클라이언트 간의 WebSocket 연결이 중단될 수 있고, 이 경우 양쪽 모두 링크의 끊어진 상태를 인식하지 못합니다. 그렇기 때문에 Socket.IO에는 주기적으로 연결 상태를 확인하는 하트비트 매커니즘이 포함되어 있습니다.

 

Packet buffering

client가 연결 해제되면 패킷이 자동으로 버퍼링되고 다시 연결되면 전송됩니다.

 

다시 말해, 기본적으로 소켓이 연결되지 않은 동안 발생한 모든 이벤트는 다시 연결될 때까지 버퍼링되는 것입니다. 이 기능은 대부분의 경우(재연결 지연이 짧은 경우) 유용합니다. packet buffering이 동작하지 않도록 설정할 수도 있습니다.

 

Acknowledgements

Socket.IO는 이벤트 방식으로 데이터를 보내고 응답을 받습니다. 이때 고전적인 request-response API가 필요할 수도 있습니다. Socket.IO에서는 이 기능을 Acknowledgements라고 합니다. emit()의 마지막 인수로 callback을 추가할 수 있으며 이 콜백은 상대방이 이 이벤트를 인정(acknowledges)하면 호출됩니다.

// Sender
socket.emit("hello", "world", (response) => {
  console.log(response); // "got it"
});



// Receiver
socket.on("hello", (arg, callback) => {
  console.log(arg); // "world"
  callback("got it");
});

 

Broadcasting

server 측에서는 연결된 모든 client 또는 client 하위 집합에 이벤트를 보낼 수 있습니다.

 

모든 client에게 전송

io.emit("hello", "world")

io.emit("hello", "world");

 

이번에 연결된 소켓을 제외한 모든 소켓에게 전송

socket.broadcast.emit("hello", "world");

io.on("connection", (socket) => {
  socket.broadcast.emit("hello", "world");
});

 

특정 소켓 집합(Room)과 연결된 모든 연결된 client에게 전송

io.to("myRoom").emit("world")

// join을 통해 소켓을 myRoom에 등록시킵니다.
io.on("connection", (socket) => {
  socket.join("myRoom");
});



// to를 통해 특정 room을 지칭할 수 있습니다.
io.to("myRoom").emit("hello world");

// 아래처럼 여러 room을 동시에 지칭할 수도 있습니다.
io.to("room1").to("room2").to("room3").emit("some event");

Room은 소켓이 들어가고(join) 나올 수 있는(leave) 임의의 채널입니다. client 하위 집합에 이벤트를 브로드캐스트하는 데 사용할 수 있습니다.

 

Multiplexing (= Namespaces)

namespace

Namespaces를 사용하면 단일 공유 연결을 통해 애플리케이션의 로직을 분할할 수 있습니다.

 

예를 들어, 인증된 사용자만 가입할 수 있는 "관리자" 채널을 만들려는 경우 유용할 수 있습니다.

io.on("connection", (socket) => {
  // classic users
});

io.of("/admin").on("connection", (socket) => {
  // admin users
});

 

일반적인 질문

오늘날에도 Socket.IO가 필요합니까?

현재 거의 모든 곳에서 WebSocket이 지원되기 때문에 굉장히 합리적인 질문입니다. 하지만, 일반 WebSocket을 사용하는 경우 결국 reconnection, acknowledgements, broadcasting과 같이 Socket.IO에 이미 포함된 대부분의 기능을 직접 구현해야 할 것입니다.

 

Socket.IO의 overhead는 무엇입니까?

socket.emit("hello", "world")는 아래와 함께 42["hello", "world"]를 포함하는 단일 WebSocket 프레임으로 전송됩니다.

 

  - 4는 Engine.IO "message" packet type입니다.

  - 2는 Socket.IO "message" packet type입니다.

  - ["hello", "world"]는 arguments array의 JSON.stringify() 버전입니다.

 

작동 원리

Socket.IO 서버(Node.js)와 Socket.IO 클라이언트(브라우저, Node.js 또는 다른 프로그래밍 언어) 간의 양방향 채널은 가능한 WebSocket 연결로 설정되며 HTTP long-polling을 fallback(websocket 연결 안 되면 HTTP long-polling 사용)으로 사용합니다.

 

Socket.IO 코드베이스는 두 개의 구분된 계층으로 나뉩니다.

  - 저수준 배관: Engine.IO라고 부릅니다. Socket.IO의 내부 엔진입니다.

  - 고수준 API: Socket.IO 자체

 

Engine.IO

Engine.IO는 서버와 클라이언트 간의 저수준 연결을 담당하며 다음을 처리합니다.

  - 다양한 전송(Websocket 혹은 HTTP long-polling)업그레이드 매커니즘

  - 단선 감지(disconnection detection)

 

Socket.IO는 WebSocket 혹은 HTTP long-polling을 통해 양방향 통신을 구현합니다.(다양한 전송)

 

WebSocket은 분명히 양방향 통신을 설정하는 가장 좋은 방법이지만 경험에 따르면 기업 프록시, 개인 방화벽, 바이러스 백신 소프트웨어로 인해 WebSocket을 설정하는 것이 항상 가능한 것은 아닙니다. 사용자 관점에서 실패한 WebSocket 연결은 실시간 애플리케이션이 데이터 교환을 시작할 때까지 최대 10초 동안 기다려야 할 수 있습니다. 이것은 사용자 경험을 손상시킵니다. 따라서 Socket.IO는 이를 방지하기 위해 아래와 같은 순서로 수행되는 업그레이드 매커니즘이 있습니다.

  1. 핸드셰이크

  2. send data (HTTP long-polling)

  3. receive data (HTTP long-polling)

  4. upgrade (WebSocket)

  5. receive data (HTTP long-polling, 4의 WebSocket 연결이 성공적으로 설정되면 HTTP long-polling은 닫힙니다.)

 

Engine.IO 연결은 다음과 같은 경우 닫힌 것으로 간주됩니다. (단선 감지)

  - 하나의 HTTP 요청(GET 혹은 POST) 실패

  - WebSocket 연결이 닫힌 경우 (예, 사용자가 브라우저에서 탭을 닫을 때)

  - socket.disconnect() 호출

 

Socket.IO

Socket.IO는 Engine.IO 연결 위에서 다음과 같은 몇 가지 추가 기능을 제공합니다. 이는 위에서 언급하였습니다.

  - automatic reconnection

  - packet buffering

  - acknowledgements

  - broadcasting to all clients or to subset of clients

  - multiplexing (=Namespace)

 

 

 

Server-side에서 Socket.IO 사용하기

우선 Socket.IO를 사용하려면 Socket.IO를 install해야 합니다. node.js가 시스템에 설치되어 있다는 가정하에 진행하겠습니다.

 

Socket.IO는 다음과 같이 설치할 수 있습니다.

// 가장 최신 버전 install
npm install socket.io

// 특정 버전 install
npm install socket.io@버전

 

초기화

Socket.IO 서버 라이브러리를 설치했으면 이제 서버를 초기화할 수 있습니다. 아래 외에도 다양한 환경에서의 초기화를 확인하려면 이 링크를 방문해 주세요.

 

Standalone

// port number 따로 전달하기
import { Server } from "socket.io";

const io = new Server({ /* options */ });

io.on("connection", (socket) => {
  // ...
});

io.listen(3000);



// port number를 첫 번째 인수로 전달하기
import { Server } from "socket.io";

const io = new Server(3000, { /* options */ });

io.on("connection", (socket) => {
  // ...
});

 

이렇게 하면 io.httpserver를 통해 액세스할 수 있는 Node.js HTTP 서버가 암시적으로 시작됩니다.

 

With an HTTP server

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

const httpServer = createServer();
const io = new Server(httpServer, { /* options */ });

io.on("connection", (socket) => {
  // ...
});

httpServer.listen(3000);

 

With an HTTPS server

import { readFileSync } from "fs";
import { createServer } from "https";
import { Server } from "socket.io";

const httpsServer = createServer({
  key: readFileSync("/path/to/my/key.pem"),
  cert: readFileSync("/path/to/my/cert.pem")
});

const io = new Server(httpsServer, { /* options */ });

io.on("connection", (socket) => {
  // ...
});

httpsServer.listen(3000);

 

서버 인스턴스

서버 인스턴스(위 코드 예제에서 'io'라고 지칭한 것)에는 애플리케이션에서 사용할 수 있는 몇 가지 attributes가 있습니다. 어떤 attributes가 있는지 배워봅시다.

 

Server#engine 

// 연결된 client 수 가져오기
const count = io.engine.clientsCount;

// 사용에 따라 기본 네임스페이스의 소켓 인스턴스 수와 유사하거나 유사하지 않을 수 있습니다.
const count2 = io.of("/").sockets.size;



// sid 생성하기
const uuid = require("uuid");

io.engine.generateId = (req) => {
  return uuid.v4(); // 전체 Socket.IO 서버에 걸쳐 유니크해야 합니다.
}

위에서 배운 Engine.IO 서버에 대한 참조입니다. 현재 연결된 클라이언트 수를 가져오는 데 사용할 수 있으며, 사용자 정의 세션 ID(sid 쿼리 매개변수)를 생성하는 데 사용할 수도 있습니다.

 

socket.io@4.1.0부터 Engine.IO 서버는 세 가지 특수 이벤트를 emits합니다.

 

initial_headers

io.engine.on("initial_headers", (headers, req) => {
  headers["test"] = "123";
  headers["set-cookie"] = "mycookie=456";
});

세션의 첫 번째 HTTP 요청(핸드셰이크)의 응답 헤더를 작성하기 직전에 emit되어 해당 응답 헤더에 custom header를 추가할 수 있도록 돕습니다.

 

headers

io.engine.on("headers", (headers, req) => {
  headers["test"] = "789";
});

세션의 각 HTTP 요청(WebSocket 업그레이드 포함)의 응답 헤더를 작성하기 직전에 방출되어 각 HTTP 응답 헤더에 custom header를 추가할 수 있도록 돕습니다.

 

connection_error

io.engine.on("connection_error", (err) => {
  console.log(err.req);      // the request object
  console.log(err.code);     // the error code, for example 1
  console.log(err.message);  // the error message, for example "Session ID unknown"
  console.log(err.context);  // some additional error context
});

연결이 비정상적으로 닫히면 emit됩니다. 아래는 error code 목록입니다.

 

Utility methods

Socket.io v4.0.0에는 Socket 인스턴스와 해당 room을 관리하기 위해 아래와 같은 몇 가지 유틸리티 메서드가 추가되었습니다. serverSideEmit 메서드는 v4.1.0부터 추가되었습니다.

 

socketJoin

// 모든 socket 인스턴스들을 "room1"이라는 room에 join 시킵니다.
io.socketsJoin("room1");

// "room1" room에 있는 모든 socket 인스턴스들을 "room2", "room3"라는 room에 join 시킵니다.
io.in("room1").socketsJoin(["room2", "room3"]);

// "admin" 네임스페이스의 "room1" room에 있는 모든 socket 인스턴스들을 "room2" room에 join 시킵니다.
io.of("/admin").in("room1").socketsJoin("room2");

// socketID에 해당되는 단일 socket을 "room1" room에 join 시킵니다.
io.in(theSocketId).socketsJoin("room1");

일치하는 소켓 인스턴스가 지정된 room에 참여하도록 합니다.

 

socketLeave

// 모든 소켓 인스턴스들을 "room1" room에서 나가도록 합니다.
io.socketsLeave("room1");

// "room1" room의 모든 소켓 인스턴스가 "room2", "room3" room을 나가도록 합니다.
io.in("room1").socketsLeave(["room2", "room3"]);

// "admin" 네임스페이스의 "room1" 방에 있는 모든 소켓 인스턴스가 "room2" room을 나가도록 합니다.
io.of("/admin").in("room1").socketsLeave("room2");

// socketID에 해당되는 단일 소켓이 "room1" room을 나가도록 합니다.
io.in(theSocketId).socketsLeave("room1");

일치하는 소켓 인스턴스가 지정된 room을 나가도록 합니다.

 

disconnectSockets

// 모든 소켓 인스턴스 연결 해제
io.disconnectSockets();

// "room1" room의 모든 소켓 인스턴스를 연결 해제하고 저수준 연결을 버립니다.
io.in("room1").disconnectSockets(true);

// "admin" 네임스페이스의 "room1" room에 있는 모든 소켓 인스턴스 연결을 끊습니다.
io.of("/admin").in("room1").disconnectSockets();

// socketID에 해당되는 단일 소켓 인스턴스의 연결을 끊습니다.
io.of("/admin").in(theSocketId).disconnectSockets();

일치하는 소켓 인스턴스의 연결을 끊습니다.

 

fetchSockets

// 메인 네임스페이스의 모든 소켓 인스턴스를 반환합니다.
const sockets = await io.fetchSockets();

// 메인 네임스페이스의 "room1" room에 있는 모든 소켓 인스턴스를 반환합니다.
const sockets = await io.in("room1").fetchSockets();

// "admin" 네임스페이스의 "room1" room에 있는 모든 소켓 인스턴스를 반환합니다.
const sockets = await io.of("/admin").in("room1").fetchSockets();

// 단일 소켓 ID로도 작동합니다.
const sockets = await io.in(theSocketId).fetchSockets();

일치하는 소켓 인스턴스를 반환합니다.

 

위의 예에서 sockets 변수는 일반적인 Socket 클래스의 하위 집합을 노출하는 객체의 배열입니다. 

for (const socket of sockets) {
  console.log(socket.id);
  console.log(socket.handshake);
  console.log(socket.rooms);
  console.log(socket.data);
  socket.emit(/* ... */);
  socket.join(/* ... */);
  socket.leave(/* ... */);
  socket.disconnect(/* ... */);
}

 

data 속성은 Socket.IO 서버 간에 정보를 공유하는 데 사용할 수 있는 임의의 개체입니다.

// server A
io.on("connection", (socket) => {
  socket.data.username = "alice";
});

// server B
const sockets = await io.fetchSockets();
console.log(sockets[0].data.username); // "alice"

 

serverSideEmit

// 보내는 쪽
io.serverSideEmit("hello", "world");

// 받는 쪽
io.on("hello", (arg1) => {
  console.log(arg1); // prints "world"
});



// Acknowledgements도 지원합니다.
// server A
io.serverSideEmit("ping", (err, responses) => {
  console.log(responses[0]); // prints "pong"
});

// server B
io.on("ping", (cb) => {
  cb("pong");
});

다중 서버 설정에서 클러스터의 다른 Socket.IO 서버로 이벤트를 emit합니다.

 

 

이러한 메서드는 브로드캐스팅과 동일한 semantics를 공유하며 동일한 필터가 적용됩니다. 이는 다음과 같습니다.

io.of("/admin").in("room1").except("room2").local.disconnectSockets();

 

Events

server 인스턴스는 하나의 단일 이벤트를 내보냅니다. 기술적으로는 두 개이지만, connect는 연결의 별칭입니다.

 

Connection

io.on("connection", (socket) => {
  // ...
});

이 이벤트는 새 연결 시 발생합니다. 첫 번째 인수는 Socket 인스턴스입니다. 그 외에 모든 API는 여기에서 찾을 수 있습니다.

 

소켓 인스턴스 (server-side)

socket의 역할

소켓은 클라이언트와 상호 작용하기 위한 기본 클래스입니다. emit, on, once, removeListener와 같은 Node.js EventEmitter의 모든 메서드를 상속합니다. Socket 인스턴스에는 응용 프로그램에서 사용할 수 있는 몇 가지 특성이 있습니다.

 

Socket#id

// server-side
io.on("connection", (socket) => {
  console.log(socket.id); // ojIckSD2jqNzOqIrAGzL
});

// client-side
socket.on("connect", () => {
  console.log(socket.id); // ojIckSD2jqNzOqIrAGzL
});

각각의 새 연결에는 임의의 20자 식별자가 할당됩니다. 이 식별자는 클라이언트 측의 값과 동기화됩니다.

 

Socket#handshake

{
  headers: /* the headers of the initial request */
  query: /* the query params of the initial request */
  auth: /* the authentication payload */
  time: /* the date of creation (as string) */
  issued: /* the date of creation (unix timestamp) */
  url: /* the request URL string */
  address: /* the ip of the client */
  xdomain: /* whether the connection is cross-domain */
  secure: /* whether the connection is secure */
}



{
  "headers": {
    "user-agent": "xxxx",
    "accept": "*/*",
    "host": "example.com",
    "connection": "close"
  },
  "query": {
    "EIO": "4",
    "transport": "polling",
    "t": "NNjNltH"
  },
  "auth": {
    "token": "123"
  },
  "time": "Sun Nov 22 2020 01:33:46 GMT+0100 (Central European Standard Time)",
  "issued": 1606005226969,
  "url": "/socket.io/?EIO=4&transport=polling&t=NNjNltH",
  "address": "::ffff:1.2.3.4",
  "xdomain": false,
  "secure": true
}

이 개체에는 Socket.IO 세션 시작 시 발생하는 핸드셰이크에 대한 세부 정보가 포함되어 있습니다.

 

Socket#rooms

io.on("connection", (socket) => {
  console.log(socket.rooms); // Set { <socket.id> }
  socket.join("room1");
  console.log(socket.rooms); // Set { <socket.id>, "room1" }
});

소켓이 속해있는 room에 대한 참조입니다.

 

Socket#data

// server A
io.on("connection", (socket) => {
  socket.data.username = "alice";
});

// server B
const sockets = await io.fetchSockets();
console.log(sockets[0].data.username); // "alice"

위에서 배운 data 객체입니다. 이는 fetchSockets() 유틸리티 메서드와 함께 사용할 수 있는 임의의 객체입니다. 

 

Socket#conn

io.on("connection", (socket) => {
  console.log("initial transport", socket.conn.transport.name); // "polling"이 출력됨

  socket.conn.once("upgrade", () => {
    // 전송이 업그레이드될 때 호출됩니다. (즉, HTTP long-polling에서 WebSocket으로 업그레이드 될 때)
    console.log("upgraded transport", socket.conn.transport.name); // "websocket"이 출력됨
  });

  socket.conn.on("packet", ({ type, data }) => {
    // 수신된 각 패킷에 대해 호출됨
  });

  socket.conn.on("packetCreate", ({ type, data }) => {
    // 전송된 각 패킷에 대해 호출됨
  });

  socket.conn.on("drain", () => {
    // write 버퍼가 비었을 때 호출됨
  });

  socket.conn.on("close", (reason) => {
    // 기본 연결이 닫혔을 때 호출됨
  });
});

기본 Engine.IO 소켓에 대한 참조입니다.

 

Additional attributes

// in a middleware
io.use(async (socket, next) => {
  try {
    const user = await fetchUser(socket);
    socket.user = user;
  } catch (e) {
    next(new Error("unknown user"));
  }
});

io.on("connection", (socket) => {
  console.log(socket.user);

  // in a listener
  socket.on("set username", (username) => {
    socket.username = username;
  });
});

기존 속성을 덮어쓰지 않는 한 모든 속성을 Socket 인스턴스에 첨부하고 나중에 사용할 수 있습니다.

 

Socket middlewares

socket.use(([event, ...args], next) => {
  // 패킷으로 작업 수행(로깅, 인증, 속도 제한 ...)
  // 마지막에 next()를 호출하는 것을 잊지 마세요.
  next();
});

소켓 미들웨어는 들어오는 각 패킷에 대해 호출된다는 점을 제외하면 일반적인 미들웨어와 매우 유사합니다.

 

io.on("connection", (socket) => {
  socket.use(([event, ...args], next) => {
    if (isUnauthorized(event)) {
      return next(new Error("unauthorized event"));
    }
    next();
  });

  socket.on("error", (err) => {
    if (err && err.message === "unauthorized event") {
      socket.disconnect();
    }
  });
});

next 메서드는 오류 개체와 함께 호출할 수도 있습니다. 이 경우 이벤트는 등록된 이벤트 핸들러에 도달하지 않고 대신 오류 이벤트가 발생합니다.

 

Events 

서버 측에서 Socket 인스턴스는 두 가지 특수 이벤트를 emits합니다.

 

disconnect

io.on("connection", (socket) => {
  socket.on("disconnect", (reason) => {
    // ...
  });
});

이 이벤트는 연결 해제 시 Socket 인스턴스에 의해 시작됩니다. reason 목록은 아래와 같습니다.

 

disconnecting

io.on("connection", (socket) => {
  socket.on("disconnecting", (reason) => {
    for (const room of socket.rooms) {
      if (room !== socket.id) {
        socket.to(room).emit("user has left", socket.id);
      }
    }
  });
});

이 이벤트는 `disconnect`와 유사하지만 Socket#rooms 세트가 아직 비어 있지 않은 경우 조금 더 일찍 시작됩니다.

 

참고로 이러한 이벤트는 connect, connect_error, newListener 및 removeListener와 함께 애플리케이션에서 사용하면 안 되는 특수 이벤트입니다.

// BAD, will throw an error
socket.emit("disconnect");

 

그 외에 모든 API는 여기에서 찾을 수 있습니다.

 

Middlewares

미들웨어 기능은 들어오는 모든 연결에 대해 실행되는 기능입니다. 미들웨어 기능은 아래의 경우 유용할 수 있습니다.

  - logging

  - authentication / authorization

  - rate liiting

 

참고로 이 기능은 연결당 한 번만 실행됩니다. (연결이 여러 HTTP 요청으로 구성된 경우에도)

 

Middleware 등록하기

io.use((socket, next) => {
  if (isValid(socket.request)) {
    next();
  } else {
    next(new Error("invalid"));
  }
});

미들웨어 function은 Socket 인스턴스와 다음으로 등록된 미들웨어 function에 액세스할 수 있습니다.

 

io.use((socket, next) => {
  next();
});

io.use((socket, next) => {
  next(new Error("thou shall not pass"));
});

io.use((socket, next) => {
  // 이전 미들웨어가 오류를 반환했기 때문에 실행되지 않습니다.
  next();
});

여러 미들웨어 function을 등록할 수 있으며 순차적으로 실행됩니다.

 

반드시 next()를 호출해야 합니다. 그렇지 않으면 지정된 시간 초과 후 닫힐 때까지 연결이 중단된 상태로 유지됩니다. 

 

중요 참고 사항: 미들웨어가 실행될 때 Socket 인스턴스는 실제로 연결되지 않습니다. 즉, 결국 연결이 실패하더라도 연결 해제 이벤트(disconnect)가 발생하지 않습니다. 예를 들어 클라이언트가 수동으로 연결을 닫는 경우...

// server-side
io.use((socket, next) => {
  setTimeout(() => {
    // 클라이언트 disconnection 이후에 next가 호출됩니다.
    next();
  }, 1000);

  socket.on("disconnect", () => {
    // 트리거되지 않음
  });
});

io.on("connection", (socket) => {
  // 트리거되지 않음
});



// client-side
const socket = io();
setTimeout(() => {
  socket.disconnect();
}, 500);

 

자격 증명(credentials) 보내기

// 클라이언트 측
// plain object
const socket = io({
  auth: {
    token: "abc"
  }
});

// or with a function
const socket = io({
  auth: (cb) => {
    cb({
      token: "abc"
    });
  }
});



// 서버 측
io.use((socket, next) => {
  const token = socket.handshake.auth.token;
  // ...
});

클라이언트는 auth 옵션을 사용하여 자격 증명을 보낼 수 있습니다. 이러한 자격 증명은 서버 측의 핸드셰이크 개체(Socket#handshake 참조)에서 액세스할 수 있습니다.

 

미들웨어 오류 핸들링

// 서버 측
io.use((socket, next) => {
  const err = new Error("not authorized");
  err.data = { content: "Please retry later" }; // additional details
  next(err);
});

// 클라이언트 측
socket.on("connect_error", (err) => {
  console.log(err instanceof Error); // true
  console.log(err.message); // not authorized
  console.log(err.data); // { content: "Please retry later" }
});

다음 메서드가 Error 객체와 함께 호출되면 연결이 거부되고 connect_error 이벤트를 수신합니다. Error 개체에 추가 세부 정보를 첨부할 수도 있습니다.

 

 

 

 

 


https://socket.io/docs/v4/

 

Introduction | Socket.IO

What Socket.IO is

socket.io

'프로그래밍 기초' 카테고리의 다른 글

Socket.IO 소개 3 - adapter  (0) 2023.07.28
Socket.IO 소개 2 - Event  (0) 2023.07.28
CORS란 (Origin, SOP, CORS, CSP)  (0) 2023.07.19
실시간 통신 - WebSocket이란  (0) 2023.07.18
트랜잭션의 격리성과 격리 수준  (0) 2023.06.27