Skip to content

๐Ÿชต 6. ์›น์†Œ์ผ“ ํด๋ผ์ด์–ธํŠธ ๊ตฌํ˜„๊ธฐ: React ํ™˜๊ฒฝ์—์„œ ํšจ์œจ์ ์ธ ์›น์†Œ์ผ“ ์•„ํ‚คํ…์ฒ˜

Taeyeon Yoon edited this page Dec 5, 2024 · 1 revision

1. ๋ฌธ์ œ ์ƒํ™ฉ๊ณผ ๋„์ „ ๊ณผ์ œ

WebSocket๊ณผ React์˜ ์ถฉ๋Œ

React์˜ ์„ ์–ธ์ ์ด๊ณ  ๋‹จ๋ฐฉํ–ฅ์ ์ธ ๋ฐ์ดํ„ฐ ํ๋ฆ„์€ ์‹ค์‹œ๊ฐ„ ์–‘๋ฐฉํ–ฅ ํ†ต์‹ ์ด ํ•„์š”ํ•œ WebSocket๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๊ตฌ์ฒด์ ์ธ ๋ฌธ์ œ์ ๋“ค์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1.1. React์˜ ์ƒ๋ช…์ฃผ๊ธฐ์™€์˜ ๋ถˆ์ผ์น˜

// โŒ ์ผ๋ฐ˜์ ์œผ๋กœ ์ž์ฃผ ๋ณด์ด๋Š” ์ž˜๋ชป๋œ ๊ตฌํ˜„
const Component = () => {
  useEffect(() => {
    const socket = io('server-url');
    socket.on('event', handleEvent);

    return () => socket.disconnect();
  }, []);
  // ๋ฌธ์ œ์ :
  // 1. ์ปดํฌ๋„ŒํŠธ ๋งˆ์šดํŠธ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์—ฐ๊ฒฐ ์ƒ์„ฑ
  // 2. ํŽ˜์ด์ง€ ์ „ํ™˜์‹œ ์—ฐ๊ฒฐ ๋Š๊น€
  // 3. ๋‹ค์ˆ˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ค‘๋ณต ์—ฐ๊ฒฐ ๊ฐ€๋Šฅ์„ฑ
};

1.2. ์ƒํƒœ ๊ด€๋ฆฌ์˜ ๋ณต์žก์„ฑ

// โŒ Context API๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ์˜ ๋ฌธ์ œ์ 
const SocketProvider = ({ children }) => {
  const [gameState, setGameState] = useState({});
  const [chatState, setChatState] = useState({});
  const [drawingState, setDrawingState] = useState({});

  useEffect(() => {
    socket.on('gameUpdate', setGameState);
    socket.on('chatUpdate', setChatState);
    socket.on('drawingUpdate', setDrawingState);
    // ๋ฌธ์ œ์ :
    // 1. Provider๊ฐ€ ๊ฑฐ๋Œ€ํ•ด์ง
    // 2. ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ
    // 3. ์ƒํƒœ ์—…๋ฐ์ดํŠธ ๋กœ์ง์ด ํ•œ ๊ณณ์— ์ง‘์ค‘
  }, []);
};

2. ํ•ด๊ฒฐ ๋ฐฉ์•ˆ: ๊ณ„์ธตํ˜• ์•„ํ‚คํ…์ฒ˜

์œ„ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ 4๊ณ„์ธต ์•„ํ‚คํ…์ฒ˜๋ฅผ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.

graph TD
    A[Socket Config] --> B[Socket Store]
    B --> C[Domain Store]
    C --> D[Custom Hooks]
    C --> E[Handlers]

    style A fill:#f9f,stroke:#333
    style B fill:#bbf,stroke:#333
    style C fill:#bfb,stroke:#333
    style D fill:#fb9,stroke:#333

Loading

