#0 개요

    3가지 페이지 (room들을 보고 입장하는 페이지, 게임 시작 대기 페이지, 게임 진행 페이지)에서 socket을 유지하고,

    그 외 페이지(로그인, 회원가입, 메인, 공지 등)에서는 socket을 유지하지 않으려고 한다.

     

    최대한 socket을 효율적으로 관리하기 위한 방법으로, 페이지 구조를 변경하고자한다.

     

    #1 SocektProvider 생성

    1 createContext

    // 1. Context 생성
    const SocketContext = createContext<Socket | null>(null);

     

    createContext는 Context 객체를 생성하는 함수이다.

    상위 컴포넌트에서 제공하는 값을 하위 컴포넌트들이 props를 넘겨 받지 않아도 직접 사용할 수 있게 해준다.

     

    나중에 <SocketContext.Provider>로 “공유하고 싶은 값”을 주입하면, 하위 컴포넌트에서는 useContext(SocketContext)로 그 값을 꺼내 쓸 수 있음.

     

    2. useRef

    useRef는 참조(Ref) 객체를 반환하는 훅으로, 값이 재랜더링 사이에서 유지되어야 하지만, 값이 바뀌어도 컴포넌트를 다시 렌더링할 필요가 없을 때 주로 사용한다.

    import React, { createContext, useEffect, useRef } from "react";
    import { io, Socket } from "socket.io-client";
    
    const SOCKET_URL = "http://localhost:3000";
    
    // 1. Context 생성
    export const SocketContext = createContext<Socket | null>(null);
    
    // 2. Provider 컴포넌트
    export const SocketProvider = ({ children }: { children: React.ReactNode }) => {
      const socketRef = useRef<Socket | null>(null);
    
      if (!socketRef.current) {
        socketRef.current = io(SOCKET_URL, {
          // 필요한 옵션들
          transports: ["websocket"],
        });
      }
    
      useEffect(() => {
        // 컴포넌트가 마운트될 때 소켓 연결
        const socket = socketRef.current;
    
        socket?.on("connect", () => {
          console.log("소켓 연결됨:", socket.id);
        });
    
        // 언마운트 시 소켓 연결 해제(필요하다면)
        return () => {
          socket?.disconnect();
        };
      }, []);
    
      return (
        <SocketContext.Provider value={socketRef.current}>
          {children}
        </SocketContext.Provider>
      );
    };

     

    3. hook 생성

    import { useContext } from "react";
    import { SocketContext } from "../components/SocketContext";
    
    export const useSocket = () => {
      const socket = useContext(SocketContext);
      if (!socket) {
        throw new Error("useSocket must be used within a SocketProvider.");
      }
      return socket;
    };

     

     

    4. 적용 결과

    import { Outlet } from "react-router";
    import { SocketProvider } from "../components/SocketContext";
    
    function Game() {
      return (
        <>
          <SocketProvider>
            <Outlet />
          </SocketProvider>
        </>
      );
    }
    
    export default Game;

    +++ 주의!

    useSocket 으로 socket을 반환 받아서 사용해도, 연결이 끊기곤 했다.

     

    이는 root에 strict mode 때문인데, 이 strict mode는 컴포넌트를 두번 랜더링하게 한다.

    그럼 socket이 두번 작동되면서, 첫번째 통신을 보내기 전에(혹은 보내자마자) 소켓이 닫혀버리는 셈.

     

    strict mode component를 주석처리해주면 정상 동작한다.

    #2

     

    • 네이버 블러그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기