λ¬Έμμ μ΄μ νμ λλ€!
λͺ©μ°¨
π’ Node.js λ°±μλ μ€μ
Phaser Baduk Metaverse νλ‘μ νΈμ Node.js λ°±μλ μ€μ κ³Ό ꡬμ±μ λν΄ μμΈν μ€λͺ ν©λλ€.
1. νλ‘μ νΈ κ°μ
- μ΄ νλ‘μ νΈλ Node.jsλ₯Ό κΈ°λ°μΌλ‘ νλ©°, μΉ νμ΄μ§λ₯Ό μλ²μμ λ λλ§νκ³ ν΄λΌμ΄μΈνΈμμ μ€μκ° ν΅μ κΈ°λ₯μ μ 곡ν©λλ€.
- μ£Όμ κΈ°μ μ€νμ
Express.js
(μΉ νλ μμν¬)μSocket.IO
(μ€μκ° ν΅μ λΌμ΄λΈλ¬λ¦¬)μ λλ€.
2. μλ² κ΅¬μ‘° λ° μ£Όμ νμΌ
Phaser Baduk Metaverse νλ‘μ νΈμ ν΅μ¬ μλ² νμΌμΈ server.js
μ ꡬ쑰μ κ° λΆλΆμ μν μ λν΄ μ€λͺ
ν©λλ€.
1) μ΄κΈ° μ€μ λ° λͺ¨λ λΆλ¬μ€κΈ°
μλ² κ΅¬λμ νμν Express
, HTTP
, Socket.IO
λͺ¨λμ λΆλ¬μ€κ³ , μ ν리μΌμ΄μ
μΈμ€ν΄μ€λ₯Ό μ΄κΈ°νν©λλ€.
javascript const express = require('express'); // μΉ μλ² νλ μμν¬ const http = require('http'); // HTTP μλ² λͺ¨λ const socketIo = require('socket.io'); // μ€μκ° μΉ μμΌ λΌμ΄λΈλ¬λ¦¬ const path = require('path'); // νμΌ κ²½λ‘ μ²λ¦¬ μ νΈλ¦¬ν° const app = express(); // Express μ ν리μΌμ΄μ μμ± const server = http.createServer(app); // HTTP μλ² μμ± (Express μ±μ κΈ°λ°μΌλ‘) const io = socketIo(server); // Socket.IO μλ² μμ± (HTTP μλ²μ μ°κ²°)
2) μ μ νμΌ μλΉ λ° λΌμ°ν
ν΄λΌμ΄μΈνΈ(νλ‘ νΈμλ)μ μ 곡λ μ μ νμΌ(HTML, CSS, JS, μ΄λ―Έμ§ λ±)μ μ€μ νκ³ , κΈ°λ³Έ κ²½λ‘μ λν λΌμ°νΈλ₯Ό μ μν©λλ€.
javascript // 'public' λλ ν 리μ μλ μ μ νμΌλ€μ ν΄λΌμ΄μΈνΈμκ² μ 곡 (μ: index.html, style.css) app.use(express.static(path.join(__dirname, 'public'))); // λ£¨νΈ κ²½λ‘('/')λ‘ μ κ·Ό μ 'public' λλ ν 리 λ΄μ 'index.html' νμΌ μ μ‘ app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); });
3) Socket.IO μ΄λ²€νΈ μ²λ¦¬
ν΄λΌμ΄μΈνΈμ μλ² κ°μ μ€μκ° ν΅μ μ λ΄λΉνλ Socket.IO
μ΄λ²€νΈ νΈλ€λ¬λ₯Ό μ€μ ν©λλ€. ν΄λΌμ΄μΈνΈ μ°κ²° λ° κ²μ μ°Έμ¬ μ΄λ²€νΈλ₯Ό μ²λ¦¬ν©λλ€.
javascript // Socket.IO μ°κ²° μ΄λ²€νΈ μ²λ¦¬ io.on('connection', (socket) => { // μλ‘μ΄ ν΄λΌμ΄μΈνΈκ° μ°κ²°λ λλ§λ€ μ€ν console.log('μλ‘μ΄ ν΄λΌμ΄μΈνΈ μ°κ²°:', socket.id); // 'join-game' μ΄λ²€νΈ μμ μ νΉμ κ²μλ°©μ ν΄λΌμ΄μΈνΈ μ‘°μΈ socket.on('join-game', (gameId) => { socket.join(gameId); // ν΄λΉ gameIdλ₯Ό κ°μ§ λ°©μ μμΌμ μΆκ° console.log(`νλ μ΄μ΄κ° κ²μ ${gameId}μ μ°Έκ°νμ΅λλ€.`); }); // ν΄λΌμ΄μΈνΈ μ°κ²° ν΄μ μ μ€ν socket.on('disconnect', () => { console.log('ν΄λΌμ΄μΈνΈ μ°κ²° ν΄μ :', socket.id); }); });
4) μλ² ν¬νΈ μ€μ λ° μ€ν
νκ²½ λ³μμμ ν¬νΈ λ²νΈλ₯Ό κ°μ Έμ€κ±°λ κΈ°λ³Έκ°(3000)μ μ¬μ©νμ¬ μλ²λ₯Ό μ§μ λ ν¬νΈμμ μ€νν©λλ€.
javascript // νκ²½ λ³μμμ PORTλ₯Ό κ°μ Έμ€κ±°λ κΈ°λ³Έκ° 3000 μ¬μ© const PORT = process.env.PORT || 3000; // μλ²λ₯Ό μ§μ λ ν¬νΈμμ μ€ννκ³ , μ±κ³΅ μ μ½μμ λ©μμ§ μΆλ ₯ server.listen(PORT, () => { console.log(`μλ²κ° ν¬νΈ ${PORT}μμ μ€ν μ€μ λλ€.`); });
3. μμ‘΄μ± κ΄λ¦¬ (package.json)
νλ‘μ νΈμ νμν λΌμ΄λΈλ¬λ¦¬ λ° μ€ν¬λ¦½νΈλ₯Ό μ μνλ package.json
νμΌμ μ£Όμ λ΄μ©μ μ€λͺ
ν©λλ€.
1) νλ‘μ νΈ μ 보
ν€ | μ€λͺ |
---|---|
name | νλ‘μ νΈ μ΄λ¦ |
version | νλ‘μ νΈ λ²μ |
description | νλ‘μ νΈμ λν κ°λ΅ν μ€λͺ |
main | νλ‘μ νΈμ μ§μ
μ νμΌ (μ¬κΈ°μλ server.js ) |
2) μ€ν¬λ¦½νΈ
μμ£Ό μ¬μ©λλ λͺ λ Ήμ΄λ₯Ό λ¨μΆνμ¬ μ μν©λλ€.
μ€ν¬λ¦½νΈ | μ€λͺ | λͺ λ Ήμ΄ |
---|---|---|
start | νλ‘λμ νκ²½μμ μλ²λ₯Ό μμν©λλ€. | node server.js |
dev | κ°λ° νκ²½μμ nodemon μ μ¬μ©νμ¬ μλ²λ₯Ό μμν©λλ€. μ½λ λ³κ²½ μ μλ μ¬μμλ©λλ€. | nodemon server.js |
pm2 | PM2 λ₯Ό μ¬μ©νμ¬ μλ²λ₯Ό μμν©λλ€. (νλ‘μΈμ€ κ΄λ¦¬μ©) | pm2 start server.js |
3) μμ‘΄μ± λͺ©λ‘
μμ‘΄μ± | λ²μ | μ€λͺ |
---|---|---|
express | 4.18.2 | Node.js μΉ μ ν리μΌμ΄μ νλ μμν¬ |
socket.io | 4.7.2 | μ€μκ° μλ°©ν₯ μΉ μμΌ ν΅μ λΌμ΄λΈλ¬λ¦¬ |
cors | 2.8.5 | κ΅μ°¨ μΆμ² 리μμ€ κ³΅μ (CORS) λ―Έλ€μ¨μ΄ |
4) κ°λ° μμ‘΄μ± λͺ©λ‘
μμ‘΄μ± | λ²μ | μ€λͺ |
---|---|---|
nodemon | 3.0.1 | κ°λ° μ€ νμΌ λ³κ²½μ κ°μ§νμ¬ μλ²λ₯Ό μλμΌλ‘ μ¬μμνλ μ νΈλ¦¬ν° |
{ "name": "phaser-baduk-metaverse", "version": "1.0.0", "description": "Phaser.js κΈ°λ° λ°λ λ©νλ²μ€ κ²μ", "main": "server.js", "scripts": { "start": "node server.js", "dev": "nodemon server.js", "pm2": "pm2 start server.js" }, "dependencies": { "express": "^4.18.2", "socket.io": "^4.7.2", "cors": "^2.8.5" }, "devDependencies": { "nodemon": "^3.0.1" } }
4. νκ²½ μ€μ
μ ν리μΌμ΄μ μ λμ λ°©μμ μ μ΄νλ νκ²½ λ³μ μ€μ λ° μλ² μ€ν λ°©λ²μ μ€λͺ ν©λλ€.
1) νκ²½ λ³μ νμΌ (.env)
λ―Όκ°ν μ 보λ νκ²½λ³λ‘ λ¬λΌμ§λ μ€μ κ°μ '.env
' νμΌμ μ μ₯νμ¬ κ΄λ¦¬ν©λλ€. μ΄ νμΌμ λ²μ κ΄λ¦¬ μμ€ν
μ ν¬ν¨λμ§ μλλ‘ νλ κ²μ΄ μΌλ°μ μ
λλ€.
# .env νμΌ μμ NODE_ENV=development // νμ¬ μ€ν νκ²½ (development, production λ±) PORT=3000 // μλ²κ° μ¬μ©ν ν¬νΈ λ²νΈ DB_PATH=./db.json // λ°μ΄ν°λ² μ΄μ€ νμΌ κ²½λ‘
2) μλ² μ€ν λͺ λ Ήμ΄
κ°λ° λ° νλ‘λμ νκ²½μμ μλ²λ₯Ό μ€ννλ λ°©λ²μ μ€λͺ ν©λλ€.
- κ°λ° λͺ¨λ:
nodemon
μ μ¬μ©νμ¬ μ½λ λ³κ²½ μ μλ²κ° μλμΌλ‘ μ¬μμλ©λλ€.
bash npm run dev
- νλ‘λμ λͺ¨λ:
node
λͺ λ Ήμ΄λ‘ μ§μ μ€ννλ©°, μμ μ μΈ μλΉμ€ μ΄μμ μ ν©ν©λλ€.
bash npm start
5. λ°μ΄ν° κ΄λ¦¬
κ²μ λ° μ¬μ©μ κ΄λ ¨ λ°μ΄ν°λ₯Ό μ μ₯νκ³ κ΄λ¦¬νλ db.json
νμΌμ ꡬ쑰λ₯Ό μ€λͺ
ν©λλ€.
1) κ²μ λ°μ΄ν° μ μ₯μ (db.json)
κ²μμ μν, μ¬μ©μ μ 보, λ°© μ 보, ν΅κ³ λ±μ μ μ₯νλ λ° μ¬μ©λ μ μλ JSON νμΌ κ΅¬μ‘°μ λλ€. μ€μ νλ‘μ νΈμμλ λ°μ΄ν°λ² μ΄μ€(μ: MongoDB, PostgreSQL)λ₯Ό μ¬μ©ν μ μμ΅λλ€.
{ "games": {}, // νμ¬ μ§ν μ€μ΄κ±°λ μλ£λ κ²μ μ 보 "users": {}, // μ¬μ©μ κ³μ λ° νλ‘ν μ 보 "rooms": {}, // μμ±λ κ²μ λ°© μ 보 "statistics": { // μ λ°μ μΈ κ²μ ν΅κ³ "totalGames": 0, // μ΄ κ²μ μ "activePlayers": 0 // νμ¬ μ μ μ€μΈ νλ μ΄μ΄ μ } }
6. λλ²κΉ λ° μλ¬ νΈλ€λ§
μλ²μ λλ²κΉ μ λκ³ , μμμΉ λͺ»ν μλ¬ λ°μ μ μμ μ μΌλ‘ μ²λ¦¬νλ λ°©λ²μ λν΄ μ€λͺ ν©λλ€.
1) λ‘κ·Έ λ 벨 μ€μ
κ°λ° νκ²½μμ μμΈν λ‘κ·Έλ₯Ό μΆλ ₯νμ¬ λλ²κΉ μ μ©μ΄νκ² ν©λλ€.
javascript // Node.js νκ²½ λ³μ 'NODE_ENV'κ° 'development'μΌ κ²½μ° μμΈ λ‘κ·Έ μΆλ ₯ if (process.env.NODE_ENV === 'development') { console.log('π§ κ°λ° λͺ¨λλ‘ μ€ν μ€'); // νμ¬ Node.js νλ‘μΈμ€μ λ©λͺ¨λ¦¬ μ¬μ©λ μΆλ ₯ console.log('π λ©λͺ¨λ¦¬ μ¬μ©λ:', process.memoryUsage()); }
2) μ μ μλ¬ νΈλ€λ§
μ²λ¦¬λμ§ μμ μμΈ(Exception)λ Promise κ±°λΆ(Rejection)κ° λ°μνμ λ μ ν리μΌμ΄μ μ΄ λΉμ μμ μΌλ‘ μ’ λ£λλ κ²μ λ°©μ§νκ³ , μλ¬λ₯Ό κΈ°λ‘ν©λλ€.
javascript // μ²λ¦¬λμ§ μμ λκΈ° μμΈ λ°μ μ νΈμΆ process.on('uncaughtException', (err) => { console.error('β μ²λ¦¬λμ§ μμ μμΈ:', err); process.exit(1); // μ ν리μΌμ΄μ μ’ λ£ (μν μ½λ 1: μ€λ₯) }); // μ²λ¦¬λμ§ μμ Promise κ±°λΆ(reject) λ°μ μ νΈμΆ process.on('unhandledRejection', (reason, promise) => { console.error('β μ²λ¦¬λμ§ μμ Promise κ±°λΆ:', reason); // μ΄ κ²½μ° μ ν리μΌμ΄μ μ μ’ λ£ν μ§ μ¬λΆλ μν©μ λ°λΌ λ€λ₯Ό μ μμ΅λλ€. });
7. μ±λ₯ μ΅μ ν
Node.js μ ν리μΌμ΄μ μ μ±λ₯μ ν₯μμν€κΈ° μν κΈ°λ²λ€μ μ€λͺ ν©λλ€.
1) λ©λͺ¨λ¦¬ κ΄λ¦¬ (κ°λΉμ§ 컬λ μ )
μ£ΌκΈ°μ μΌλ‘ κ°λΉμ§ 컬λ μ μ λͺ μμ μΌλ‘ μ€ννμ¬ λ©λͺ¨λ¦¬ μ¬μ©λμ μ΅μ νν μ μμ΅λλ€. μ΄λ νΉν λ©λͺ¨λ¦¬ λμκ° μμ¬λ λ μ μ©ν©λλ€.
javascript // 30μ΄λ§λ€ κ°λΉμ§ 컬λ μ (GC)μ λͺ μμ μΌλ‘ μ€ν setInterval(() => { // Node.jsμμ 'global.gc'λ₯Ό μ¬μ©νλ €λ©΄ 'node --expose-gc' μ΅μ μΌλ‘ μ€νν΄μΌ ν©λλ€. if (global.gc) { global.gc(); // κ°λΉμ§ 컬λ μ κ°μ μ€ν console.log('π§Ή κ°λΉμ§ 컬λ μ μ€ν'); } }, 30000); // 30000 λ°λ¦¬μ΄ = 30μ΄
2) ν΄λ¬μ€ν° λͺ¨λ
Node.jsλ λ¨μΌ μ€λ λ κΈ°λ°μ΄μ§λ§, cluster
λͺ¨λμ μ¬μ©νμ¬ CPU μ½μ΄ μλ§νΌ μ컀 νλ‘μΈμ€λ₯Ό μμ±νμ¬ λ©ν°μ½μ΄ CPUμ μ΄μ μ νμ©νκ³ , μλ²μ μμ μ±κ³Ό μ²λ¦¬λμ ν₯μμν¬ μ μμ΅λλ€.
const cluster = require('cluster'); // Node.js ν΄λ¬μ€ν° λͺ¨λ const numCPUs = require('os').cpus().length; // μμ€ν μ CPU μ½μ΄ μ // νμ¬ νλ‘μΈμ€κ° λ§μ€ν° νλ‘μΈμ€μΈμ§ νμΈ if (cluster.isMaster) { console.log(`λ§μ€ν° νλ‘μΈμ€ ${process.pid} μ€ν μ€`); // CPU μ½μ΄ μλ§νΌ μ컀 νλ‘μΈμ€ μμ± for (let i = 0; i < numCPUs; i++) { cluster.fork(); // μλ‘μ΄ μ컀 νλ‘μΈμ€ μμ± } // μ컀 νλ‘μΈμ€κ° μ’ λ£λ λ (μ: μμΈ λ°μ λλ μλ μ’ λ£) cluster.on('exit', (worker, code, signal) => { console.log(`μ컀 ${worker.process.pid} μ’ λ£ (μ½λ: ${code}, μκ·Έλ: ${signal})`); cluster.fork(); // μμ μ μΈ μλΉμ€λ₯Ό μν΄ μλ‘μ΄ μ컀 μ¬μμ }); } else { // μ컀 νλ‘μΈμ€λ μ€μ μλ² λ‘μ§μ μ€ν // μ΄ κ²½μ° 'server.js' νμΌμ΄ μ컀 νλ‘μΈμ€ λ΄μμ λ€μ λ‘λλμ΄ μλ²κ° μ€νλ©λλ€. require('./server.js'); }
8. κ΄λ ¨ λ¬Έμ
β μ΄ νμ΄μ§λ μλμΌλ‘ μμ±λμμ΅λλ€. β