RESTful 라우트는 REST (Representational State Transfer) 아키텍처 스타일에 기반한 웹 서비스의 자원(리소스) 접근 및 조작 방법을 정의하는 URL 패턴이다.
REST는 클라이언트가 서버 자원에 접근하고, 이를 처리하기 위한 일관성 있는 규칙을 제공하며, 주로 HTTP 메서드와 URL을 통해 자원에 대해 작업을 수행한다.
RESTful 라우트는 기본적으로 HTTP 메서드를 통해 CRUD 작업(Create, Read, Update, Delete)을 한다.
| 종류 | 의미 |
|---|---|
| GET | 자원 조회 |
| POST | 새로운 자원 생성 |
| PUT | 자원 전체 수정 (또는 생성) |
| PATCH | 자원 일부 수정 |
| DELETE | 자원 삭제 |
GET: 데이터가 URL의 쿼리 파라미터(Query Parameters)로 전달된다. 이 방식은 URL의 뒤에 ?와 함께 파라미터가 붙는 형태이다.
예시: GET /search?query=hello&sort=asc
제한 사항: URL에 포함되는 데이터는 보통 브라우저와 서버에 따라 문자 수 제한(대략 2048자)이 있습니다.
POST: 데이터가 HTTP 요청 본문(Body)에 담겨 전달된다.
GET: 쿼리 파라미터에 데이터가 URL에 노출되므로, URL에 데이터가 직접 표시된다. 민감한 데이터는 전송할 수 없다.
POST: 요청 본문에 데이터를 담기 때문에 데이터가 URL에 노출되지 않는다. 민감한 데이터를 전송하는 데에는 더 적합하지만 POST 요청도 HTTPS가 아닌 경우에는 여전히 보안 위험이 크다.
GET: 캐시가 가능하다. 브라우저나 프록시 서버가 GET 요청의 응답을 캐시할 수 있으므로, 동일한 GET 요청을 보냈을 때 서버에 요청을 보내지 않고 캐시된 데이터를 사용할 수 있다.
POST: 캐시되지 않는다. POST 요청은 주로 서버 상태를 변경하기 때문에 응답이 캐시되지 않고, 매번 서버에 요청이 전달된다.
GET: 멱등하다. 동일한 GET 요청을 여러 번 보내도 서버의 상태는 변경되지 않으며, 응답은 항상 동일하다. 이는 GET이 데이터를 조회하는 작업이기 때문에 서버의 상태를 변경하지 않기 때문이다.
POST: 멱등하지 않다. 동일한 POST 요청을 여러 번 보내면 서버에 새로운 자원이 여러 개 생성될 수 있거나, 서버의 상태가 매번 달라질 수 있다. POST는 서버에 데이터를 보내고 처리하는 작업이므로, 그 결과가 변할 수 있습니다.
멱등성
연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질
// app.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const port = 3000;
// EJS 설정
app.set('view engine', 'ejs');
// body-parser 미들웨어 사용 (POST 데이터 처리)
app.use(bodyParser.urlencoded({ extended: true }));
// GET 요청: 폼을 보여주는 라우트
app.get('/', (req, res) => {
res.render('index'); // index.ejs 파일을 렌더링
});
// POST 요청: 폼 데이터를 처리하는 라우트
app.post('/register', (req, res) => {
const { name, email } = req.body; // 폼에서 전송된 데이터
// 여기서 데이터를 저장하거나 처리할 수 있음 (예: 데이터베이스 저장)
res.send(`User Registered: Name - ${name}, Email - ${email}`);
});
// 서버 시작
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
<!-- views/index.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST Request Example</title>
</head>
<body>
<h1>User Registration</h1>
<form action="/register" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required><br><br>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required><br><br>
<button type="submit">Register</button>
</form>
</body>
</html>

