μ‚¬μš©μž 도ꡬ

μ‚¬μ΄νŠΈ 도ꡬ


wiki:it:dream_of_enc:metaverse:nodejs

λ¬Έμ„œμ˜ 이전 νŒμž…λ‹ˆλ‹€!


🟒 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. κ΄€λ ¨ λ¬Έμ„œ

β€” 이 νŽ˜μ΄μ§€λŠ” μžλ™μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€. β€”

wiki/it/dream_of_enc/metaverse/nodejs.1753769599.txt.gz Β· λ§ˆμ§€λ§‰μœΌλ‘œ μˆ˜μ •λ¨: μ €μž syjang0803

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki