🔄 REST API vs GraphQL
📖 정의
REST API는 HTTP 메서드를 사용하여 리소스 중심으로 설계된 전통적인 API 아키텍처입니다. GraphQL은 Facebook이 개발한 쿼리 언어로, 클라이언트가 필요한 데이터를 정확하게 요청할 수 있는 유연한 API 방식입니다. REST는 여러 엔드포인트를 가지며, GraphQL은 단일 엔드포인트에서 모든 데이터를 처리합니다.
🎯 비유로 이해하기
레스토랑 vs 뷔페
REST API = 레스토랑 메뉴
├─ 정해진 메뉴만 주문 가능
├─ 각 메뉴마다 정해진 구성
├─ "피자 주세요" → 피자 전체가 옴
├─ 토핑만 빼거나 추가하기 어려움
└─ 간단하고 예측 가능
GraphQL = 뷔페
├─ 원하는 음식만 골라서 가져옴
├─ 필요한 만큼만 선택
├─ "치즈만 주세요" → 치즈만 받음
├─ 자유롭게 조합 가능
└─ 유연하지만 복잡할 수 있음
도서관 책 대출
REST API = 전통적인 대출
사서: "어떤 책이 필요하신가요?"
나: "컴퓨터 책 주세요"
사서: "이 책들 전부 드릴게요" (책 10권)
나: "저는 1장만 필요한데..." (나머지는 불필요)
GraphQL = 스마트 대출
나: "컴퓨터 책의 3장만 필요해요"
사서: "3장만 복사해드릴게요" (정확히 필요한 것만)
나: "완벽해요!"
⚙️ 작동 원리
1. 데이터 요청 방식 비교
REST API: 여러 엔드포인트
GET /users/1 → 사용자 정보
GET /users/1/posts → 사용자의 게시글
GET /posts/1/comments → 게시글의 댓글
총 3번의 요청 필요!
GraphQL: 단일 엔드포인트
POST /graphql
{
user(id: 1) {
name
posts {
title
comments {
text
}
}
}
}
1번의 요청으로 모든 데이터!
2. Over-fetching vs Under-fetching
REST API 문제
Over-fetching (불필요한 데이터 받음)
GET /users/1
{
"id": 1,
"name": "김철수",
"email": "kim@example.com",
"phone": "010-1234-5678",
"address": "서울시...",
"createdAt": "2024-01-01",
// 이름만 필요한데 모든 정보를 받음!
}
Under-fetching (부족한 데이터)
GET /users/1 → 사용자 정보
GET /users/1/posts → 추가 요청 필요
GET /users/1/friends → 또 추가 요청
// 여러 번 요청해야 함!
GraphQL 해결
정확히 필요한 것만
{
user(id: 1) {
name // 이름만 요청!
}
}
→ { "name": "김철수" }
한 번에 모든 것
{
user(id: 1) {
name
posts { title }
friends { name }
}
}
→ 모든 데이터를 1번에!
3. API 설계 철학
REST: 리소스 중심
┌─────── ──────────┐
│ /users │ → 사용자 목록
│ /users/1 │ → 특정 사용자
│ /posts │ → 게시글 목록
│ /posts/1 │ → 특정 게시글
└─────────────────┘
각 리소스마다 엔드포인트
GraphQL: 쿼리 중심
┌─────────────────┐
│ /graphql │ → 모든 요청
└─────────────────┘
↓
┌────────┐
│ Query │ → 데이터 읽기
│Mutation│ → 데이터 변경
│Subscribe│ → 실시간 구독
└────────┘
💡 실제 예시
REST API 예시 (Express.js)
// Express.js REST API 구현
const express = require('express');
const app = express();
app.use(express.json());
// 데이터 (실제로는 데이터베이스 사용)
const users = [
{
id: 1,
name: '김철수',
email: 'kim@example.com',
age: 25
},
{
id: 2,
name: '이영희',
email: 'lee@example.com',
age: 30
}
];
const posts = [
{
id: 1,
userId: 1,
title: 'REST API 소개',
content: 'REST는...'
},
{
id: 2,
userId: 1,
title: 'GraphQL 소개',
content: 'GraphQL은...'
}
];
// ========== GET: 모든 사용자 조회 ==========
app.get('/api/users', (req, res) => {
res.json(users);
});
// ========== GET: 특정 사용자 조회 ==========
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
}
res.json(user);
});
// ========== POST: 사용자 생성 ==========
app.post('/api/users', (req, res) => {
const newUser = {
id: users.length + 1,
name: req.body.name,
email: req.body.email,
age: req.body.age
};
users.push(newUser);
res.status(201).json(newUser);
});
// ========== PUT: 사용자 수정 ==========
app.put('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
}
user.name = req.body.name || user.name;
user.email = req.body.email || user.email;
user.age = req.body.age || user.age;
res.json(user);
});
// ========== DELETE: 사용자 삭제 ==========
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) {
return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
}
users.splice(index, 1);
res.status(204).send();
});
// ========== GET: 사용자의 게시글 조회 ==========
app.get('/api/users/:id/posts', (req, res) => {
const userId = parseInt(req.params.id);
const userPosts = posts.filter(p => p.userId === userId);
res.json(userPosts);
});
// ========== GET: 특정 게시글 조회 ==========
app.get('/api/posts/:id', (req, res) => {
const post = posts.find(p => p.id === parseInt(req.params.id));
if (!post) {
return res.status(404).json({ error: '게시글을 찾을 수 없습니다' });
}
res.json(post);
});
app.listen(3000, () => {
console.log('REST API 서버 실행: http://localhost:3000');
});