์„ค๊ณ„ ์›์น™

  1. ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ

    • Socket ๋กœ์ง์„ 4๊ฐ€์ง€ ํ•ต์‹ฌ ์˜์—ญ์œผ๋กœ ๋ถ„๋ฆฌ
      1. ์ƒํƒœ ๊ด€๋ฆฌ: Store์™€ ์•ก์…˜์„ ํ†ตํ•œ ์ˆœ์ˆ˜ ์ƒํƒœ ๊ด€๋ฆฌ
      2. ์—ฐ๊ฒฐ ๊ด€๋ฆฌ: ์†Œ์ผ“ ์—ฐ๊ฒฐ/์žฌ์—ฐ๊ฒฐ ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ
      3. ์š”์ฒญ ์ฒ˜๋ฆฌ: ์›น์†Œ์ผ“ ์š”์ฒญ ํ•ธ๋“ค๋Ÿฌ ๊ตฌํ˜„
      4. ์‘๋‹ต ์ฒ˜๋ฆฌ: ์›น์†Œ์ผ“ ์ด๋ฒคํŠธ ๊ตฌ๋… ๋ฐ ์ฒ˜๋ฆฌ
    • ๊ฐ ์˜์—ญ์„ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋…๋ฆฝ์ ์ธ ๋ชจ๋“ˆ๋กœ ๊ตฌ์„ฑ
  2. ๋‹จ์ผ ์ฑ…์ž„ ์›์น™

    • Socket Store
    • Domain Store
    • Custom Hooks + Handlers
  3. ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ•œ ์„ค๊ณ„

    enum SocketNamespace {
      GAME = 'game',
      DRAWING = 'drawing',
      CHAT = 'chat',
    }
    • ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ํ†ตํ•œ ๊ธฐ๋Šฅ๋ณ„ ์†Œ์ผ“ ๋ถ„๋ฆฌ
    • ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€๊ฐ€ ์šฉ์ดํ•œ ๋ชจ๋“ˆ๋Ÿฌ ๊ตฌ์กฐ
    • ๊ฐ ๋„๋ฉ”์ธ์˜ ๋…๋ฆฝ์ ์ธ ํ™•์žฅ ๊ฐ€๋Šฅ
    • ๋„๋ฉ”์ธ๋ณ„ ๊ฐœ๋ณ„ ์Šค์ผ€์ผ๋ง ์ง€์›

์ด๋Ÿฌํ•œ ๊ณ„์ธตํ˜• ๊ตฌ์กฐ๋Š” ๋ณต์žกํ•œ ์‹ค์‹œ๊ฐ„ ๊ธฐ๋Šฅ์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค๋ฉฐ, ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€๋‚˜ ๊ธฐ์กด ๊ธฐ๋Šฅ ์ˆ˜์ •์ด ์šฉ์ดํ•œ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

2.1. Socket Config: ๊ธฐ๋ณธ ์„ค์ • ๊ณ„์ธต

์†Œ์ผ“ ์„ค์ •์„ ๋‹ด์€ ๊ณ„์ธต์ž…๋‹ˆ๋‹ค.

// socket.config.ts
export const SOCKET_CONFIG = {
  URL: import.meta.env.VITE_SOCKET_URL || '<http://localhost:3000>',
  PATHS: {
    [SocketNamespace.GAME]: '/game',
    [SocketNamespace.DRAWING]: '/drawing',
    [SocketNamespace.CHAT]: '/chat',
  },
  BASE_OPTIONS: {
    autoConnect: false,
    reconnection: true,
    reconnectionAttempts: 5,
  }
};

2.2. Socket Store: ์ „์—ญ ์—ฐ๊ฒฐ ๊ด€๋ฆฌ ๊ณ„์ธต

์‚ฌ์šฉํ•  ์†Œ์ผ“ ์ธ์Šคํ„ด์Šค์™€ ์—ฐ๊ฒฐ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๋‹ด์•„ ๊ฐ ์†Œ์ผ“์˜ ์ƒํƒœ๋ฅผ ํŒŒ์•…ํ•˜๊ณ , ๊ณตํ†ต ์†Œ์ผ“ ์•ก์…˜์„ ์ •์˜ํ•œ ๊ณ„์ธต์ž…๋‹ˆ๋‹ค.

