5. 백엔드 서버 구축(지렁이 온라인 게임)


안녕하세요. 온라인 Snake 게임 개발 교육과정의 다섯 번째 시간입니다. 오늘은 Express.js를 이용한 서버 설정과 RESTful API 구현에 대해 알아보겠습니다.


1. Express.js 서버 설정

먼저 기본적인 Express.js 서버를 설정합니다.

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const path = require('path');

const app = express();
const server = http.createServer(app);
const io = socketIo(server);

const PORT = process.env.PORT || 3000;

app.use(express.static(path.join(__dirname, 'public')));

server.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

2. 게임 로직 모듈화

게임 로직을 별도의 모듈로 분리하여 관리합니다.

// gameLogic.js
const GRID_SIZE = 20;
const CANVAS_SIZE = 400;

class Game {
    constructor() {
        this.players = [];
        this.foods = [];
        this.gridSize = GRID_SIZE;
        this.canvasSize = CANVAS_SIZE;
    }

    addPlayer(id) {
        // 플레이어 추가 로직
    }

    removePlayer(id) {
        // 플레이어 제거 로직
    }

    movePlayer(id, direction) {
        // 플레이어 이동 로직
    }

    update() {
        // 게임 상태 업데이트 로직
    }

    // 기타 필요한 메서드들...
}

module.exports = Game;

3. Socket.io 이벤트 핸들링

서버에서 Socket.io 이벤트를 처리합니다.

const Game = require('./gameLogic');

const games = new Map();

io.on('connection', (socket) => {
    console.log('A user connected');

    socket.on('createRoom', () => {
        const roomCode = generateRoomCode();
        games.set(roomCode, new Game());
        socket.join(roomCode);
        socket.emit('roomCreated', roomCode);
    });

    socket.on('joinRoom', (roomCode) => {
        if (games.has(roomCode)) {
            socket.join(roomCode);
            const game = games.get(roomCode);
            game.addPlayer(socket.id);
            socket.emit('joinedRoom', roomCode);
            io.to(roomCode).emit('playerJoined', game.players.length);
        } else {
            socket.emit('roomNotFound');
        }
    });

    socket.on('startGame', (roomCode) => {
        if (games.has(roomCode)) {
            const game = games.get(roomCode);
            game.start();
            io.to(roomCode).emit('gameStarted', game.getState());
            startGameLoop(roomCode);
        }
    });

    socket.on('changeDirection', ({ roomCode, direction }) => {
        if (games.has(roomCode)) {
            const game = games.get(roomCode);
            game.movePlayer(socket.id, direction);
        }
    });

    socket.on('disconnect', () => {
        console.log('A user disconnected');
        // 플레이어 제거 및 게임 상태 업데이트 로직
    });
});

4. 게임 루프 구현

서버에서 게임 루프를 실행하여 게임 상태를 주기적으로 업데이트합니다.

function startGameLoop(roomCode) {
    const game = games.get(roomCode);
    const interval = setInterval(() => {
        game.update();
        const gameState = game.getState();
        io.to(roomCode).emit('gameState', gameState);

        if (game.isOver()) {
            clearInterval(interval);
            io.to(roomCode).emit('gameOver', game.getWinners());
            games.delete(roomCode);
        }
    }, 100); // 100ms 간격으로 업데이트
}

5. RESTful API 구현

게임 관련 정보를 조회할 수 있는 간단한 RESTful API를 구현합니다.

app.get('/api/rooms', (req, res) => {
    const roomList = Array.from(games.keys());
    res.json(roomList);
});

app.get('/api/room/:roomCode', (req, res) => {
    const roomCode = req.params.roomCode;
    if (games.has(roomCode)) {
        const game = games.get(roomCode);
        res.json(game.getState());
    } else {
        res.status(404).json({ error: 'Room not found' });
    }
});

6. 에러 핸들링

서버에서 발생할 수 있는 에러를 적절히 처리합니다.

app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).json({ error: 'Something went wrong!' });
});

process.on('uncaughtException', (err) => {
    console.error('Uncaught Exception:', err);
    process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
    console.error('Unhandled Rejection at:', promise, 'reason:', reason);
    process.exit(1);
});

7. 환경 변수 사용

보안과 유연성을 위해 환경 변수를 사용합니다.

require('dotenv').config();

const PORT = process.env.PORT || 3000;
const MAX_PLAYERS = process.env.MAX_PLAYERS || 4;

결론

이번 포스팅에서는 Express.js를 이용한 백엔드 서버 구축에 대해 알아보았습니다. 게임 로직의 모듈화, Socket.io를 이용한 실시간 통신, RESTful API 구현, 그리고 에러 핸들링 등 서버 개발의 주요 측면들을 다뤘습니다.

이러한 백엔드 구조는 게임의 안정성과 확장성을 보장하며, 클라이언트와 효율적으로 통신할 수 있는 기반을 제공합니다.

다음 포스팅에서는 게임 최적화와 버그 수정에 대해 다루겠습니다. 성능 개선 기법과 일반적인 게임 개발 관련 버그들을 어떻게 해결하는지 알아보겠습니다. 감사합니다!


관련 포스팅

결과물: 지렁이 게임 멀티 6인용(ver. 2.0) – CSAI

1. 프로젝트 개요 및 기획(지렁이 게임 온라인) – CSAI

2. 게임 로직 설계(지렁이 온라인 게임) – CSAI

3. 멀티플레이어 기능 구현(지렁이 온라인 게임) – CSAI

4. 프론트엔드 개발(지렁이 온라인 게임) – CSAI

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다