Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Node.js, Socket.IO 및 Redis를 사용하여 확장 가능한 실시간 멀티플레이어 Tic‑Tac‑Toe 게임 만들기

Node.js, Socket.IO 및 Redis를 사용하여 확장 가능한 실시간 멀티플레이어 Tic‑Tac‑Toe 게임 만들기

이 튜토리얼에서는 실시간 멀티플레이어 Tic-Tac-Toe 게임을 구축해 보겠습니다. Node.js 사용 , Socket.IORedis . 이 게임을 사용하면 두 명의 플레이어가 서로 다른 브라우저 탭에서 연결하여 교대로 플레이하고 플레이하는 동안 실시간 업데이트를 확인할 수 있습니다. Redis를 사용하겠습니다. 여러 WebSocket 서버에서 게임 상태 동기화를 관리하여 애플리케이션을 확장 가능하게 만듭니다.

결국에는 실시간 기능을 갖추고 WebSocket 및 Redis를 사용하여 확장 가능한 실시간 애플리케이션을 구축하는 방법에 대한 확실한 이해를 갖춘 완전한 기능의 게임을 갖게 될 것입니다.

배우게 될 내용

  • Socket.IO 사용 방법 실시간 소통을 위해.

  • Redis Pub/Sub 사용 방법 여러 클라이언트에서 게임 상태를 동기화합니다.

  • 확장 가능한 WebSocket 서버 아키텍처를 설정하는 방법.

전제조건

시작하기 전에 다음이 설치되어 있는지 확인하세요:

  • Node.js(v16 이상)

  • 레디스

  • Docker(선택 사항, 컨테이너에서 Redis를 실행하기 위한 경우)

  • JavaScript, Node.js 및 WebSocket에 대한 기본 지식

목차

  • 프로젝트 개요

  • 1단계:개발 환경 설정

  • 2단계:프로젝트 설정

  • 3단계:Redis로 WebSocket 서버 구현

  • 4단계:React 프론트엔드 인터페이스 구현

  • 5단계:애플리케이션 실행

  • 6단계:실시간으로 Redis 메시지 보기

  • 데모

  • 결론

프로젝트 개요

우리는 다음 기능을 갖춘 실시간 Tic-Tac-Toe 게임을 구축할 것입니다:

  • 두 명의 플레이어 연결해서 게임을 할 수 있습니다.

  • 게임 보드는 다양한 브라우저 탭에서 실시간으로 업데이트됩니다.

  • 보드가 가득 차면 게임에서 승자를 발표하거나 무승부를 선언합니다.

우리는 다음을 사용할 것입니다:

  • Node.js Socket.IO 사용 WebSocket 연결을 처리하기 위한 것입니다.

  • 레디스 클라이언트 간 게임 상태 동기화를 관리하는 Pub/Sub.

1단계:개발 환경 설정

Node.js 설치

시스템에 Node.js가 설치되어 있는지 확인하세요:

node -v

아직 설치되어 있지 않다면 Node.js에서 다운로드하세요.

Redis 설치

Redis를 로컬에 설치하거나 Docker 컨테이너에서 실행할 수 있습니다.

macOS(Homebrew 사용)

먼저 아래 명령을 실행하기 전에 시스템에 Homebrew가 설치되어 있는지 확인하세요.

brew install redis
brew services start redis

다음 명령을 사용하여 Redis 컨테이너가 실행 중인지 확인하세요.

redis-cli ping

다음 내용을 확인하세요:

PONG

Docker를 사용하여 Redis 실행

docker run --name redis-server -p 6379:6379 -d redis

다음을 사용하여 Redis가 실행 중인지 확인하세요:

docker exec -it redis-server redis-cli ping

2단계:프로젝트 설정

1. 프로젝트 디렉토리 생성

mkdir tic-tac-toe
cd tic-tac-toe
npm init -y

2. 종속성 설치

npm install express socket.io redis dotenv

3. 환경 변수 생성

.env 만들기 파일을 프로젝트 루트에 다음 내용으로 저장하세요:

PORT=3000
REDIS_HOST=localhost
REDIS_PORT=6379

3단계:Redis로 WebSocket 서버 구현

이 단계에서는 Node.js를 사용하여 실시간 게임 상호작용을 처리하는 WebSocket 서버를 설정합니다. , Socket.IORedis . 이 서버는 게임 상태를 관리하고, 플레이어 이동을 처리하며, Redis Pub/Sub를 사용하여 여러 클라이언트 간의 동기화를 보장합니다.