// socket.store.ts
export const useSocketStore = create<SocketState>((set) => ({
  sockets: {
    [SocketNamespace.GAME]: null,
    [SocketNamespace.DRAWING]: null,
    [SocketNamespace.CHAT]: null,
  },
  connected: {
    [SocketNamespace.GAME]: false,
    [SocketNamespace.DRAWING]: false,
    [SocketNamespace.CHAT]: false,
  },
  actions: {
    connect: (namespace, auth?) => {
      const socket = socketCreators[namespace](auth);
      socket.connect();
      set(state => ({
        sockets: { ...state.sockets, [namespace]: socket }
      }));
    },
    // ...
  }
}));

2.3. Domain Store: ๋„๋ฉ”์ธ๋ณ„ ์ƒํƒœ ๊ด€๋ฆฌ ๊ณ„์ธต

๋„๋ฉ”์ธ๋ณ„ ์ƒํƒœ ๊ด€๋ฆฌ ๊ณ„์ธต์ž…๋‹ˆ๋‹ค. ๊ฒŒ์ž„, ๋“œ๋กœ์ž‰, ์ฑ„ํŒ… ์†Œ์ผ“์˜ ๋ถ€์‚ฐ๋ฌผ์ธ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ณ„์ธต์ž…๋‹ˆ๋‹ค.

// gameSocket.store.ts
export const useGameSocketStore = create<GameState & GameActions>()(
  devtools(
    (set) => ({
      room: null,
      players: [],
      actions: {
        updateRoom: (room) => set({ room }),
        updatePlayers: (players) => set({ players }),
      }
    }),
    { name: 'GameStore' }
  )
);

2.4. Custom Hooks + Handlers: ์ปดํฌ๋„ŒํŠธ ์—ฐ๋™ ๊ณ„์ธต

์—ฐ๊ฒฐ ๋กœ์ง + ์‘๋‹ต ์ด๋ฒคํŠธ ๋“ฑ๋ก์„ ๋‹ด์€ Custom Hooks์™€ ์š”์ฒญ ์ด๋ฒคํŠธ ํ•จ์ˆ˜๋ฅผ ๋‹ด์•„๋‚ธ Handlers๋ฅผ ๊ตฌํ˜„ํ•œ ๊ณ„์ธต์ž…๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ ์ˆ˜์ค€์˜ ์†Œ์ผ“ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•ด ํ˜ธ์ถœ๋งŒ์œผ๋กœ ์ปดํฌ๋„ŒํŠธ์— ์‰ฝ๊ฒŒ ์—ฐ๋™ํ•˜๋„๋ก ๊ณ„์ธต์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

// useGameSocket.ts
export const useGameSocket = () => {
  const { roomId } = useParams();
  const { sockets, connected, actions: socketActions } = useSocketStore();
  const { actions: gameActions } = useGameSocketStore();

  useEffect(() => {
    if (!roomId) return;

    // 1. ์†Œ์ผ“ ์—ฐ๊ฒฐ
    socketActions.connect(SocketNamespace.GAME);

    // 2. ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์„ค์ •
    const handlers = {
      joinedRoom: (response: JoinRoomResponse) => {
        gameActions.updateRoom(response.room);
        gameActions.updatePlayers(response.players);
      },
      // ...
    };

    Object.entries(handlers).forEach(([event, handler]) => {
      socket.on(event, handler);
    });

    return () => {
      // 3. ํด๋ฆฐ์—…
      socketActions.disconnect(SocketNamespace.GAME);
    };
  }, [roomId]);

  return {
    isConnected: connected.game,
    actions: gameActions,
  };
};
import type { JoinRoomRequest, JoinRoomResponse, ReconnectRequest } from '@troublepainter/core';
import { useSocketStore } from '@/stores/socket/socket.store';

// socket ์š”์ฒญ๋งŒ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ
export const gameSocketHandlers = {
  joinRoom: (request: JoinRoomRequest): Promise<JoinRoomResponse> => {
    const socket = useSocketStore.getState().sockets.game;
    if (!socket) throw new Error('Socket not connected');

    return new Promise(() => {
      socket.emit('joinRoom', request);
    });
  },

  reconnect: (request: ReconnectRequest): Promise<void> => {
    const socket = useSocketStore.getState().sockets.game;
    if (!socket) throw new Error('Socket not connected');

    return new Promise(() => {
      socket.emit('reconnect', request);
    });
  },
};

