====== 🟒 Node.js λ°±μ—”λ“œ μ„€μ • ====== Phaser Baduk Metaverse ν”„λ‘œμ νŠΈμ˜ ''Node.js'' λ°±μ—”λ“œ μ„€μ •κ³Ό ꡬ성에 λŒ€ν•΄ μžμ„Ένžˆ μ„€λͺ…ν•©λ‹ˆλ‹€. ---- ===== 1. Node.jsλž€ λ¬΄μ—‡μΈκ°€μš”? ===== ''Node.js''λŠ” ''JavaScript''λ₯Ό μ„œλ²„μ—μ„œ μ‹€ν–‰ν•  수 있게 ν•΄μ£ΌλŠ” λŸ°νƒ€μž„ ν™˜κ²½μž…λ‹ˆλ‹€. μ›Ή λΈŒλΌμš°μ €μ—μ„œλ§Œ μ‹€ν–‰λ˜λ˜ ''JavaScript''λ₯Ό μ„œλ²„μ—μ„œλ„ μ‹€ν–‰ν•  수 있게 ν•΄μ€λ‹ˆλ‹€. **μ£Όμš” νŠΉμ§•:** * ''JavaScript''둜 μ„œλ²„λ₯Ό λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€. * 비동기 μ²˜λ¦¬μ— 강점이 μžˆμŠ΅λ‹ˆλ‹€. * ''npm''을 톡해 λ‹€μ–‘ν•œ νŒ¨ν‚€μ§€λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. * λΉ λ₯Έ 개발이 κ°€λŠ₯ν•©λ‹ˆλ‹€. ---- ===== 2. ν”„λ‘œμ νŠΈ κ°œμš” ===== 이 ν”„λ‘œμ νŠΈλŠ” ''Node.js''λ₯Ό 기반으둜 ν•˜λ©°, μ›Ή νŽ˜μ΄μ§€λ₯Ό μ„œλ²„μ—μ„œ λ Œλ”λ§ν•˜κ³  ν΄λΌμ΄μ–ΈνŠΈμ™€μ˜ μ‹€μ‹œκ°„ 톡신 κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€. **μ£Όμš” 기술 μŠ€νƒ:** * **''Express.js''** - μ›Ή ν”„λ ˆμž„μ›Œν¬ * **''Socket.IO''** - μ‹€μ‹œκ°„ 톡신 라이브러리 * **''PM2''** - ν”„λ‘œμ„ΈμŠ€ 관리 도ꡬ ---- ===== 3. μ„œλ²„ ꡬ쑰 및 μ£Όμš” 파일 ===== Phaser Baduk Metaverse ν”„λ‘œμ νŠΈμ˜ 핡심 μ„œλ²„ 파일인 ''server.js''의 ꡬ쑰와 각 λΆ€λΆ„μ˜ 역할에 λŒ€ν•΄ μ„€λͺ…ν•©λ‹ˆλ‹€. ==== 1) 초기 μ„€μ • 및 λͺ¨λ“ˆ 뢈러였기 ==== μ„œλ²„ ꡬ동에 ν•„μš”ν•œ λͺ¨λ“ˆλ“€μ„ 뢈러였고, μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μΈμŠ€ν„΄μŠ€λ₯Ό μ΄ˆκΈ°ν™”ν•©λ‹ˆλ‹€. const express = require('express'); // μ›Ή μ„œλ²„ ν”„λ ˆμž„μ›Œν¬ const http = require('http'); // HTTP μ„œλ²„ λͺ¨λ“ˆ const socketIo = require('socket.io'); // μ‹€μ‹œκ°„ μ›Ή μ†ŒμΌ“ 라이브러리 const path = require('path'); // 파일 경둜 처리 μœ ν‹Έλ¦¬ν‹° **각 λͺ¨λ“ˆμ˜ μ—­ν• :** * ''express'' - μ›Ή μ„œλ²„λ₯Ό μ‰½κ²Œ λ§Œλ“€ 수 μžˆλŠ” ν”„λ ˆμž„μ›Œν¬ * ''http'' - ''HTTP'' μ„œλ²„λ₯Ό μƒμ„±ν•˜λŠ” κΈ°λ³Έ λͺ¨λ“ˆ * ''socket.io'' - μ‹€μ‹œκ°„ μ–‘λ°©ν–₯ 톡신을 μœ„ν•œ 라이브러리 * ''path'' - 파일 경둜λ₯Ό μ•ˆμ „ν•˜κ²Œ μ²˜λ¦¬ν•˜λŠ” μœ ν‹Έλ¦¬ν‹° ==== 2) μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μΈμŠ€ν„΄μŠ€ 생성 ==== const app = express(); // Express μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 생성 const server = http.createServer(app); // HTTP μ„œλ²„ 생성 (Express 앱을 기반으둜) const io = socketIo(server); // Socket.IO μ„œλ²„ 생성 (HTTP μ„œλ²„μ— μ—°κ²°) **μ„€λͺ…:** * ''app'' - ''Express'' μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 객체 * ''server'' - ''HTTP'' μ„œλ²„ 객체 (''Express'' 앱을 기반으둜 생성) * ''io'' - ''Socket.IO'' μ„œλ²„ 객체 (''HTTP'' μ„œλ²„μ— μ—°κ²°) ==== 3) 정적 파일 μ„œλΉ™ 및 λΌμš°νŒ… ==== ν΄λΌμ΄μ–ΈνŠΈ(ν”„λ‘ νŠΈμ—”λ“œ)에 제곡될 정적 파일(''HTML'', ''CSS'', ''JS'', 이미지 λ“±)을 μ„€μ •ν•˜κ³ , κΈ°λ³Έ κ²½λ‘œμ— λŒ€ν•œ 라우트λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€. // 'public' 디렉토리에 μžˆλŠ” 정적 νŒŒμΌλ“€μ„ ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ 제곡 app.use(express.static(path.join(dirname, 'public'))); // 루트 경둜('/')둜 μ ‘κ·Ό μ‹œ 'public' 디렉토리 λ‚΄μ˜ 'index.html' 파일 전솑 app.get('/', (req, res) => { res.sendFile(path.join(dirname, 'public', 'index.html')); }); **μ„€λͺ…:** * ''express.static()'' - 정적 νŒŒμΌμ„ μ›Ήμ—μ„œ μ ‘κ·Ό κ°€λŠ₯ν•˜κ²Œ ν•©λ‹ˆλ‹€. * ''path.join(dirname, 'public')'' - ν˜„μž¬ λ””λ ‰ν† λ¦¬μ˜ ''public'' 폴더 κ²½λ‘œμž…λ‹ˆλ‹€. * ''app.get('/')'' - 루트 경둜둜 μ ‘κ·Όν–ˆμ„ λ•Œ μ‹€ν–‰λ˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€. ==== 4) Socket.IO 이벀트 처리 ==== ν΄λΌμ΄μ–ΈνŠΈμ™€ μ„œλ²„ κ°„μ˜ μ‹€μ‹œκ°„ 톡신을 λ‹΄λ‹Ήν•˜λŠ” ''Socket.IO'' 이벀트 ν•Έλ“€λŸ¬λ₯Ό μ„€μ •ν•©λ‹ˆλ‹€. // 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); }); }); **μ„€λͺ…:** * ''io.on('connection')'' - μƒˆλ‘œμš΄ ν΄λΌμ΄μ–ΈνŠΈκ°€ 연결될 λ•Œ μ‹€ν–‰λ©λ‹ˆλ‹€. * ''socket.on('join-game')'' - ν΄λΌμ΄μ–ΈνŠΈκ°€ κ²Œμž„ μ°Έκ°€ μš”μ²­μ„ 보낼 λ•Œ μ‹€ν–‰λ©λ‹ˆλ‹€. * ''socket.join(gameId)'' - νŠΉμ • κ²Œμž„λ°©μ— μ†ŒμΌ“μ„ μΆ”κ°€ν•©λ‹ˆλ‹€. * ''socket.on('disconnect')'' - ν΄λΌμ΄μ–ΈνŠΈ 연결이 λŠμ–΄μ§ˆ λ•Œ μ‹€ν–‰λ©λ‹ˆλ‹€. ==== 5) μ„œλ²„ 포트 μ„€μ • 및 μ‹€ν–‰ ==== ν™˜κ²½ λ³€μˆ˜μ—μ„œ 포트 번호λ₯Ό κ°€μ Έμ˜€κ±°λ‚˜ κΈ°λ³Έκ°’(3000)을 μ‚¬μš©ν•˜μ—¬ μ„œλ²„λ₯Ό μ§€μ •λœ ν¬νŠΈμ—μ„œ μ‹€ν–‰ν•©λ‹ˆλ‹€. // ν™˜κ²½ λ³€μˆ˜μ—μ„œ PORTλ₯Ό κ°€μ Έμ˜€κ±°λ‚˜ κΈ°λ³Έκ°’ 3000 μ‚¬μš© const PORT = process.env.PORT || 3000; // μ„œλ²„λ₯Ό μ§€μ •λœ ν¬νŠΈμ—μ„œ μ‹€ν–‰ν•˜κ³ , 성곡 μ‹œ μ½˜μ†”μ— λ©”μ‹œμ§€ 좜λ ₯ server.listen(PORT, () => { console.log(`μ„œλ²„κ°€ 포트 ${PORT}μ—μ„œ μ‹€ν–‰ μ€‘μž…λ‹ˆλ‹€.`); console.log(`http://localhost:${PORT}μ—μ„œ μ ‘μ†ν•˜μ„Έμš”.`); }); **μ„€λͺ…:** * ''process.env.PORT'' - ν™˜κ²½ λ³€μˆ˜μ—μ„œ 포트 번호λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€. * ''|| 3000'' - ν™˜κ²½ λ³€μˆ˜κ°€ μ—†μœΌλ©΄ κΈ°λ³Έκ°’ 3000을 μ‚¬μš©ν•©λ‹ˆλ‹€. * ''server.listen()'' - μ„œλ²„λ₯Ό μ§€μ •λœ ν¬νŠΈμ—μ„œ μ‹œμž‘ν•©λ‹ˆλ‹€. * 콜백 ν•¨μˆ˜λŠ” μ„œλ²„ μ‹œμž‘ 성곡 μ‹œ μ‹€ν–‰λ˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€. ---- ===== 4. μ˜μ‘΄μ„± 관리 (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) 미듀웨어 | | helmet | 7.0.0 | λ³΄μ•ˆ 헀더 μ„€μ • 미듀웨어 | | compression | 1.7.4 | 응닡 μ••μΆ• 미듀웨어 | | express-rate-limit | 6.7.0 | μš”μ²­ μ œν•œ 미듀웨어 | ==== 4) 개발 μ˜μ‘΄μ„± λͺ©λ‘ ==== ^ μ˜μ‘΄μ„± ^ 버전 ^ μ„€λͺ… ^ | nodemon | 2.0.22 | 개발 μ‹œ μ½”λ“œ λ³€κ²½ κ°μ§€ν•˜μ—¬ μ„œλ²„ μžλ™ μž¬μ‹œμž‘ | | jest | 29.5.0 | JavaScript ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬ | | supertest | 6.3.3 | HTTP ν…ŒμŠ€νŠΈ 라이브러리 | ---- ===== 5. ν™˜κ²½ μ„€μ • ===== ==== 1) ν™˜κ²½ λ³€μˆ˜ μ„€μ • ==== ν”„λ‘œμ νŠΈ λ£¨νŠΈμ— ''.env'' νŒŒμΌμ„ μƒμ„±ν•˜μ—¬ ν™˜κ²½ λ³€μˆ˜λ₯Ό μ„€μ •ν•©λ‹ˆλ‹€: # μ„œλ²„ 포트 μ„€μ • PORT=3000 # λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° 정보 MONGODB_URI=mongodb://localhost:27017/baduk_metaverse # JWT μ‹œν¬λ¦Ώ ν‚€ JWT_SECRET=your-secret-key-here # ν™˜κ²½ μ„€μ • NODE_ENV=development **μ£Όμš” ν™˜κ²½ λ³€μˆ˜:** * ''PORT'' - μ„œλ²„κ°€ 싀행될 포트 번호 * ''MONGODB_URI'' - ''MongoDB'' λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° λ¬Έμžμ—΄ * ''JWT_SECRET'' - ''JWT'' 토큰 μ•”ν˜Έν™”μ— μ‚¬μš©ν•  μ‹œν¬λ¦Ώ ν‚€ * ''NODE_ENV'' - ν˜„μž¬ ν™˜κ²½ (''development''/''production'') ==== 2) ν™˜κ²½ λ³€μˆ˜ μ‚¬μš©λ²• ==== // dotenv νŒ¨ν‚€μ§€λ₯Ό μ‚¬μš©ν•˜μ—¬ .env 파일 λ‘œλ“œ require('dotenv').config(); // ν™˜κ²½ λ³€μˆ˜ μ‚¬μš© const PORT = process.env.PORT || 3000; const MONGODB_URI = process.env.MONGODB_URI; const JWT_SECRET = process.env.JWT_SECRET; **μ„€λͺ…:** * ''require('dotenv').config()'' - ''.env'' νŒŒμΌμ„ λ‘œλ“œν•©λ‹ˆλ‹€. * ''process.env.λ³€μˆ˜λͺ…'' - ν™˜κ²½ λ³€μˆ˜ 값을 κ°€μ Έμ˜΅λ‹ˆλ‹€. * ''|| κΈ°λ³Έκ°’'' - ν™˜κ²½ λ³€μˆ˜κ°€ 없을 λ•Œ 기본값을 μ‚¬μš©ν•©λ‹ˆλ‹€. ---- ===== 6. ν”„λ‘œμ νŠΈ ꡬ쑰 ===== **ꢌμž₯ν•˜λŠ” ν”„λ‘œμ νŠΈ 폴더 ꡬ쑰:** baduk-metaverse/ β”œβ”€β”€ server.js # 메인 μ„œλ²„ 파일 β”œβ”€β”€ package.json # ν”„λ‘œμ νŠΈ μ„€μ • 파일 β”œβ”€β”€ .env # ν™˜κ²½ λ³€μˆ˜ 파일 β”œβ”€β”€ .gitignore # Git λ¬΄μ‹œ 파일 λͺ©λ‘ β”œβ”€β”€ public/ # 정적 파일 폴더 β”‚ β”œβ”€β”€ index.html # 메인 HTML 파일 β”‚ β”œβ”€β”€ css/ # CSS νŒŒμΌλ“€ β”‚ β”œβ”€β”€ js/ # ν΄λΌμ΄μ–ΈνŠΈ JavaScript νŒŒμΌλ“€ β”‚ └── images/ # 이미지 νŒŒμΌλ“€ β”œβ”€β”€ routes/ # 라우트 νŒŒμΌλ“€ β”‚ β”œβ”€β”€ games.js # κ²Œμž„ κ΄€λ ¨ 라우트 β”‚ β”œβ”€β”€ users.js # μ‚¬μš©μž κ΄€λ ¨ 라우트 β”‚ └── statistics.js # 톡계 κ΄€λ ¨ 라우트 β”œβ”€β”€ controllers/ # 컨트둀러 νŒŒμΌλ“€ β”‚ β”œβ”€β”€ BadukGameController.js β”‚ β”œβ”€β”€ UserController.js β”‚ └── StatisticsController.js β”œβ”€β”€ models/ # 데이터 λͺ¨λΈ νŒŒμΌλ“€ β”‚ β”œβ”€β”€ BadukGame.js β”‚ β”œβ”€β”€ User.js β”‚ └── BadukGameState.js β”œβ”€β”€ middleware/ # 미듀웨어 νŒŒμΌλ“€ β”‚ β”œβ”€β”€ auth.js # 인증 미듀웨어 β”‚ └── errorHandler.js # μ—λŸ¬ 처리 미듀웨어 β”œβ”€β”€ utils/ # μœ ν‹Έλ¦¬ν‹° νŒŒμΌλ“€ β”‚ β”œβ”€β”€ BadukMoveValidator.js β”‚ └── helpers.js └── tests/ # ν…ŒμŠ€νŠΈ νŒŒμΌλ“€ β”œβ”€β”€ server.test.js └── game.test.js **각 ν΄λ”μ˜ μ—­ν• :** * ''public/'' - ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ μ œκ³΅ν•  νŒŒμΌλ“€ * ''routes/'' - ''URL'' κ²½λ‘œλ³„ 처리 둜직 * ''controllers/'' - λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 처리 * ''models/'' - 데이터 ꡬ쑰 μ •μ˜ * ''middleware/'' - μš”μ²­ 처리 쀑간 단계 * ''utils/'' - 곡톡 μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜λ“€ * ''tests/'' - ν…ŒμŠ€νŠΈ μ½”λ“œλ“€ ---- ===== 7. 개발 ν™˜κ²½ μ„€μ • ===== ==== 1) Node.js μ„€μΉ˜ ==== **Windowsμ—μ„œ Node.js μ„€μΉ˜:** * ''Node.js'' 곡식 μ›Ήμ‚¬μ΄νŠΈ (''https://nodejs.org'') λ°©λ¬Έ * ''LTS'' 버전 λ‹€μš΄λ‘œλ“œ 및 μ„€μΉ˜ * μ„€μΉ˜ μ™„λ£Œ ν›„ ν„°λ―Έλ„μ—μ„œ 확인: node --version npm --version ==== 2) ν”„λ‘œμ νŠΈ μ΄ˆκΈ°ν™” ==== # μƒˆ 폴더 생성 및 이동 mkdir baduk-metaverse cd baduk-metaverse # npm ν”„λ‘œμ νŠΈ μ΄ˆκΈ°ν™” npm init -y # ν•„μš”ν•œ νŒ¨ν‚€μ§€ μ„€μΉ˜ npm install express socket.io cors helmet compression express-rate-limit # 개발용 νŒ¨ν‚€μ§€ μ„€μΉ˜ npm install --save-dev nodemon ==== 3) κΈ°λ³Έ μ„œλ²„ 파일 생성 ==== ''server.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); // 정적 파일 μ„œλΉ™ app.use(express.static(path.join(dirname, 'public'))); // κΈ°λ³Έ 라우트 app.get('/', (req, res) => { res.sendFile(path.join(dirname, 'public', 'index.html')); }); // Socket.IO μ—°κ²° 처리 io.on('connection', (socket) => { console.log('μƒˆλ‘œμš΄ ν΄λΌμ΄μ–ΈνŠΈ μ—°κ²°:', socket.id); socket.on('disconnect', () => { console.log('ν΄λΌμ΄μ–ΈνŠΈ μ—°κ²° ν•΄μ œ:', socket.id); }); }); // μ„œλ²„ μ‹œμž‘ const PORT = process.env.PORT || 3000; server.listen(PORT, () => { console.log(`μ„œλ²„κ°€ 포트 ${PORT}μ—μ„œ μ‹€ν–‰ μ€‘μž…λ‹ˆλ‹€.`); }); ==== 4) package.json 슀크립트 μ„€μ • ==== { "name": "baduk-metaverse", "version": "1.0.0", "description": "Phaser Baduk Metaverse ν”„λ‘œμ νŠΈ", "main": "server.js", "scripts": { "start": "node server.js", "dev": "nodemon server.js", "test": "jest" }, "dependencies": { "express": "^4.18.2", "socket.io": "^4.7.2", "cors": "^2.8.5", "helmet": "^7.0.0", "compression": "^1.7.4", "express-rate-limit": "^6.7.0" }, "devDependencies": { "nodemon": "^2.0.22", "jest": "^29.5.0" } } ---- ===== 8. μ„œλ²„ μ‹€ν–‰ 및 ν…ŒμŠ€νŠΈ ===== ==== 1) 개발 λͺ¨λ“œλ‘œ μ„œλ²„ μ‹€ν–‰ ==== npm run dev **μ„€λͺ…:** * ''nodemon''을 μ‚¬μš©ν•˜μ—¬ μ„œλ²„λ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€. * μ½”λ“œ λ³€κ²½ μ‹œ μžλ™μœΌλ‘œ μ„œλ²„κ°€ μž¬μ‹œμž‘λ©λ‹ˆλ‹€. * 개발 쀑에 맀우 μœ μš©ν•©λ‹ˆλ‹€. ==== 2) ν”„λ‘œλ•μ…˜ λͺ¨λ“œλ‘œ μ„œλ²„ μ‹€ν–‰ ==== npm start **μ„€λͺ…:** * ''node''λ₯Ό μ‚¬μš©ν•˜μ—¬ μ„œλ²„λ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€. * μ½”λ“œ λ³€κ²½ μ‹œ μˆ˜λ™μœΌλ‘œ μ„œλ²„λ₯Ό μž¬μ‹œμž‘ν•΄μ•Ό ν•©λ‹ˆλ‹€. * μ‹€μ œ μ„œλΉ„μŠ€ ν™˜κ²½μ—μ„œ μ‚¬μš©ν•©λ‹ˆλ‹€. ==== 3) μ„œλ²„ ν…ŒμŠ€νŠΈ ==== **λΈŒλΌμš°μ €μ—μ„œ ν…ŒμŠ€νŠΈ:** * λΈŒλΌμš°μ € μ—΄κΈ° * ''http://localhost:3000'' 접속 * μ„œλ²„κ°€ μ •μƒμ μœΌλ‘œ μ‘λ‹΅ν•˜λŠ”μ§€ 확인 **ν„°λ―Έλ„μ—μ„œ ν…ŒμŠ€νŠΈ:** curl http://localhost:3000 ---- ===== 9. 디버깅 및 λ‘œκΉ… ===== ==== 1) μ½˜μ†” λ‘œκΉ… ==== // κΈ°λ³Έ λ‘œκΉ… console.log('일반 λ©”μ‹œμ§€'); console.error('μ—λŸ¬ λ©”μ‹œμ§€'); console.warn('κ²½κ³  λ©”μ‹œμ§€'); // 객체 λ‘œκΉ… const gameData = { id: 1, name: 'λ°”λ‘‘ κ²Œμž„' }; console.log('κ²Œμž„ 데이터:', gameData); // μ‹œκ°„κ³Ό ν•¨κ»˜ λ‘œκΉ… console.log(`${new Date().toISOString()} - μ„œλ²„ μ‹œμž‘λ¨`); ==== 2) λ‘œκΉ… 미듀웨어 ==== // μš”μ²­ λ‘œκΉ… 미듀웨어 app.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`); next(); }); // μ—λŸ¬ λ‘œκΉ… 미듀웨어 app.use((err, req, res, next) => { console.error('μ„œλ²„ μ—λŸ¬:', err); res.status(500).json({ error: 'μ„œλ²„ λ‚΄λΆ€ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); }); ==== 3) 디버깅 도ꡬ ==== **Node.js λ‚΄μž₯ 디버거 μ‚¬μš©:** node --inspect server.js **Chrome DevToolsμ—μ„œ 디버깅:** * Chrome λΈŒλΌμš°μ € μ—΄κΈ° * %%chrome://inspect%% 접속 * "Open dedicated DevTools for Node" 클릭 ---- ===== 10. μ„±λŠ₯ μ΅œμ ν™” ===== ==== 1) μ••μΆ• μ„€μ • ==== const compression = require('compression'); // 응닡 μ••μΆ• 미듀웨어 app.use(compression()); ==== 2) 캐싱 μ„€μ • ==== // 정적 파일 캐싱 app.use(express.static(path.join(dirname, 'public'), { maxAge: '1d', // 1일간 μΊμ‹œ etag: true // ETag μ‚¬μš© })); ==== 3) μš”μ²­ μ œν•œ ==== const rateLimit = require('express-rate-limit'); // API μš”μ²­ μ œν•œ const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15λΆ„ max: 100, // IPλ‹Ή μ΅œλŒ€ 100개 μš”μ²­ message: 'λ„ˆλ¬΄ λ§Žμ€ μš”μ²­μ΄ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); app.use('/api/', limiter); ---- ===== 11. λ³΄μ•ˆ μ„€μ • ===== ==== 1) Helmet 미듀웨어 ==== const helmet = require('helmet'); // λ³΄μ•ˆ 헀더 μ„€μ • app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", "data:", "https:"], connectSrc: ["'self'", "ws:", "wss:"] } } })); ==== 2) CORS μ„€μ • ==== const cors = require('cors'); // CORS μ„€μ • app.use(cors({ origin: process.env.NODE_ENV === 'production' ? ['https://yourdomain.com'] : ['http://localhost:3000'], credentials: true })); ==== 3) μž…λ ₯ 데이터 검증 ==== // μž…λ ₯ 데이터 검증 예제 app.post('/api/games', (req, res) => { const { gameType, settings } = req.body; // ν•„μˆ˜ ν•„λ“œ 검증 if (!gameType) { return res.status(400).json({ error: 'κ²Œμž„ νƒ€μž…μ΄ ν•„μš”ν•©λ‹ˆλ‹€.' }); } // 데이터 νƒ€μž… 검증 if (typeof gameType !== 'string') { return res.status(400).json({ error: 'κ²Œμž„ νƒ€μž…μ€ λ¬Έμžμ—΄μ΄μ–΄μ•Ό ν•©λ‹ˆλ‹€.' }); } // μΆ”κ°€ 처리... }); ---- ===== 12. λ‹€μŒ 단계 ===== ''Node.js'' 기본을 λ°°μ› λ‹€λ©΄ λ‹€μŒμ„ ν•™μŠ΅ν•΄λ³΄μ„Έμš”: * **''Express.js''** - μ›Ή ν”„λ ˆμž„μ›Œν¬ 심화 ν•™μŠ΅ * **''Socket.IO''** - μ‹€μ‹œκ°„ 톡신 κ΅¬ν˜„ * **λ°μ΄ν„°λ² μ΄μŠ€ 연동** - ''MongoDB'', ''MySQL'' λ“± * **인증 μ‹œμŠ€ν…œ** - ''JWT'', ''Passport.js'' λ“± * **ν…ŒμŠ€νŠΈ μž‘μ„±** - ''Jest'', ''Mocha'' λ“± **μΆ”μ²œ ν•™μŠ΅ μˆœμ„œ:** - [[wiki:it:dream_of_enc:metaverse:express|πŸš€ Express.js μ›Ή ν”„λ ˆμž„μ›Œν¬]] - [[wiki:it:dream_of_enc:metaverse:socketio|πŸ”Œ Socket.IO μ‹€μ‹œκ°„ 톡신]] - [[wiki:it:dream_of_enc:metaverse:pm2|⚑ PM2 ν”„λ‘œμ„ΈμŠ€ 관리]] --- //이 νŽ˜μ΄μ§€λŠ” μžλ™μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.// ---