⌨️ Socket.io

박상은·2022년 6월 23일
0

Polling: 응답할 때까지 지속적으로 주기에 맞게 요청함
Long Polling: 요청을 보내고 응답받을 때까지 연결을 끊지 않고 대기함

🤝 Socket.io

1. 들어가기 전에 알아두면 좋은 키워드

  1. io: 서버 측 객체
  2. socket: 클라이언트 측 객체
  3. emit: 송신 이벤트 발생
  4. on: 수신 이벤트 등록

2. 빌트인 이벤트

  1. connect: 연결될 때 실행되는 이벤트
  2. connection
  3. disconnect: 연결이 끊길 때 실행되는 이벤트
  4. disconnecting

🧐 설치 및 예시 코드

  • socket.io에서 사용할 타입 ( 클라이언트와 서버 모두 공유해서 사용 )
// 여기 정의한 타입은 모두 필요에 의해서 수정해서 사용해야 한다.
type MessageReceiveAndSend = {
  message: string;
};

// 이벤트 수신 시 사용 ( 서버 -> 클라이언트 )
export type ServerToClientEvents = {
  onReceive: ({ message }: MessageReceiveAndSend) => void;
};
// 이벤트 송신 시 사용 ( 클라이언트 -> 서버 )
export type ClientToServerEvents = {
  onSend: ({ message }: MessageReceiveAndSend) => void;
};
// 서버측 socket.io("")
// ?
export type InterServerEvents = {
  ping: () => void;
};
// 서버측 socket.data ( socket 객체들이 공유할 데이터 )
export type SocketData = {
  name: string;
  age: number;
};

1. 서버 측

1.1 설치

npm i express socket.io
npm i -D types/express types/socket.io

2.2 예시 코드

  • app.ts
// ClientToServerEvents, ServerToClientEvents 등은 import 했다고 가정
import express from "express";
import { createServer } from "http";
import { Server } from "socket.io";

const app = express();
app.set("PORT", 8080);

const httpServer = createServer(app);
const io = new Server<
  ClientToServerEvents,
  ServerToClientEvents,
  InterServerEvents,
  SocketData
>(httpServer, {
  // options ...
  cors: {
    origin: "http://localhost:3000",
    methods: ["GET", "POST"],
    credentials: true,
  },
});

// 미들웨어 등록 ( 들어오는 모든 연결 이전에 실행 )
// 여러 개의 미들웨어를 등록할 수 있으며, 이전 미들웨어에서 에러가 발생한다면 이후 미들웨어는 모두 실행하지 않음
io.use((socket, next) => {
  try {
    console.log("미들 웨어 실행!!");
    socket.data.age = 25;

    next();
  } catch (error) {
    console.error("error >> ", error);
    next(new Error("socket.io middleware error"));
  }
});

// 소켓에 연결될 경우 실행
io.on("connection", async (socket) => {
  // 소켓마다 고유의 식별자를 가짐 ( 20자 )
  console.log("소켓 연결 완료 >> ", socket.id);
  // 연결된 소켓의 개수
  console.log("연결 횟수 >> ", socket.engine.clientsCount);
  
  // 소켓이 입장한 모든 방들 이름 ( 소켓 생성 시 각자의 식별자의 이름과 같은 방에 입장된다. ( 개인 메시지 구현에 이용하면 좋음 ) )
  console.log("소켓이 들어간 방 >> ", socket.rooms);	// ["식별자"]
  
  // "room123"방에 입장
  socket.join("room123");
  // "room123"방에서 퇴장
  socket.leave("room123");
  
  // 응답 이벤트 등록... "on( 이벤트명, callback )" 순으로 입력 typescript를 사용한다면 자동 완성 지원
  // ㉮ 클라이언트 측에서 "onSend"라는 응답을 보내면 callback에 전송된 인자가 매개변수로 들어가고 실행
  socket.on("onSend", ({ message }) => {
    console.log(message);
  });
  
  // ㉯ 전송 이벤트 실행... "emit( 이벤트명, 매개변수, 매개변수, ... )" 순으로 입력 typescript를 사용한다면 자동 완성 지원
  // 원래는 특정 이벤트에 대한 응답으로 넣는 것이 일반적이기 때문에 위쪽 ㉮내부에서 사용하는 것이 더 맞는 예시지만 보기 더 편하게 하기 위해서 분리함
  socket.emit("onReceive", { message: "서버에서 클라이언트로 메시지 전달!!!" });
  
  // 응답 이벤트인 "onSend" 등록 해제
  socket.off("onSend", (...args) => console.log(args));
  
  // "room123"에 입장된 클라이언트들에게 "onReceive"이벤트 실행
  socket.in("room123").emit("onReceive", { message: "메시지 전송 by in" });
  socket.to("room123").emit("onReceive", { message: "메시지 전송 by to" });
  
  // 본인을 제외한 클리이턴트들에게 "onReceive"이벤트 실행
  socket.broadcast.emit("onReceive", { message: "본인 제외 메시지 전송 by broadcast" });
  
  // 위의 in/to, broadcase, emit ( + of ) 등을 적절하게 조합해서 사용하면 된다.
});

httpServer.listen(app.get("PORT"), () => console.log(`${app.get("PORT")}번 대기중`));

2. 클라이언트 측

2.1 설치

npm i socket.io-client
npm i -D @types/socket.io-client

2.2 예시 코드

// ClientToServerEvents, ServerToClientEvents는 import했다고 가정
import React, { useEffect, useState } from "react";
import { io, Socket } from "socket.io-client";

function App() {
  // socket을 담을 변수
  const [socket, setSocket] =
    useState<Socket<ServerToClientEvents, ClientToServerEvents>>();

  // 서버와 연결
  useEffect(() => {
    // 연결 및 socket 생성
    const socket: Socket<ServerToClientEvents, ClientToServerEvents> = io(
      "http://localhost:5000",
      {
        withCredentials: true,
      }
    );
    // 연결에 성공 시 실행
    socket.on("connect", () => {
      console.log("클라이언트 연결됨");
    });
    // "onReceive"로 요청오면 실행 ( 서버측의 ㉯로 전송 )
    socket.on("onReceive", ({ message }) => {
      console.log("서버에서 보낸 메시지 >> ", message);
    });

    // socket 등록
    setSocket(socket);
  }, [setSocket]);

  // 전송 버튼 클릭 시 실행
  const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    // "onSend"로 응답 ( 서버측의 ㉮에서 받음 )
    socket?.emit("onSend", { message: "전송" });
  };

  return (
    <main>
      <h1>Socket.io</h1>
      
      <form onSubmit={onSubmit}>
        <button type="submit">전송</button>
      </form>
    </main>
  );
}

export default App;

🙂 추가 정보

예시에서 보여준 것 외에도 많은 유용한 기능들을 구현해놔서 공식 문서 읽어보고 그대로 사용하면 됩니다.

  1. socket.io가 두 번 연결된다면 <React.StrictMode>를 제거하면 됩니다. ( 개발 모드일 경우 오류를 제대로 잡기 위해 두 번 실행됨 )

0개의 댓글