export type GameSocketHandlers = typeof gameSocketHandlers;

3. ์‹ค์ œ ์ ์šฉ: Layout ๊ธฐ๋ฐ˜ ๊ตฌํ˜„

๋ ˆ์ด์•„์›ƒ์— ๊ณ„์ธตํ˜• ์•„ํ‚คํ…์ฒ˜๋ฅผ ์‹ค์ œ๋กœ ์ ์šฉํ•ด ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

// GameLayout.tsx
const GameLayout = () => {
  const { isConnected } = useGameSocket();

  // ์—ฐ๊ฒฐ ์ƒํƒœ์— ๋”ฐ๋ฅธ UI ์ฒ˜๋ฆฌ
  if (!isConnected) {
    return <LoadingSpinner message="์—ฐ๊ฒฐ ์ค‘..." />;
  }

  return (
    <div className="flex min-h-screen flex-col">
      <header>
        <Logo variant="side" />
      </header>

      <main className="mx-auto">
        <div className="flex">
          {/* ํ”Œ๋ ˆ์ด์–ด ๋ชฉ๋ก */}
          <PlayerList />

          {/* ๊ฒŒ์ž„ ์˜์—ญ */}
          <section className="flex-1">
            <Outlet />
          </section>

          {/* ์ฑ„ํŒ… ์˜์—ญ */}
          <Chat />
        </div>
      </main>
    </div>
  );
};
  1. ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ ๋‹จ์ˆœํ™”
    • Layout ์ˆ˜์ค€์—์„œ ์†Œ์ผ“ ์—ฐ๊ฒฐ ๊ด€๋ฆฌ
    • ํ•˜์œ„ ๋ผ์šฐํŠธ ์ „ํ™˜ ์‹œ์—๋„ ์—ฐ๊ฒฐ ์œ ์ง€
  2. ์ƒํƒœ ๊ณต์œ  ์ตœ์ ํ™”
    • ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋งŒ ์ƒํƒœ ๊ตฌ๋…
    • Props drilling ๋ฐฉ์ง€
  3. ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ํ–ฅ์ƒ
    • ์—ฐ๊ฒฐ ์ƒํƒœ์— ๋”ฐ๋ฅธ ์ผ๊ด€๋œ UI
    • ํŽ˜์ด์ง€ ์ „ํ™˜ ์‹œ ๋Š๊น€์—†๋Š” ์‹ค์‹œ๊ฐ„ ๊ธฐ๋Šฅ

4. ๊ฒฐ๋ก  ๋ฐ ์ด์ 

์ด๋Ÿฌํ•œ ์ €๋งŒ์˜ ๊ณ„์ธตํ˜• ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„์˜ ์ฃผ์š” ์ด์ ์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์œ ์ง€๋ณด์ˆ˜์„ฑ: ๊ฐ ๊ณ„์ธต์˜ ์—ญํ• ์ด ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์–ด ์ฝ”๋“œ ๊ด€๋ฆฌ๊ฐ€ ์šฉ์ด
  • ์žฌ์‚ฌ์šฉ์„ฑ: ์ปค์Šคํ…€ ํ›…์„ ํ†ตํ•ด ์›น์†Œ์ผ“ ๋กœ์ง์„ ์‰ฝ๊ฒŒ ์žฌ์‚ฌ์šฉ
  • ํ™•์žฅ์„ฑ: ์ƒˆ๋กœ์šด ์†Œ์ผ“ ๊ธฐ๋Šฅ ์ถ”๊ฐ€๊ฐ€ ์šฉ์ดํ•œ ๊ตฌ์กฐ
  • ํƒ€์ž… ์•ˆ์ „์„ฑ: TypeScript์™€ Socket.IO์˜ ํƒ€์ž… ์‹œ์Šคํ…œ ํ™œ์šฉ