코드의 각 섹션을 분석하여 모든 것이 어떻게 조화를 이루는지 정확히 이해할 수 있도록 하겠습니다.서버 코드 설명

server.js이라는 파일을 만듭니다. 그리고 다음 코드를 추가하세요:

import dotenv from 'dotenv';
import express from 'express';
import http from 'http';
import { Server } from 'socket.io';
import { createClient } from 'redis';
dotenv.config(); // Load environment variables from .env file
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
 cors: {
 origin: "http://localhost:5173",
 methods: ["GET", "POST"],
 }
});
  • 도텐브 :.env에서 환경 변수를 로드합니다. 포트나 키와 같은 민감한 정보를 안전하게 보관하기 위한 파일입니다.

  • 표현 :HTTP 요청을 처리하기 위한 기본 Express 서버를 설정합니다.

  • http :Node에 내장된 http을 사용하여 HTTP 서버를 만듭니다. Socket.IO와 함께 사용할 모듈 WebSocket 통신을 위한 것입니다.

  • 소켓.IO :이 라이브러리는 서버와 클라이언트 간의 실시간 양방향 통신을 가능하게 합니다.

  • CORS 구성 :localhost:5173에서 실행되는 프런트엔드의 교차 출처 요청을 허용합니다. .

그런 다음 Redis 게시자 및 구독자 클라이언트를 생성하기 위해 server.js에 다음 코드를 추가합니다. :

// Initialize Redis clients
const pubClient = createClient();
const subClient = createClient();
await pubClient.connect();
await subClient.connect();

우리는 Redis를 사용합니다. 연결된 클라이언트 간의 실시간 데이터 동기화를 처리합니다.

  • pubClient :메시지를 게시하는 데 사용됩니다(예:게임 상태 업데이트).

  • 하위 클라이언트 :메시지를 구독합니다(업데이트 수신).

  • 연결() :Redis 서버에 연결을 설정합니다.

이 패러다임에서는 한 클라이언트는 업데이트를 게시하는 데 사용되고 다른 클라이언트는 업데이트를 구독합니다. 이는 구독의 Redis 클라이언트이므로 차단 동작을 방지하는 데 도움이 됩니다. 모드는 메시지 수신만 가능합니다.

게임 업데이트를 위해 Redis 채널을 구독하려면 server.js에 다음 코드를 추가하겠습니다. :

// Subscribe to the Redis channel for game updates
await subClient.subscribe('game-moves', (message) => {
 gameState = JSON.parse(message);
 io.emit('gameState', gameState);
});
  • subClient.subscribe :game-moves에서 메시지를 수신합니다. 채널.

  • 플레이어가 새로운 움직임을 보일 때마다 Redis에서 게임 상태가 업데이트되고 연결된 모든 클라이언트에 새로운 상태가 통보됩니다.

  • message 매개변수에는 게임 상태가 문자열로 포함되어 있습니다. 이를 JavaScript 객체로 파싱하고 Socket.IO를 사용하여 업데이트된 상태를 브로드캐스트합니다. .

다음으로 게임 상태와 기능을 정의하기 위해 server.js에 다음 코드를 추가하겠습니다. :

// Define initial game state
let gameState = {
 board: Array(9).fill(null),
 xIsNext: true,
};
// Function to reset the game
function resetGame() {
 gameState = {
 board: Array(9).fill(null),
 xIsNext: true,
 };
}
  • gameState :보드의 현재 상태와 차례가 누구인지 추적합니다(xIsNext ).

    • 보드는 9개 셀의 배열로 표시됩니다(각 셀은 'X', 'O' 또는 null일 수 있음). ).

    • xIsNext 깃발은 어느 플레이어의 차례인지를 결정합니다.

  • resetGame() :보드와 방향 지시등을 초기 상태로 재설정하여 새로운 게임을 시작할 수 있도록 합니다.

다음으로 WebSocket 연결을 처리하기 위해 server.js에 다음 코드를 추가해 보겠습니다. :

