이 튜토리얼에서는 실시간 멀티플레이어 Tic-Tac-Toe 게임을 구축해 보겠습니다. Node.js 사용 , Socket.IO 및 Redis . 이 게임을 사용하면 두 명의 플레이어가 서로 다른 브라우저 탭에서 연결하여 교대로 플레이하고 플레이하는 동안 실시간 업데이트를 확인할 수 있습니다. 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.IO 및 Redis . 이 서버는 게임 상태를 관리하고, 플레이어 이동을 처리하며, 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 :이 이벤트는 플레이어가 셀을 클릭할 때 트리거됩니다.
-
검증 :이동하기 전에 셀이 이미 점유되어 있는지 또는 게임이 종료되었는지 확인합니다.
-
게임 상태 업데이트 중 :이동이 유효하면 보드를 업데이트하고 차례를 전환합니다.
-
-
업데이트된 게임 상태는 다음과 같습니다:
-
Redis에 게시됨 :이렇게 하면 서버의 모든 인스턴스가 동기화 상태로 유지됩니다.
-
모든 고객에게 방송 :모든 플레이어의 게임판이 즉시 업데이트됩니다.
-
게임 재시작을 처리하기 위해 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명 이상의 사람들이 개발자로 취업하는 데 도움을 주었습니다. 시작하세요