์ด ์•„ํ‚คํ…์ฒ˜๋Š” ์‹ค์‹œ๊ฐ„ ๊ฒŒ์ž„, ์ฑ„ํŒ…, ํ˜‘์—… ๋„๊ตฌ ๋“ฑ WebSocket์ด ํ•„์š”ํ•œ React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ์˜๋„๋ฅผ ๋‹ด์•„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ : Zustand๋ฅผ ํ†ตํ•œ ๊ณ„์ธตํ˜• ์•„ํ‚คํ…์ฒ˜ ๊ตฌํ˜„ ๋ฐฐ๊ฒฝ

์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ ํƒ ๊ณผ์ •

1. ๋กœ์ปฌ ์ƒํƒœ ๊ด€๋ฆฌ์˜ ํ•œ๊ณ„

// โŒ ์ปดํฌ๋„ŒํŠธ ๋‚ด ๋กœ์ปฌ ์ƒํƒœ ๊ด€๋ฆฌ์˜ ๋ฌธ์ œ์ 
const GameRoom = () => {
  const [socket, setSocket] = useState<Socket | null>(null);
  const [gameState, setGameState] = useState({});
  const [connected, setConnected] = useState(false);

  // ๋ฌธ์ œ์ :
  // 1. ์ƒํƒœ ์ „ํŒŒ๋ฅผ ์œ„ํ•œ Props Drilling
  // 2. ์ปดํฌ๋„ŒํŠธ ๊ฐ„ ์ƒํƒœ ๋™๊ธฐํ™” ์–ด๋ ค์›€
  // 3. ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ ์ƒํƒœ ์œ ์‹ค
  return <GameComponent socket={socket} gameState={gameState} />;
};
  • Props Drilling: ์—ฌ๋Ÿฌ ๋‹จ๊ณ„์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฑฐ์ณ ์†Œ์ผ“๊ณผ ์ƒํƒœ๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•จ
  • ์ƒํƒœ ๋™๊ธฐํ™”: ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€์—์„œ ๋™์ผํ•œ ์†Œ์ผ“ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ƒํƒœ ๋™๊ธฐํ™”๊ฐ€ ๋ณต์žกํ•ด์ง
  • ์˜์†์„ฑ: ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ์ด๋‚˜ ๋„ค๋น„๊ฒŒ์ด์…˜ ์‹œ ์ƒํƒœ ์œ ์ง€๊ฐ€ ์–ด๋ ค์›€
  • ๋””๋ฒ„๊น…: ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์ถ”์ ํ•˜๊ณ  ๋””๋ฒ„๊น…ํ•˜๊ธฐ ์–ด๋ ค์›€
  • ํ…Œ์ŠคํŠธ: ์ปดํฌ๋„ŒํŠธ์™€ ์†Œ์ผ“ ๋กœ์ง์ด ๊ฐ•ํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜์–ด ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ค์›€

2. Context API์˜ ํ•œ๊ณ„

// โŒ Context API ์‚ฌ์šฉ ์‹œ์˜ ๋ฌธ์ œ์ 
const SocketProvider = ({ children }) => {
  const [gameState, setGameState] = useState({});

  useEffect(() => {
    // ๋ชจ๋“  ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ•œ ๊ณณ์— ์ง‘์ค‘
    socket.on('gameStart', onGameStart);
    socket.on('playerJoin', onPlayerJoin);
    socket.on('drawing', onDrawing);
    // ... ๊ณ„์†๋˜๋Š” ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋“ค
  }, []);

  // ๋ฌธ์ œ์ :
  // 1. Provider๊ฐ€ ๋น„๋Œ€ํ•ด์ง
  // 2. ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ
  // 3. ๊ธฐ๋Šฅ๋ณ„ ๋ถ„๋ฆฌ๊ฐ€ ์–ด๋ ค์›€
  return (
    <SocketContext.Provider value={{ socket, gameState }}>
      {children}
    </SocketContext.Provider>
  );
};
  • ์ด๋ฒคํŠธ ๊ด€๋ฆฌ ๋ณต์žก์„ฑ: ๋งŽ์€ ์ด๋ฒคํŠธ๋ฅผ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌํ•˜๋ฉด์„œ ์ฝ”๋“œ๊ฐ€ ๋น„๋Œ€ํ•ด์ง
  • ์ƒํƒœ ์—…๋ฐ์ดํŠธ ์ตœ์ ํ™”: Context API ํŠน์„ฑ์ƒ Provider ์•ˆ์—์„œ ํŠน์ • ์ด๋ฒคํŠธ๋กœ ์ธํ•œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ์œ ๋ฐœ
  • ์ฝ”๋“œ ๋ถ„ํ• : ๊ธฐ๋Šฅ๋ณ„๋กœ Context๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉด Provider ์ค‘์ฒฉ์ด ์‹ฌํ•ด์ง