io.on('connection', (socket) => {
 console.log('New client connected:', socket.id);
 // Send the current game state to the newly connected client
 socket.emit('gameState', gameState);
  • io.on('connection') 새 클라이언트가 연결되면 이벤트가 트리거됩니다.

  • 소켓.ID :연결된 각 클라이언트의 고유 식별자입니다.

  • 현재 gameState를 즉시 보냅니다. 새 클라이언트에게 현재 보드를 볼 수 있도록 합니다.

플레이어 이동을 처리하기 위해 server.js에 다음 코드를 추가합니다. :

 // Handle player moves
 socket.on('makeMove', (index) => {
 // Prevent making a move if cell is already taken or game is over
 if (gameState.board[index] || calculateWinner(gameState.board)) return;
 // Update the board and switch turns
 gameState.board[index] = gameState.xIsNext ? 'X' : 'O';
 gameState.xIsNext = !gameState.xIsNext;
 // Publish the updated game state to Redis
 pubClient.publish('game-moves', JSON.stringify(gameState));
 io.emit('gameState', gameState);
 });
  • makeMove :이 이벤트는 플레이어가 셀을 클릭할 때 트리거됩니다.

    • 검증 :이동하기 전에 셀이 이미 점유되어 있는지 또는 게임이 종료되었는지 확인합니다.

    • 게임 상태 업데이트 중 :이동이 유효하면 보드를 업데이트하고 차례를 전환합니다.

  • 업데이트된 게임 상태는 다음과 같습니다:

    1. Redis에 게시됨 :이렇게 하면 서버의 모든 인스턴스가 동기화 상태로 유지됩니다.

    2. 모든 고객에게 방송 :모든 플레이어의 게임판이 즉시 업데이트됩니다.

게임 재시작을 처리하기 위해 server.js에 다음 코드를 추가합니다. :

// Handle game restarts
socket.on('restartGame', () => {
 resetGame();
 io.emit('gameState', gameState);
});

클라이언트 연결 끊김 처리를 처리하기 위해 server.js에 다음 코드를 추가합니다. :

 socket.on('disconnect', () => {
 console.log('Client disconnected:', socket.id);
 });
});

마지막으로 게임의 로직을 처리하기 위해 server.js에 다음 함수를 추가하겠습니다. :

// Function to check if there's a winner
function calculateWinner(board) {
 const lines = [
 [0, 1, 2], [3, 4, 5], [6, 7, 8],
 [0, 3, 6], [1, 4, 7], [2, 5, 8],
 [0, 4, 8], [2, 4, 6]
 ];
 for (let [a, b, c] of lines) {
 if (board[a] && board[a] === board[b] && board[a] === board[c]) {
 return board[a];
 }
 }
 return null;
}
function isBoardFull(board) {
 return board.every((cell) => cell !== null);
}
  • calculateWinner() :보드에 승리 조합이 있는지 확인합니다.

  • isBoardFull() :모든 셀이 채워졌는지 확인하여 무승부를 나타냅니다.

4단계:React 프런트엔드 인터페이스 구현

이 단계에서는 Tic-Tac-Toe 게임을 위한 간단하고 대화형 React 프런트엔드를 구축합니다. 이 프런트엔드를 통해 플레이어는 WebSocket 서버에 연결하고, 이동하고, 게임 보드 업데이트를 실시간으로 확인할 수 있습니다.

App.jsx에서 , 다음 코드를 추가하세요:

import React, { useEffect, useState } from 'react';
import io from 'socket.io-client';
const socket = io('http://localhost:3000');
function App() {
 const [gameState, setGameState] = useState({
 board: Array(9).fill(null),
 xIsNext: true,
 winner: null
 });
 useEffect(() => {
 socket.on('gameState', (state) => {
 setGameState(state);
 });
 return () => socket.off('gameState');
 }, []);
 const handleClick = (index) => {
 if (gameState.board[index] || gameState.winner) return;
 socket.emit('makeMove', index);
 };
 const renderCell = (index) => (
 <button onClick={() => handleClick(index)}>{gameState.board[index]}</button>
 );
 return (
 <div>
 <h1>Multiplayer Tic-Tac-Toe</h1>
 <div className="board">
 {[...Array(9)].map((_, i) => renderCell(i))}
 </div>
 <button onClick={() => socket.emit('restartGame')}>Restart Game</button>
 </div>
 );
}
export default App;

