====== π Express.js μΉ νλ μμν¬ ======
Phaser Baduk Metaverse νλ‘μ νΈμ ''Express.js'' μΉ νλ μμν¬ κ΅¬νμ λν΄ μ€λͺ
ν©λλ€.
----
===== 1. Express.jsλ 무μμΈκ°μ? =====
''Express.js''λ ''Node.js''λ₯Ό μν λΉ λ₯΄κ³ μ μ°ν μΉ μ ν리μΌμ΄μ
νλ μμν¬μ
λλ€. μΉ μλ²λ₯Ό μ½κ² λ§λ€ μ μκ² ν΄μ£Όλ λꡬλΌκ³ μκ°νμλ©΄ λ©λλ€.
**μ£Όμ νΉμ§:**
* μΉ μλ²λ₯Ό λΉ λ₯΄κ² λ§λ€ μ μμ΅λλ€.
* ''RESTful API''λ₯Ό μ½κ² ꡬνν μ μμ΅λλ€.
* μ μ νμΌ(''HTML'', ''CSS'', μ΄λ―Έμ§ λ±)μ μ 곡ν μ μμ΅λλ€.
* λ―Έλ€μ¨μ΄λ₯Ό ν΅ν΄ κΈ°λ₯μ νμ₯ν μ μμ΅λλ€.
----
===== 2. νλ‘μ νΈ κ΅¬μ‘° μ΄ν΄νκΈ° =====
**Express.js νλ‘μ νΈμ κΈ°λ³Έ ꡬ쑰:**
* ''server.js'' - λ©μΈ μλ² νμΌ
* ''routes/'' - λΌμ°νΈ(URL κ²½λ‘) κ΄λ¦¬ ν΄λ
* ''controllers/'' - λΉμ¦λμ€ λ‘μ§ κ΄λ¦¬ ν΄λ
* ''middleware/'' - λ―Έλ€μ¨μ΄(μ€κ° μ²λ¦¬) ν΄λ
* ''public/'' - ν΄λΌμ΄μΈνΈμκ² μ 곡ν νμΌλ€
----
===== 3. κΈ°λ³Έ μλ² μ€μ =====
==== 1) νμν λͺ¨λ λΆλ¬μ€κΈ° ====
λ¨Όμ νμν λͺ¨λλ€μ λΆλ¬μ΅λλ€:
const express = require('express');
const path = require('path');
const cors = require('cors');
const helmet = require('helmet');
const compression = require('compression');
const rateLimit = require('express-rate-limit');
**κ° λͺ¨λμ μν :**
* ''express'' - μΉ μλ² νλ μμν¬
* ''path'' - νμΌ κ²½λ‘ μ²λ¦¬
* ''cors'' - λ€λ₯Έ λλ©μΈμμμ μ κ·Ό νμ©
* ''helmet'' - 보μ κ°ν
* ''compression'' - νμΌ μμΆ
* ''rateLimit'' - μμ² μ ν
==== 2) Express μ± μμ± ====
const app = express();
const PORT = process.env.PORT || 3000;
**μ€λͺ
:**
* ''app'' - Express μ ν리μΌμ΄μ
μΈμ€ν΄μ€
* ''PORT'' - μλ²κ° μ€νλ ν¬νΈ λ²νΈ (κΈ°λ³Έκ°: 3000)
==== 3) 보μ λ―Έλ€μ¨μ΄ μ€μ ====
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "ws:", "wss:"]
}
}
}));
**μ΄ μ€μ μ λͺ©μ :**
* μΉμ¬μ΄νΈ 보μμ κ°νν©λλ€.
* μ
μ± μ€ν¬λ¦½νΈ μ€νμ λ°©μ§ν©λλ€.
* νμ©λ 리μμ€λ§ λ‘λν μ μκ² ν©λλ€.
==== 4) CORS μ€μ ====
app.use(cors({
origin: process.env.NODE_ENV === 'production'
? ['https://yourdomain.com']
: ['http://localhost:3000', 'http://localhost:8080'],
credentials: true
}));
**CORSλ?**
* λ€λ₯Έ λλ©μΈμμμ μ κ·Όμ μ μ΄νλ 보μ μ μ±
μ
λλ€.
* κ°λ° νκ²½μμλ ''localhost:3000'', ''localhost:8080''μ νμ©ν©λλ€.
* μ΄μ νκ²½μμλ νΉμ λλ©μΈ(''https://yourdomain.com'')λ§ νμ©ν©λλ€.
==== 5) μμΆ λ° μμ² μ ν μ€μ ====
// μμΆ λ―Έλ€μ¨μ΄
app.use(compression());
// μμ² μ ν μ€μ
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15λΆ
max: 100, // IPλΉ μ΅λ 100κ° μμ²
message: 'λ무 λ§μ μμ²μ΄ λ°μνμ΅λλ€. μ μ ν λ€μ μλν΄μ£ΌμΈμ.'
});
app.use('/api/', limiter);
**μ€λͺ
:**
* ''compression'' - νμΌ ν¬κΈ°λ₯Ό μ€μ¬μ μ μ‘ μλλ₯Ό ν₯μμν΅λλ€.
* ''rateLimit'' - ν λ²μ λ무 λ§μ μμ²μ 보λ΄λ κ²μ λ°©μ§ν©λλ€.
==== 6) λ°μ΄ν° νμ± μ€μ ====
// JSON νμ± λ―Έλ€μ¨μ΄
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
**μ€λͺ
:**
* ''express.json()'' - ''JSON'' ννμ λ°μ΄ν°λ₯Ό νμ±ν©λλ€.
* ''express.urlencoded()'' - νΌ λ°μ΄ν°λ₯Ό νμ±ν©λλ€.
* ''limit: '10mb''' - μ΅λ ''10mb''κΉμ§ νμ©ν©λλ€.
==== 7) μ μ νμΌ μλΉ μ€μ ====
// μ μ νμΌ μλΉ
app.use(express.static(path.join(__dirname, 'public'), {
maxAge: '1d',
etag: true
}));
**μ€λͺ
:**
* ''public'' ν΄λμ νμΌλ€μ μΉμμ μ κ·Ό κ°λ₯νκ² ν©λλ€.
* ''maxAge: '1d''' - νμΌμ 1μΌκ° μΊμν©λλ€.
* ''etag: true'' - νμΌ λ³κ²½ κ°μ§λ₯Ό μν νκ·Έλ₯Ό μ¬μ©ν©λλ€.
==== 8) λ‘κΉ
λ―Έλ€μ¨μ΄ ====
// λ‘κΉ
λ―Έλ€μ¨μ΄
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next();
});
**μ€λͺ
:**
* λͺ¨λ μμ²μ λν΄ λ‘κ·Έλ₯Ό λ¨κΉλλ€.
* μμ² μκ°, λ°©λ²(''GET'', ''POST'' λ±), ''URL''μ κΈ°λ‘ν©λλ€.
* λλ²κΉ
μ λ§€μ° μ μ©ν©λλ€.
----
===== 4. λΌμ°νΈ μ€μ =====
==== 1) λΌμ°νΈ νμΌ λΆλ¬μ€κΈ° ====
// λΌμ°νΈ μ€μ
app.use('/api/games', require('./routes/games'));
app.use('/api/users', require('./routes/users'));
app.use('/api/statistics', require('./routes/statistics'));
**μ€λͺ
:**
* ''/api/games'' - κ²μ κ΄λ ¨ ''API''
* ''/api/users'' - μ¬μ©μ κ΄λ ¨ ''API''
* ''/api/statistics'' - ν΅κ³ κ΄λ ¨ ''API''
==== 2) λ©μΈ νμ΄μ§ λΌμ°νΈ ====
// λ©μΈ νμ΄μ§
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
**μ€λͺ
:**
* λ£¨νΈ κ²½λ‘(''/ '')λ‘ μ κ·Όνλ©΄ ''index.html'' νμΌμ μ 곡ν©λλ€.
* μΉμ¬μ΄νΈμ 첫 νμ΄μ§μ
λλ€.
----
===== 5. μλ¬ μ²λ¦¬ =====
==== 1) 404 μλ¬ μ²λ¦¬ ====
// 404 μλ¬ μ²λ¦¬
app.use((req, res) => {
res.status(404).json({ error: 'νμ΄μ§λ₯Ό μ°Ύμ μ μμ΅λλ€.' });
});
**μ€λͺ
:**
* μ‘΄μ¬νμ§ μλ νμ΄μ§μ μ κ·Όν λ μ€νλ©λλ€.
* ''404'' μν μ½λμ μλ¬ λ©μμ§λ₯Ό λ°νν©λλ€.
==== 2) μλ² μλ¬ μ²λ¦¬ ====
// μλ¬ νΈλ€λ§ λ―Έλ€μ¨μ΄
app.use((err, req, res, next) => {
console.error('μλ² μλ¬:', err);
res.status(500).json({ error: 'μλ² λ΄λΆ μ€λ₯κ° λ°μνμ΅λλ€.' });
});
**μ€λͺ
:**
* μλ²μμ λ°μνλ λͺ¨λ μλ¬λ₯Ό μ²λ¦¬ν©λλ€.
* μλ¬ λ‘κ·Έλ₯Ό μ½μμ μΆλ ₯ν©λλ€.
* ν΄λΌμ΄μΈνΈμκ²λ μΌλ°μ μΈ μλ¬ λ©μμ§λ₯Ό μ μ‘ν©λλ€.
----
===== 6. κ²μ λΌμ°νΈ μμ =====
==== 1) κ²μ λΌμ°νΈ κΈ°λ³Έ ꡬ쑰 ====
const express = require('express');
const router = express.Router();
const BadukGameController = require('../controllers/BadukGameController');
const auth = require('../middleware/auth');
**μ€λͺ
:**
* ''express.Router()'' - λΌμ°ν° κ°μ²΄ μμ±
* ''BadukGameController'' - κ²μ κ΄λ ¨ λΉμ¦λμ€ λ‘μ§
* ''auth'' - μΈμ¦ λ―Έλ€μ¨μ΄
==== 2) κ²μ λͺ©λ‘ μ‘°ν ====
// κ²μ λͺ©λ‘ μ‘°ν
router.get('/', async (req, res) => {
try {
const games = await BadukGameController.getGameList(req.query);
res.json({
success: true,
data: games,
total: games.length
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
**μ€λͺ
:**
* ''GET /api/games'' - λͺ¨λ κ²μ λͺ©λ‘μ μ‘°νν©λλ€.
* ''try-catch'' - μλ¬ μ²λ¦¬λ₯Ό μν ꡬ문μ
λλ€.
* μ±κ³΅ μ κ²μ λͺ©λ‘κ³Ό μ΄ κ°μλ₯Ό λ°νν©λλ€.
* μ€ν¨ μ μλ¬ λ©μμ§λ₯Ό λ°νν©λλ€.
==== 3) μ κ²μ μμ± ====
// μ κ²μ μμ±
router.post('/', auth, async (req, res) => {
try {
const { gameType, settings } = req.body;
const game = await BadukGameController.createGame(req.user.id, gameType, settings);
res.status(201).json({
success: true,
data: game
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
});
**μ€λͺ
:**
* ''POST /api/games'' - μλ‘μ΄ κ²μμ μμ±ν©λλ€.
* ''auth'' - μΈμ¦λ μ¬μ©μλ§ μ κ·Ό κ°λ₯ν©λλ€.
* ''req.body'' - ν΄λΌμ΄μΈνΈκ° λ³΄λΈ λ°μ΄ν°μ
λλ€.
* ''201'' - μμ± μ±κ³΅ μν μ½λμ
λλ€.
==== 4) νΉμ κ²μ μ‘°ν ====
// νΉμ κ²μ μ‘°ν
router.get('/:gameId', async (req, res) => {
try {
const game = await BadukGameController.getGame(req.params.gameId);
if (!game) {
return res.status(404).json({
success: false,
error: 'κ²μμ μ°Ύμ μ μμ΅λλ€.'
});
}
res.json({
success: true,
data: game
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
**μ€λͺ
:**
* ''GET /api/games/:gameId'' - νΉμ κ²μ μ 보λ₯Ό μ‘°νν©λλ€.
* ''req.params.gameId'' - ''URL''μμ κ²μ ''ID''λ₯Ό μΆμΆν©λλ€.
* κ²μμ΄ μμΌλ©΄ ''404'' μλ¬λ₯Ό λ°νν©λλ€.
==== 5) κ²μ μ°Έκ° ====
// κ²μ μ°Έκ°
router.post('/:gameId/join', auth, async (req, res) => {
try {
const { gameId } = req.params;
const { playerName } = req.body;
const result = await BadukGameController.joinGame(gameId, req.user.id, playerName);
res.json({
success: true,
data: result
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
});
**μ€λͺ
:**
* ''POST /api/games/:gameId/join'' - νΉμ κ²μμ μ°Έκ°ν©λλ€.
* ''req.params.gameId'' - ''URL''μμ κ²μ ''ID''λ₯Ό κ°μ Έμ΅λλ€.
* ''req.body.playerName'' - μμ² λ³Έλ¬Έμμ νλ μ΄μ΄ μ΄λ¦μ κ°μ Έμ΅λλ€.
==== 6) κ²μ μ΄λ κΈ°λ‘ ====
// κ²μ μ΄λ κΈ°λ‘
router.post('/:gameId/move', auth, async (req, res) => {
try {
const { gameId } = req.params;
const { x, y, player } = req.body;
const result = await BadukGameController.makeMove(gameId, req.user.id, x, y, player);
res.json({
success: true,
data: result
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
});
**μ€λͺ
:**
* ''POST /api/games/:gameId/move'' - κ²μμμ λμ λμ΅λλ€.
* ''x, y'' - λ°λνμ μ’νμ
λλ€.
* ''player'' - νλ μ΄μ΄ μ 보μ
λλ€.
----
===== 7. μλ² μ€ν =====
==== 1) μλ² μμ ====
// μλ² μμ
app.listen(PORT, () => {
console.log(`μλ²κ° ν¬νΈ ${PORT}μμ μ€ν μ€μ
λλ€.`);
console.log(`http://localhost:${PORT}μμ μ μνμΈμ.`);
});
**μ€λͺ
:**
* μ§μ λ ν¬νΈμμ μλ²λ₯Ό μμν©λλ€.
* μλ² μμ μ μ½μμ λ©μμ§λ₯Ό μΆλ ₯ν©λλ€.
==== 2) λͺ¨λ λ΄λ³΄λ΄κΈ° ====
module.exports = app;
**μ€λͺ
:**
* λ€λ₯Έ νμΌμμ μ΄ μ±μ μ¬μ©ν μ μλλ‘ λ΄λ³΄λ
λλ€.
* ν
μ€νΈλ λ€λ₯Έ μλ²μμ μ¬μ©ν λ νμν©λλ€.
----
===== 8. μ€μ΅ μμ =====
==== 1) κ°λ¨ν Express μλ² λ§λ€κΈ° ====
**1λ¨κ³: νλ‘μ νΈ μ΄κΈ°ν**
npm init -y
npm install express
**2λ¨κ³: κΈ°λ³Έ μλ² νμΌ μμ± (''app.js'')**
const express = require('express');
const app = express();
const PORT = 3000;
// JSON νμ± λ―Έλ€μ¨μ΄
app.use(express.json());
// κ°λ¨ν λΌμ°νΈ
app.get('/', (req, res) => {
res.json({ message: 'μλ
νμΈμ! Express μλ²μ
λλ€.' });
});
app.get('/api/users', (req, res) => {
res.json([
{ id: 1, name: 'κΉμ² μ' },
{ id: 2, name: 'μ΄μν¬' }
]);
});
// μλ² μμ
app.listen(PORT, () => {
console.log(`μλ²κ° ν¬νΈ ${PORT}μμ μ€ν μ€μ
λλ€.`);
});
**3λ¨κ³: μλ² μ€ν**
node app.js
**4λ¨κ³: ν
μ€νΈ**
* λΈλΌμ°μ μμ ''http://localhost:3000'' μ μ
* ''http://localhost:3000/api/users'' μ μ
----
===== 9. μ£Όμμ¬νκ³Ό ν =====
==== 1) 보μ μ£Όμμ¬ν ====
* νμ ''helmet'' λ―Έλ€μ¨μ΄λ₯Ό μ¬μ©νμΈμ.
* λ―Όκ°ν μ 보λ νκ²½ λ³μλ‘ κ΄λ¦¬νμΈμ.
* μ
λ ₯ λ°μ΄ν°λ₯Ό κ²μ¦νμΈμ.
* ''HTTPS''λ₯Ό μ¬μ©νμΈμ.
==== 2) μ±λ₯ μ΅μ ν ====
* ''compression'' λ―Έλ€μ¨μ΄λ₯Ό μ¬μ©νμΈμ.
* μ μ νμΌ μΊμ±μ μ€μ νμΈμ.
* λ°μ΄ν°λ² μ΄μ€ μ°κ²° νμ μ¬μ©νμΈμ.
* λΆνμν λ―Έλ€μ¨μ΄λ₯Ό μ κ±°νμΈμ.
==== 3) λλ²κΉ
ν ====
* λ‘κΉ
λ―Έλ€μ¨μ΄λ₯Ό μ¬μ©νμΈμ.
* μλ¬ μ²λ¦¬ λ―Έλ€μ¨μ΄λ₯Ό μΆκ°νμΈμ.
* κ°λ° νκ²½μμλ μμΈν μλ¬ λ©μμ§λ₯Ό νμνμΈμ.
* νλ‘λμ
μμλ μΌλ°μ μΈ μλ¬ λ©μμ§λ₯Ό νμνμΈμ.
----
===== 10. λ€μ λ¨κ³ =====
''Express.js'' κΈ°λ³Έμ λ°°μ λ€λ©΄ λ€μμ νμ΅ν΄λ³΄μΈμ:
* **''Socket.IO''** - μ€μκ° ν΅μ ꡬν
* **λ°μ΄ν°λ² μ΄μ€ μ°λ** - ''MongoDB'', ''MySQL'' λ±
* **μΈμ¦ μμ€ν
** - ''JWT'', ''Passport.js'' λ±
* **νμΌ μ
λ‘λ** - ''Multer'' λ―Έλ€μ¨μ΄
* **''API'' λ¬Έμν** - ''Swagger'' λ±
**μΆμ² νμ΅ μμ:**
- [[wiki:it:dream_of_enc:metaverse:nodejs|π’ Node.js λ°±μλ μ€μ ]]
- [[wiki:it:dream_of_enc:metaverse:socketio|π Socket.IO μ€μκ° ν΅μ ]]
- [[wiki:it:dream_of_enc:metaverse:game_logic|π§ λ°λ κ²μ λ‘μ§]]
---
//μ΄ νμ΄μ§λ μλμΌλ‘ μμ±λμμ΅λλ€.//
---