์ค๋์ ์ด์ ๋ง๋ mini-node server๋ฅผ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์ฌ, express๋ก ๋ฆฌํฉํ ๋งํด๋ณผ ์์ ์ด๋ค - Express ๊ณต์ ๋ฌธ์
MERN stack์ JS ์ํ๊ณ์์ ์ธ๊ธฐ ์๋ ํ๋ ์์ํฌ์ธ MongoDB, Express, React, Node.js๋ฅผ ์ง์นญํ๋ค.
์ด ์ค์์ Express๋ Node.js ํ๊ฒฝ์์ ์น ์๋ฒ, ๋๋ API ์๋ฒ๋ฅผ ์ ์ํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ ์ธ๊ธฐ ์๋ ํ๋ ์์ํฌ๋ค.
Express๋ก ๊ตฌํํ ์๋ฒ๊ฐ Node.js HTTP ๋ชจ๋๋ก ์์ฑํ ์๋ฒ์ ๋ค๋ฅธ ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
1. Express ์ค์น
npm install express
2. ๊ฐ๋จํ ์น ์๋ฒ ๋ง๋ค๊ธฐ
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
3. ๋ผ์ฐํ : ๋ฉ์๋์ url์ ๋ฐ๋ผ ๋ถ๊ธฐ(Routing)ํ๊ธฐ
๋ฉ์๋์ url(/lower, /upper ๋ฑ)๋ก ๋ถ๊ธฐ์ ์ ๋ง๋๋ ๊ฒ์ ๋ผ์ฐํ (Routing)์ด๋ผ๊ณ ํ๋ค.
ํด๋ผ์ด์ธํธ๋ HTTP ์์ฒญ ๋ฉ์๋(GET, POST ๋ฑ)์ ํจ๊ป ์๋ฒ์ ํน์ URI(๋๋ ๊ฒฝ๋ก)๋ก HTTP ์์ฒญ์ ๋ณด๋ธ๋ค.
๋ผ์ฐํ
์ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ํด๋นํ๋ Endpoint์ ๋ฐ๋ผ ์๋ฒ๊ฐ ์๋ตํ๋ ๋ฐฉ๋ฒ์ ๊ฒฐ์ ํ๋ ๊ฒ์ด๋ค.
์ถ๊ฐ์ ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ , ์์ํ Node.js๋ก ์ฝ๋๋ฅผ ์์ฑํ๋ฉด, ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ ์ ์๋ค.
const requestHandler = (req, res) => {
if(req.url === '/lower') {
if (req.method === 'GET') {
res.end(data)
} else if (req.method === 'POST') {
req.on('data', (req, res) => {
// do something ...
})
}
}
}
const router = express.Router()
router.get('/lower', (req, res) => {
res.send(data);
})
router.post('/lower', (req, res) => {
// do something
})
์๋์ฐจ ๊ณต์ฅ์์๋ ์ปจ๋ฒ ์ด์ด ๋ฒจํธ ์์ ์ฌ๋ ค์ง ์๋์ฐจ์ ๋ผ๋์, ๊ณต์ ๋ง๋ค ๋ถํ์ ์ถ๊ฐํ๋ค.
๋ชจ๋ ๋ถํ์ด ์ถ๊ฐ๋๋ฉด ์์ฑ๋ ์๋์ฐจ๊ฐ, ์ด๋๊ฐ ๋ฌธ์ ๊ฐ ์๋ค๋ฉด ๋ถ๋ํ์ด ๊ฒฐ๊ณผ๋ฌผ๋ก ๋์ค๊ฒ ๋๋ค.
๋ฏธ๋ค์จ์ด(Middleware)๋ ์๋์ฐจ ๊ณต์ฅ์ ๊ณต์ ๊ณผ ๋น์ทํ๋ค.
์ปจ๋ฒ ์ด์ด ๋ฒจํธ ์์ ์ฌ๋ผ๊ฐ ์๋ ์์ฒญ(Request)์ ํ์ํ ๊ธฐ๋ฅ์ ๋ํ๊ฑฐ๋, ๋ถ๋ํ์ ๋ฐ์ผ๋ก ๊ฑท์ด๋ด๋ ์ญํ ์ ํ๋ค.
๋ฏธ๋ค์จ์ด๋ express์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ด๋ผ๊ณ ํ ์ ์๋ค.
๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ๋ ์ํฉ์ ๋ค์๊ณผ ๊ฐ๋ค.
๋ฏธ๋ค์จ์ด๋ฅผ ์ด์ฉํ๋ฉด Node.js ๋ง์ผ๋ก ๊ตฌํํ ์๋ฒ์์๋ ๋ฒ๊ฑฐ๋ก์ธ ์ ์๋ ์์ ์ ๋ณด๋ค ์ฝ๊ฒ ์ ์ฉํ ์ ์๋ค.
case 1: POST ์์ฒญ ๋ฑ์ ํฌํจ๋ body(payload)๋ฅผ ๊ตฌ์กฐํํ ๋
// Node.js๋ก HTTP ์์ฒญ body๋ฅผ ๋ฐ๋ ์ฝ๋
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// body ๋ณ์์๋ ๋ฌธ์์ด ํํ๋ก payload๊ฐ ๋ด๊ฒจ์ ธ ์์ต๋๋ค.
});
// express.json()์ ์ด์ฉํ ์ฝ๋
const jsonParser = express.json();
// ์๋ต
app.post('/api/users', jsonParser, function (req, res) {
})
Node.js๋ก HTTP body(payload)๋ฅผ ๋ฐ์ ๋๋ Buffer๋ฅผ ์กฐํฉํด์ ๋ค์ ๋ณต์กํ ๋ฐฉ์์ผ๋ก body๋ฅผ ์ป์ ์ ์๋ค.
๋คํธ์ํฌ์์ chunk๋ฅผ ํฉ์น๊ณ , buffer๋ฅผ ๋ฌธ์์ด๋ก ๋ณํํ๋ ์์
์ด ํ์ํ๋ค.
Express๋ฅผ ์ฌ์ฉํ๋ฉด ์ด ๊ณผ์ ์ ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
Express v4.16.0๋ถํฐ๋ body-parser๋ฅผ ๋ฐ๋ก ์ค์นํ์ง ์๊ณ , Express ๋ด์ฅ ๋ฏธ๋ค์จ์ด์ธ express.json()์ ์ฌ์ฉํ๋ค.
case 2: ๋ชจ๋ ์์ฒญ/์๋ต์ CORS ํค๋๋ฅผ ๋ถ์ผ ๋
const defaultCorsHeader = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Accept',
'Access-Control-Max-Age': 10
};
...
if (req.method === 'OPTIONS') {
res.writeHead(200, defaultCorsHeader);
res.end()
}
// npm์ผ๋ก cors ์ค์น
npm install cors
// ๋ชจ๋ ์์ฒญ์ ๋ํด CORS ํ์ฉ โจ
const cors = require('cors');
...
app.use(cors());
// ํน์ ์์ฒญ์ ๋ํด CORS ํ์ฉ
const cors = require('cors')
...
app.get('/products/:id', cors(), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for a Single Route'})
})
Node.js HTTP ๋ชจ๋์ ์ด์ฉํ ์ฝ๋์ CORS ํค๋๋ฅผ ๋ถ์ด๋ ค๋ฉด, ์๋ต ๊ฐ์ฒด์ writeHead ๋ฉ์๋๋ฅผ ์ด์ฉํ ์ ์๋ค.
Node.js์์๋ ์ด ๋ฉ์๋ ๋ฑ์ ์ด์ฉํ์ฌ ๋ผ์ฐํ
๋ง๋ค ํค๋๋ฅผ ๋งค๋ฒ ๋ฃ์ด์ฃผ์ด์ผ ํ๋ค.
๊ทธ๋ฟ๋ง ์๋๋ผ, OPTIONS ๋ฉ์๋์ ๋ํ ๋ผ์ฐํ
๋ ๋ฐ๋ก ๊ตฌํํด์ผ ํ๋ค.
cors ๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ๋ฉด ์ด ๊ณผ์ ์ ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
๐ก ๋ฏธ๋ค์จ์ด ์๋ ์๋ฆฌ ์ดํดํ๊ธฐ
case 3: ๋ชจ๋ ์์ฒญ์ ๋ํด url์ด๋ ๋ฉ์๋๋ฅผ ํ์ธํ ๋
๋ฏธ๋ค์จ์ด๋ ๋ง ๊ทธ๋๋ก ํ๋ก์ธ์ค ์ค๊ฐ์ ๊ด์ฌํ์ฌ ํน์ ์ญํ ์ ์ํํ๋ค.
์๋ง์ ๋ฏธ๋ค์จ์ด๊ฐ ์์ง๋ง, ๊ฐ์ฅ ๋จ์ํ ๋ฏธ๋ค์จ์ด ๋ก๊ฑฐ(logger)๋ฅผ ์๋ก ๋ค๊ฒ ๋ค.
๋ก๊ฑฐ๋ ๋๋ฒ๊น
์ด๋, ์๋ฒ ๊ด๋ฆฌ์ ๋์์ด ๋๊ธฐ ์ํด console.log๋ก ์ ์ ํ ๋ฐ์ดํฐ๋ ์ ๋ณด๋ฅผ ์ถ๋ ฅํ๋ค.
๋ฐ์ดํฐ๊ฐ ์ฌ๋ฌ ๋ฏธ๋ค์จ์ด๋ฅผ ๊ฑฐ์น๋ ๋์ ์๋ตํ ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค๋ฉด,
๋ฏธ๋ค์จ์ด ์ฌ์ด์ฌ์ด์ ๋ก๊ฑฐ๋ฅผ ์ฝ์
ํ์ฌ ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ํ์ธํ๊ฑฐ๋, ๋๋ฒ๊น
์ ์ฌ์ฉํ ์ ์๋ค.
์ ๊ทธ๋ฆผ์ endpoint๊ฐ /์ด๋ฉด์, ํด๋ผ์ด์ธํธ๋ก๋ถํฐ GET ์์ฒญ์ ๋ฐ์์ ๋ ์ ์ฉ๋๋ ๋ฏธ๋ค์จ์ด๋ค.
ํ๋ผ๋ฏธํฐ์ ์์์ ์ ์ํด์ผ ํ๋ค.
req, res๋ ์ฐ๋ฆฌ๊ฐ ์ ์๋ ์์ฒญ(request), ์๋ต(response)์ด๊ณ next๋ ๋ค์ ๋ฏธ๋ค์จ์ด๋ฅผ ์คํํ๋ ์ญํ ์ ํ๋ค.
case 4: ์์ฒญ ํค๋์ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๊ฐ ๋ด๊ฒจ์๋์ง ํ์ธํ ๋
app.use((req, res, next) => {
// ํ ํฐ์ด ์๋์ง ํ์ธ, ์์ผ๋ฉด ๋ฐ์์ค ์ ์์.
if(req.headers.token){
req.isLoggedIn = true;
next();
} else {
res.status(400).send('invalid user')
}
})
HTTP ์์ฒญ์์ ํ ํฐ์ด ์๋์ง๋ฅผ ํ๋จํ์ฌ, ์ด๋ฏธ ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ผ ๊ฒฝ์ฐ ์ฑ๊ณต, ์๋ ๊ฒฝ์ฐ ์๋ฌ๋ฅผ ๋ณด๋ด๋ ๋ฏธ๋ค์จ์ด ์์ ์ด๋ค.
Node.js HTTP ๋ชจ๋์ ์ด์ฉํ ์ฝ๋๋ฅผ Express๋ก ๋ณํํ๋ฉด์ ์ฐพ์ ํคํฌ์ธํธ๊ฐ ๋ฑ 3๊ฐ ์์๋ค.
res.send
๋ ๊ฐ์ฒด ๋ฆฌํฐ๋ด์ ์ฌ์ฉํด์ ํํ3๋ฒ์ ์ถ๊ฐ ์ค๋ช ํ๋ฉด, ๋์ ์ฐจ์ด์ ์ ๋์ฑ ๋ ๋๋ฌ๋๋ค.
Express์์๋ res.send({ convertedText })์ ๊ฐ์ด ๊ฐ์ฒด ๋ฆฌํฐ๋ด์ ์ฌ์ฉํ์ฌ ์๋ต ๋ฐ์ดํฐ๋ฅผ ํํํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด์ง๋ง,
์์ํ Node.js HTTP ๋ชจ๋์์๋ response.end(convertedText)์ ๊ฐ์ด ์๋ต ๋ณธ๋ฌธ์ ์ง์ ์ง์ ํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.
์ด์ ๊ธฐ์กด ์ฝ๋์ express ๋ฒ์ ์ฝ๋๋ฅผ ์ฌ๋ ค๋ณด๊ฒ ๋ค.
// ๊ธฐ์กด ์ฝ๋
const http = require("http");
const PORT = 4999;
const ip = "localhost";
const server = http.createServer((request, response) => {
// GET / ์์ฒญ ์ฒ๋ฆฌ
if (request.method === "GET" && request.url === "/") {
response.writeHead(200, defaultCorsHeader); // 200 OK ์๋ต ์ฝ๋์ CORS ํค๋๋ฅผ ๋ฐํ
response.end("hello mini-server sprints"); // "hello mini-server sprints"๋ฅผ ์๋ต์ผ๋ก ๋ณด๋
}
// OPTIONS ๋ฉ์๋์ ๋ํ ์ฒ๋ฆฌ
else if (request.method === "OPTIONS") {
response.writeHead(204, defaultCorsHeader); // 204 No Content ์๋ต ์ฝ๋์ CORS ํค๋๋ฅผ ๋ฐํ
response.end();
return;
}
// POST /lower ์์ฒญ ์ฒ๋ฆฌ
else if (request.method === "POST" && request.url === "/lower") {
let requestBody = "";
request.on("data", (text) => {
requestBody += text.toString();
});
request.on("end", () => {
const convertedText = requestBody.toLowerCase(); // ๋ฐ์ ํ
์คํธ๋ฅผ ์๋ฌธ์๋ก ๋ณํ
response.writeHead(200, defaultCorsHeader); // 200 OK ์๋ต ์ฝ๋์ CORS ํค๋๋ฅผ ๋ฐํ
response.end(convertedText); // ๋ณํ๋ ํ
์คํธ๋ฅผ ์๋ต์ผ๋ก ๋ณด๋
});
}
// POST /upper ์์ฒญ ์ฒ๋ฆฌ
else if (request.method === "POST" && request.url === "/upper") {
let requestBody = "";
request.on("data", (text) => {
requestBody += text.toString();
});
request.on("end", () => {
const convertedText = requestBody.toUpperCase(); // ๋ฐ์ ํ
์คํธ๋ฅผ ๋๋ฌธ์๋ก ๋ณํ
response.writeHead(200, defaultCorsHeader); // 200 OK ์๋ต ์ฝ๋์ CORS ํค๋๋ฅผ ๋ฐํ
response.end(convertedText); // ๋ณํ๋ ํ
์คํธ๋ฅผ ์๋ต์ผ๋ก ๋ณด๋
});
}
// ๊ทธ ์ธ์ ์์ฒญ์ ๋ํ ์ฒ๋ฆฌ (404 Not Found)
else {
response.statusCode = 404; // 404 Not Found ์๋ต ์ฝ๋๋ฅผ ์ค์
response.end(); // ์๋ต ์ข
๋ฃ
}
console.log(
`http request method is ${request.method}, url is ${request.url}`
);
});
server.listen(PORT, ip, () => {
console.log(`http server listen on ${ip}:${PORT}`);
});
// ์๋ต ํค๋
const defaultCorsHeader = {
"Access-Control-Allow-Origin": "*", // ๋ชจ๋ ์ถ์ฒ์์์ ์์ฒญ์ ํ์ฉ
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", // ์ํ๋ HTTP ๋ฉ์๋๋ฅผ ๋ช
์
"Access-Control-Allow-Headers": "Content-Type, Accept", // ํ์ฉํ๋ ์์ฒญ ํค๋๋ฅผ ๋ช
์
"Access-Control-Max-Age": 10, // Preflight ์์ฒญ์ ์บ์ฑ ์๊ฐ์ ์ค์
};
// Express ์ฝ๋
const express = require("express");
const app = express(); // Express ์ ํ๋ฆฌ์ผ์ด์
์์ฑ
const PORT = 3999; // ๊ธฐ์กด basic ์๋ฒ์ port ๋ฒํธ ๋ค๋ฅด๊ฒ
const ip = "localhost";
const cors = require("cors");
app.use(cors());
// JSON ํ์ฑ์ ์ํ ๋ฏธ๋ค์จ์ด ๋ฑ๋ก
// ์ด ์ค์ ์ด ๋น ์ง๋ฉด ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ๋ก ์๋ชป๋ ์์ฒญ์ ๋ณด๋์ ๋ ๋ฐ์ํ๋ ์ค๋ฅ,
// ์๋ฒ์์ ์ ์กํ ์๋ต์ด JSON ํ์์ด ์๋๋ผ HTML ํ์์ผ๋ก ๋์ฐฉํจ
app.use(express.json({ strict: false })); // ์ด๋ฒ ๊ฒฝ์ฐ๋ json์ ๋ฐฐ์ด์ด๋ ๊ฐ์ฒด๊ฐ ์๋ ๋ฌธ์์ด๋ก ๋ณด๋ด๊ธฐ๋๋ฌธ์ strict: false
// GET / ์์ฒญ ์ฒ๋ฆฌ
app.get("/", (req, res) => {
res.send("hello mini-server sprints"); // "hello mini-server sprints"๋ฅผ ์๋ต์ผ๋ก ๋ณด๋
});
// OPTIONS ๋ฉ์๋์ ๋ํ ์ฒ๋ฆฌ
app.options("/", (req, res) => {
res.sendStatus(204); // 204 No Content ์๋ต ์ฝ๋๋ฅผ ๋ฐํ
});
// POST /upper ์์ฒญ ์ฒ๋ฆฌ
app.post("/upper", (req, res) => {
const requestBody = req.body;
const convertedText = requestBody.toUpperCase(); // ๋ฐ์ ํ
์คํธ๋ฅผ ๋๋ฌธ์๋ก ๋ณํ
res.send({ convertedText }); // ๋ณํ๋ ํ
์คํธ๋ฅผ ์๋ต์ผ๋ก ๋ณด๋
});
// POST /lower ์์ฒญ ์ฒ๋ฆฌ
app.post("/lower", (req, res) => {
const requestBody = req.body;
const convertedText = requestBody.toLowerCase(); // ๋ฐ์ ํ
์คํธ๋ฅผ ์๋ฌธ์๋ก ๋ณํ
res.send({ convertedText }); // ๋ณํ๋ ํ
์คํธ๋ฅผ ์๋ต์ผ๋ก ๋ณด๋
});
// ๊ทธ ์ธ์ ์์ฒญ์ ๋ํ ์ฒ๋ฆฌ (404 Not Found)
app.use((req, res) => {
res.sendStatus(404); // 404 Not Found ์๋ต ์ฝ๋๋ฅผ ๋ฐํ
});
// ์๋ฒ ์์
app.listen(PORT, ip, () => {
console.log(`Express server listening on ${ip}:${PORT}`);
});
ํ์คํ express ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ฉด์ ์ฝ๋๊ฐ ๊ฐ๊ฒฐํด์ง ๊ฒ์ ํ ๋์๋ ์์๋ณผ ์ ์๋ค.
์ฌ์ค ํค ํฌ์ธํธ 3๊ฐ๋ ๋ด๊ฐ ์ฝ๋ ์ง๋ฉด์ ๋นผ๋จน๊ณ , ์ค๋ฅ๋ฅผ ๋์ ๋ ์ผ์ด์ค๋ผ ์์ผ๋ก๋ ์ ๋ ์ ๊น๋จน์ ๊ฒ ๊ฐ๋ค ๐