Register 클릭 후 결과
body-parser 미들웨어는 Express.js에서 HTTP 요청의 본문(body) 데이터를 처리하기 위해 사용되는 미들웨어이다.
body-parser 미들웨어는 다양한 포맷의 데이터를 파싱할 수 있으며, 가장 많이 사용되는 두 가지는 다음과 같다.
urlencoded : HTML 폼 데이터와 같은 URL 인코딩된 데이터를 처리json : JSON 형식의 데이터를 처리// body-parser 사용 설정
app.use(bodyParser.json()); // JSON 형식의 데이터 파싱
app.use(bodyParser.urlencoded({ extended: true })); // URL-encoded 데이터 파싱
body-parser 미들웨어를 사용하지 않는다면
body-parser 또는 내장된express.json()/express.urlencoded()같은 미들웨어를 사용하지 않으면, POST 요청으로 보내진 데이터는 req.body에 저장되지 않는다. 즉, 서버에서 클라이언트가 보낸 데이터를 읽을 수 없게 됩니다.
extended: true : URL-encoded 데이터를 처리할 때 객체 형태로 중첩된 데이터를 허용한다. true일 경우, qs 라이브러리를 사용하여 복잡한 객체 구조를 파싱할 수 있다. 반면 false일 경우, querystring 모듈을 사용하여 간단한 문자열을 파싱한다.bodyParser.json() : JSON 형식의 요청 본문을 파싱하여 req.body에 넣는다.
bodyParser.urlencoded({ extended: true }) : URL-encoded 형식의 폼 데이터를 파싱하여 req.body에 넣는다.
HTML 폼에서는 GET과 POST 메서드만 지원된다.
이를 해결하기 위해서는 HTTP 요청의 메서드를 Override해서 나머지 HTTP 메서드(DELETE, PUT 등)를 구현해야한다. 이를 지원해주는 것이 method-override이다.
method-override doc
https://expressjs.com/id/resources/middleware/method-override.html
npm install method-override
// DELETE 요청 처리
app.delete('/delete', (req, res) => {
res.send('Item deleted');
});
JS는 여태 적어왔던 방법과 동일하게 코드를 작성하면 된다.
<form action="/delete" method="POST">
<input type="hidden" name="_method" value="DELETE">
<button type="submit">Delete Item</button>
</form>
서버에서는 이 폼이 제출되면 POST 요청으로 받아들이지만, method-override 미들웨어를 통해 _method 값이 DELETE이기 때문에 해당 요청을 DELETE로 처리한다.
/myapp
│
├── index.js
├── package.json
└── views
├── index.ejs
├── DB.ejs
├── post.ejs
└── show.ejs
{
"name": "nodejs",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@types/node": "^18.0.6",
"body-parser": "^1.20.3",
"ejs": "^3.1.10",
"express": "^4.21.0",
"method-override": "^3.0.0",
"nodemon": "^3.1.7",
"uuid": "^10.0.0"
}
}
// app.js
const express = require('express');
const bodyParser = require('body-parser');
const {v4: uuid} = require('uuid')
const methodOverride = require('method-override')
const app = express();
const port = 3000;
let myDB = [
{
name : 'Park',
age: '20',
gender: 'male',
id: uuid()
},
{
name : 'Lee',
age: '23',
gender: 'male',
id: uuid()
},
{
name : 'Kim',
age: '21',
gender: 'female',
id: uuid()
}
]
// EJS 설정
app.set('view engine', 'ejs');
// body-parser 미들웨어 사용 (POST 데이터 처리)
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// 폼 methodOverride 미들웨어 사용 (PUT, DELETE)
app.use(methodOverride('_method'))
// GET 요청: 폼을 보여주는 라우트
app.get('/', (req, res) => {
res.render('index'); // index.ejs 파일을 렌더링
});
//DB 명단
app.get('/DB', (req, res) => {
res.render('DB', {
myDB: myDB
});
})
// Post: DB 데이터 추가
app.get('/post', (req, res) => {
res.render('post'); // post.ejs 파일을 렌더링
});
app.post('/post',(req, res)=>{
const {name, age, gender} = req.body;
myDB.push({name, age, gender, id: uuid() });
res.redirect('/DB');
})
// show: DB 데이터 조회
app.get('/DB/:id', (req, res) =>{
const {id} = req.params;
const targetDB = myDB.find(db => db.id === id);
res.render('show', {targetDB})
})
// delete: DB 데이터 삭제
app.delete('/DB/:id', (req, res) =>{
const {id} = req.params;
myDB = myDB.filter(myDB => myDB.id !== id) //id가 다른 것만 골라서 myDB를 재정의, 즉 id가 동일한 index 삭제
res.redirect('/DB')
})
// 서버 시작
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>명단</title>
</head>
<body>
<h1>명단</h1>
<ul>
<% for(let person of myDB){ %>
<li>name: <b><%= person.name %></b>, age: <b><%= person.age %></b>, gender: <b> <%= person.gender %></b>
<a href='/DB/<%= person.id %>'>정보</a>
<form action="/DB/<%= person.id %>?_method=DELETE" method="POST">
<button>delete</button>
</form>
</li>
<% } %>
</ul>
<a href="/post">new!</a>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>HOME</title>
</head>
<body>
<h2>my DB</h2>
<a href="/post">DB 추가</a> <br>
<a href="/DB">DB 명단</a>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>post</title>
</head>
<body>
<form action="/post" method="post">
<label>name</label>
<input type="text" name="name">
<br>
<label>age</label>
<input type="text" name="age">
<br>
<label>gender</label>
<input type="text" name="gender">
<br>
<input type="submit">
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>Show</title>
</head>
<body>
name: <b><%= targetDB.name %></b> <br>
age: <b><%= targetDB.age %></b> <br>
gender: <b> <%= targetDB.gender %></b> <br>
id: <b> <%= targetDB.id %></b> <br>
<a href="/DB">DB</a>
</body>
</html>
