HTTP(HyperText Transfer Protocol)는 웹 상에서 데이터를 주고받기 위한 규약이다. HTTP 프로토콜을 사용할 때는 다양한 종류의 요청 메소드가 있다. 이 중에서도 "멱등성(Idempotent)"이라는 특성을 가지는 요청 메소드에 대해서 중점적으로 좀 학습해보았다.
멱등성(Idempotent)은 같은 작업을 여러 번 수행하더라도 동일한 결과를 반환해야 한다는 특성이다. HTTP 컨텍스트에서 이는 같은 요청을 하나 이상 실행하더라도 서버의 상태는 동일하게 유지되어야 함을 의미한다.
HTTP 메소드 중에서는 GET, HEAD, PUT, DELETE 등이 멱등성을 가진다.
이제 Node.js와 Express 라이브러리를 사용해 간단한 RESTful API를 구축하며 멱등성을 확인하는 코드 예시를 살펴본다.
먼저, 새 프로젝트 폴더를 생성하고 필요한 노드 패키지를 설치한다.
npm init -y
npm install express
Express를 불러오고, 간단한 데이터 구조를 만든다.
const express = require('express');
const app = express();
app.use(express.json());
const tasks = [
{ id: 1, name: 'Task 1' },
{ id: 2, name: 'Task 2' },
{ id: 3, name: 'Task 3' }
];
GET 요청은 멱등성을 가지므로 같은 요청을 여러 번 해도 결과는 동일해야 한다. 다음은 전체 태스크 목록을 반환하는 GET 요청과, 특정 태스크를 반환하는 GET 요청의 코드 예시다.
app.get('/tasks', (req, res) => {
res.send(tasks);
});
app.get('/tasks/:id', (req, res) => {
const task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) return res.status(404).send('Task not found');
res.send(task);
});
GET 요청은 데이터를 변경하지 않으므로 동일한 요청을 여러 번 수행하더라도 반환되는 결과와 서버의 상태는 변하지 않는다.
HEAD 요청은 GET 요청과 유사하지만, 리소스의 본문을 반환하지 않고 헤더 정보만을 가져온다. 이러한 요청도 GET과 마찬가지로 서버의 상태를 변경하지 않는다. 따라서 멱등성이 유지된다.
app.head('/tasks/:id', (req, res) => {
const task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) return res.status(404).send('Task not found');
res.status(200).send();
});
PUT 요청은 특정 리소스를 새로운 상태로 업데이트하거나, 해당 리소스가 없을 경우 새로 생성한다. 멱등성을 가지므로 동일한 PUT 요청을 여러 번 실행해도 최종 상태는 동일하다.
app.put('/tasks/:id', (req, res) => {
let task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) {
task = { id: parseInt(req.params.id), name: req.body.name };
tasks.push(task);
} else {
task.name = req.body.name;
}
res.send(task);
});
DELETE 요청은 서버의 특정 리소스를 삭제한다. 같은 리소스에 대한 DELETE 요청을 여러 번 수행하더라도 결과는 동일하다.
app.delete('/tasks/:id', (req, res) => {
const task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) return res.status(404).send('Task not found');
const index = tasks.indexOf(task);
tasks.splice(index, 1);
res.send(task);
});
POST 요청은 새로운 리소스를 생성하기 위한 메소드다. 같은 POST 요청을 여러 번 실행하면 서버의 상태가 계속해서 변하므로 멱등성이 없다고 하겠다!
let nextId = 4;
app.post('/tasks', (req, res) => {
const task = {
id: nextId,
name: req.body.name
};
tasks.push(task);
nextId++;
res.status(201).send(task);
});
PATCH 요청은 리소스의 일부만을 업데이트한다. 이 메소드 역시 같은 요청을 여러 번 수행하면 서버의 상태가 변하므로 멱등성이 없다.
app.patch('/tasks/:id', (req, res) => {
const task = tasks.find(t => t.id === parseInt(req.params.id));
if (!task) return res.status(404).send('Task not found');
task.name = req.body.name || task.name;
res.send(task);
});
멱등성(Idempotent)은 API를 설계하거나 사용할 때 매우 중요한 개념이므로 잘 이해하고 있어야 한다.