3. Zustand๋ฅผ ์„ ํƒํ•œ ์ด์œ 

  1. ํšจ์œจ์ ์ธ ์ƒํƒœ ๊ตฌ๋… โœ…ย ํ•„์š”ํ•œ ์ƒํƒœ๋งŒ ์„ ํƒ์ ์œผ๋กœ ๊ตฌ๋…

    const Room = () => {
      // ํŠน์ • ์ƒํƒœ๋งŒ ๊ตฌ๋…ํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ๋ฐฉ์ง€
      const room = useGameStore((state) => state.room);
      const updateRoom = useGameStore((state) => state.actions.updateRoom);
    
      useEffect(() => {
        socket.on('roomUpdate', updateRoom);
      }, []);
    };
  2. ๊ณ„์ธตํ˜• ๊ตฌ์กฐ์— ์ ํ•ฉํ•œ API โœ…ย ์Šคํ† ์–ด ๊ฐ„ ์ƒํƒœ ๊ณต์œ  ๋ฐ ์กฐํ•ฉ์ด ์ž์œ ๋กœ์›€

    const useGameStore = create<GameState>()((set, get) => ({
      room: null,
      players: [],
      actions: {
        // ๋‹ค๋ฅธ ์Šคํ† ์–ด์˜ ์ƒํƒœ ์ฐธ์กฐ ๊ฐ€๋Šฅ
        updateRoom: (room) => {
          set({ room });
          get().actions.syncWithSocket(room);
        }
      }
    }));
  3. ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ

    • TypeScript ์ง€์›์ด ์šฐ์ˆ˜
    • DevTools๋ฅผ ํ†ตํ•œ ์ƒํƒœ ๋””๋ฒ„๊น…
    • ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ†ตํ•œ ๊ธฐ๋Šฅ ํ™•์žฅ
  4. ๋ฒˆ๋“ค ํฌ๊ธฐ

    • ์ž‘์€ ๋ฒˆ๋“ค ํฌ๊ธฐ (Redux: ~22KB, MobX: ~16KB, Zustand: ~1KB)
    • ์ตœ์†Œํ•œ์˜ ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ
  5. ํ•™์Šต ๊ณก์„ : โœ… ์ง๊ด€์ ์ธ API

const useStore = create((set) => ({
  socket: null,
  connect: () => set({ socket: io() }),
  disconnect: () => set({ socket: null })
}));

์ด๋Ÿฌํ•œ ์ด์œ ๋กœ Zustand๋ฅผ ์‚ฌ์šฉํ•œ ๊ณ„์ธตํ˜• ์•„ํ‚คํ…์ฒ˜๊ฐ€ WebSocket ํ†ต์‹ ๊ณผ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋Š” ์ตœ์ ์˜ ์„ ํƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ˜Ž ์›จ๋ฒ ๋ฒ ๋ฒ ๋ฒฑ

๐Ÿ‘ฎ๐Ÿป ํŒ€ ๊ทœ์น™

๐Ÿ’ป ํ”„๋กœ์ ํŠธ

๐Ÿชต ์›จ๋ฒ ๋ฒฑ ๊ธฐ์ˆ ๋กœ๊ทธ

๐Ÿช„ ๋ฐ๋ชจ ๊ณต์œ 

๐Ÿ”„ ์Šคํ”„๋ฆฐํŠธ ๊ธฐ๋ก

๐Ÿ“— ํšŒ์˜๋ก

Clone this wiki locally