다음은 React 앱이 어떻게 분류되는지 요약한 것입니다:

  • 웹소켓 연결 :

    • 프런트엔드는 socket.io-client를 사용하여 서버에 대한 연결을 설정합니다. .
  • 상태 관리 :

    • 게임 상태(gameState )은 React의 useState로 관리됩니다. 다음을 포함합니다:

      • 보드 (9셀).

      • xIsNext 플래그 현재 플레이어의 차례를 나타냅니다.

      • 승자 상태입니다.

  • 실시간 업데이트 :

    • useEffect 후크:

      • gameState를 듣습니다. 서버에서 업데이트됩니다.

      • 변경 사항이 감지되면 로컬 게임 상태를 업데이트합니다.

      • 구성 요소가 마운트 해제되면 WebSocket 수신기를 정리합니다.

  • 플레이어 이동 처리 :

    • handleClick 기능:

      • 이동을 허용하기 전에 셀이 이미 점유되어 있는지 또는 게임에 승자가 있는지 확인합니다.

      • makeMove을 보냅니다. 클릭한 셀 인덱스와 함께 서버에 이벤트를 보냅니다.

  • 게임 보드 렌더링 :

    • renderCell 함수는 보드의 각 셀에 대한 버튼을 생성합니다.

    • 보드는 3x3 그리드를 사용하여 표시됩니다.

  • 게임 다시 시작 :

    • '게임 다시 시작' 버튼을 누르면 restartGame이 표시됩니다. 모든 플레이어의 게임 보드를 재설정하는 이벤트입니다.
  • 사용자 인터페이스 :

    • 플레이어가 차례대로 돌아가며 실시간으로 업데이트를 확인할 수 있는 간단하고 대화형 레이아웃입니다.

5단계:애플리케이션 실행

백엔드 시작

백엔드 서버를 시작하려면 새 터미널 창을 열고 다음 명령을 실행하세요:

cd tic-tac-toe
npm start

프런트엔드 시작

React 프런트엔드 서버를 시작하려면 새 터미널 창을 열고 아래 명령을 실행하세요. (게임을 실행하려면 두 가지를 동시에 실행해야 하므로 백엔드 서버가 실행 중인 것과 동일한 명령을 사용하지 마세요.)

cd tic-tac-toe-client
npm run dev

게임 접속

브라우저를 열고 다음으로 이동하세요:

http://localhost:5173

6단계:실시간으로 Redis 메시지 보기

게임이 실행되는 동안 Redis 메시지를 보고 실시간 게임 상태 업데이트를 확인할 수 있습니다.

터미널을 열고 다음을 실행하세요:

redis-cli
SUBSCRIBE game-moves

그러면 게임 업데이트가 표시됩니다:

1) "message"
2) "game-moves"
3) "{\"board\":[\"X\",null,\"O\",null,\"X\",null,null,null,null],\"xIsNext\":false}"

이동이 이루어지거나 게임 상태가 변경될 때마다 서버는 업데이트된 게임 상태를 game-moves에 게시합니다. 채널. redis-cli 사용 , 게임이 진행되는 동안 이러한 업데이트를 실시간으로 모니터링할 수 있습니다.

데모

이 데모에서는 Tic Tac Toe 게임이 로컬에서 실행되어 플레이어가 차례대로 실시간 업데이트를 보여주는 것을 볼 수 있습니다.

게임플레이에서는 턴 전환, 보드 업데이트, 게임 상태 발표(승자 또는 무승부)와 같은 기능을 선보입니다. 이는 게임이 WebSocket 통신을 활용하여 원활한 대화형 경험을 제공하는 방법을 강조합니다.

결론

축하합니다. Node.js, Socket.IO 및 Redis를 사용하여 실시간 멀티플레이어 Tic-Tac-Toe 게임을 성공적으로 구축했습니다. 여러분이 배운 내용은 다음과 같습니다:

  • Socket.IO를 사용한 실시간 WebSocket 통신 .

  • Redis Pub/Sub를 사용한 게임 상태 관리 .

  • React를 사용하여 반응형 프런트엔드 구축 .

다음 단계

  • 플레이어 인증을 추가합니다.

  • 채팅 기능을 구현하세요.

  • 확장성을 위해 애플리케이션을 클라우드 제공업체에 배포하세요.

즐거운 코딩 되세요!

무료로 코딩을 배우세요. freeCodeCamp의 오픈 소스 커리큘럼은 40,000명 이상의 사람들이 개발자로 취업하는 데 도움을 주었습니다. 시작하세요