HTTP 메서드와 URI를 사용한 웹 표준 통신 규칙
HTTP 메서드 는 동작과 자원을 포함
URI 는 서버의 위치와 리소스의 경로를 포함
api 통신을 할 때 표준 형식을 따라 클라이언트와 서버가 만들어져있으면 다른 서비스로의 확장성, 서버의 교체가 쉬운 점 등의 장점이 있음
REST 디자인 원칙
Design Principles ( 디자인 원칙 )을 따르는 API를 REST API라고 함
REST Design Principles ( 디자인 원칙 )
독립성 일회성
장점
- 리퀘스트에 모든 정보가 있기 때문에 디버깅 수월 함, 다른 곳에서 찾을 것이 없음
- state를 보관하지 않기 때문에 다른 서버로 대체 하기 쉬움
예시) 음식 배달 주문
Statelessness
손님:메뉴,주소정보를 담아 요청
가게: 요청한메뉴를 해당주소로 응답
Stateful
손님:어제와 같은거로 주세요
가게:어제의 주문 명세에서메뉴,주소를 찾아서 응답
간소화, 일관성 확장성 , 성능개선 Cache-Control, Expires, ETag, Last-Modified 등의 헤더를 사용하여 리소스의 캐시 가능 여부, 캐시 유지 기간, 캐시된 리소스의 유효성 검사 등을 관리할 수 있음.보안 가공 추상화 캡슐화 중간 매개체 가 존재 할 수 있음REST API 작동 방법
HTTP를 사용해서 Resource의 CRUD 기능을 지원
예제 ) express를 사용해서 로컬 서버를 만들어 통신

npm i express
npm i cors
//server.js
const express = require("express");
const cors = require("cors"); // CORS를 위한 패키지
const app = express();
const PORT = 3000;
app.use(cors()); // CORS 미들웨어 사용
app.use(express.json());
// 간단한 데이터 저장소
let todos = [
{ id: 1, title: "Learn REST API", completed: false },
{ id: 2, title: "Build a project", completed: false },
];
// GET 요청 처리: 모든 할 일 목록 반환
app.get("/api/todos", (req, res) => {
res.status(200).json(todos);
});
// POST 요청 처리: 새 할 일 추가
app.post("/api/todos", (req, res) => {
const todo = {
id: todos.length + 1,
title: req.body.title,
completed: false,
};
todos.push(todo);
res.status(201).send(todo);
});
// DELETE 요청 처리: 특정 할 일 삭제
app.delete("/api/todos/:id", (req, res) => {
const { id } = req.params; // 요청에서 ID 추출
const newTodo = todos.filter(todo => todo.id !== parseInt(id));
todos = newTodo;
res.status(204).send(); // 성공적으로 처리되었으나 클라이언트에 반환할 내용이 없음
});
//PUT 요청 처리 : 특정 할 일 수정
app.put("/api/todos/:id", (req, res) => {
const { id } = req.params; // 요청에서 ID 추출
let found = false; // 수정할 할 일이 없는 경우를 위한 변수 선언
// 수정 로직
todos = todos.map(todo => {
if (todo.id === parseInt(id)) {
found = true;
return {
...todo, // 기존 todo 객체의 속성을 복사
...req.body, // req.body에서 받은 속성으로 덮어쓰기
};
}
return todo; // 수정할 할 일이 없는 경우 기존 todo 객체 반환
});
if (found) {
res.status(200).send({ message: "Todo updated succecfully" });
} else {
res.status(404).send({ message: "Todo not found" }); // 수정할 할 일이 없는 경우 404 응답 보냄
}
});
// 서버 시작
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
// index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Todo List</title>
</head>
<body>
<h1>Todo List</h1>
<ul id="todo-list"></ul>
<form id="todo-form">
<input
type="text"
id="todo-input"
placeholder="Add a new todo"
required
/>
<button type="submit">Add Todo</button>
</form>
<script>
document.addEventListener("DOMContentLoaded", fetchTodos);
const form = document.getElementById("todo-form");
form.addEventListener("submit", addTodo);
function fetchTodos() {
// 서버에 있는 투두리스트 정보 읽기 GET
fetch("http://localhost:3000/api/todos")
.then(response => response.json())
.then(data => {
console.log("GET 성공, todos:", data);
const list = document.getElementById("todo-list");
list.innerHTML = ""; // Clear current todos
data.forEach(todo => {
//가져온 데이터를 랜더링함
const li = document.createElement("li");
li.textContent = todo.title;
list.appendChild(li);
//삭제 버튼
const deleteButton = document.createElement("button");
deleteButton.innerHTML = "삭제";
li.appendChild(deleteButton);
deleteButton.onclick = function () {
deleteTodo(todo.id);
};
//수정 버튼
const editButton = document.createElement("button");
editButton.textContent = "수정";
editButton.onclick = function () {
editTodo(todo.id);
};
li.appendChild(editButton);
});
});
}
// 특정 할 일 수정 PUT
function editTodo(todoId) {
const newTitle = prompt("Enter new title:");
if (!newTitle) {
alert("Title cannot be empty!");
return;
}
fetch(`http://localhost:3000/api/todos/${todoId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title: newTitle, completed: false }),
})
.then(response => response.json())
.then(() => {
fetchTodos();
})
.catch(error => conssole.error("Error:", error));
}
// 특정 할 일 삭제 DELETE
function deleteTodo(todoId) {
console.log("todoId", todoId);
fetch(`http://localhost:3000/api/todos/${todoId}`, {
method: "DELETE",
})
.then(() => {
fetchTodos(); // 할 일 목록 새로고침
})
.catch(error => console.error("Error:", error));
}
//할 일 생성 POST
function addTodo(event) {
event.preventDefault();
const input = document.getElementById("todo-input");
const title = input.value;
fetch("http://localhost:3000/api/todos", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title: title }),
})
.then(response => response.json())
.then(data => {
fetchTodos(); // Refresh the list
input.value = ""; // Clear input
})
.catch(error => console.error("Error:", error));
}
</script>
</body>
</html>
코드를 실행해보면 CRUD 기능이 동작함을 볼 수 있음
일부분만 잘라서 살펴보면
클라이언트 측
fetch("http://localhost:3000/api/todos", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title: title }),
})
서버 측
app.post("/api/todos", (req, res) => {
const todo = {
id: todos.length + 1,
title: req.body.title,
completed: false,
};
todos.push(todo);
res.status(201).send(todo);
});
여기까지 간략하게 REST의 개념을 정리하고 적용을 해보며 API 호출하는 코드를 그냥 공식처럼 쓰는 것이 아니라 왜 그런 형식으로 작성하는지 좀 더 이해가 넓어졌다.
REST API에 대해 더 깊이 공부할 것이 많이 있다.