시스템 설계 - 12. REST vs GraphQL vs gRPC: 작업별 최적 API 선택
REST vs GraphQL vs gRPC: 모든 작업에 맞는 API 선택하기
다루는 내용: REST, GraphQL, gRPC, API 게이트웨이, BFF 패턴, Rate Limiting, API 버전 관리
수백만 달러를 들인 Stripe의 API
2013년, Stripe는 큰 문제에 직면했습니다. 그들의 REST API는 너무나 잘 설계돼 전 세계 개발자들에게 사랑받았고, 기업들은 그 위에 전체 결제 흐름을 구축했습니다.
그런데 급격한 변화를 도입해야 했습니다.
API를 단순히 업데이트할 수는 없었습니다—수천 개의 비즈니스가 즉시 깨질 위험이 있었고, 모든 사용자를 한 번에 마이그레이션하도록 강요할 수도 없었습니다. 그래서 Stripe는 여러 API 버전을 동시에 유지하면서 각 클라이언트를 자신이 만든 버전으로 라우팅하는 뛰어난 방식을 선택했습니다.
오늘날 Stripe는 2011년부터 시작된 모든 API 버전을 지원합니다. 모든 버전이 살아있으며, 기존 통합도 그대로 작동합니다.
그 대가는 전용 버전 관리 인프라, 모든 버전에 대한 호환성 테스트, 그리고 API 안정성에만 전념하는 엔지니어링 팀이었습니다.
교훈: API 설계 선택은 수년간 누적됩니다. REST, GraphQL, gRPC 중 어떤 것을 선택하고, 어떻게 버전 관리·게이트웨이·Rate‑limit을 적용하느냐에 따라 향후 10년간 아키텍처의 유연성이 결정됩니다.
이 결정을 현명하게 내립시다.
REST: 웹의 보편 언어
**REST (Representational State Transfer)**는 HTTP 메서드와 리소스 URL을 중심으로 한 아키텍처 스타일이며, 2000년 Roy Fielding의 논문에서 정의된 이후 API 분야의 표준이 되었습니다.
REST의 6가지 원칙
1. Stateless — 모든 요청에 필요한 정보를 모두 포함합니다. 서버에 세션이 없습니다.
2. Client-Server — 프론트엔드와 백엔드가 독립적입니다. 어느 쪽이든 변경 가능.
3. Cacheable — 응답이 캐시 가능한지 선언합니다.
4. Uniform Interface — 표준 HTTP 메서드 + 리소스 URL.
5. Layered System — 클라이언트는 자신이 서버, 캐시, 로드밸런서 중 무엇과 통신하는지 모릅니다.
6. Code on Demand — (선택) 서버가 실행 가능한 코드를 클라이언트에 전달할 수 있습니다.
REST 실전
리소스는 명사, HTTP 메서드는 동사입니다.
GET /users/123 → 사용자 123 조회
POST /users → 새 사용자 생성
PUT /users/123 → 사용자 123을 전체 교체
PATCH /users/123 → 사용자 123을 부분 업데이트
DELETE /users/123 → 사용자 123 삭제
GET /users/123/orders → 사용자 123의 주문 목록 조회
POST /users/123/orders → 사용자 123의 주문 생성
HTTP 상태 코드는 의미를 전달합니다:
200 OK → 성공, 본문에 데이터 포함
201 Created → 리소스 생성 성공 (POST)
204 No Content → 성공, 본문 없음 (DELETE)
400 Bad Request → 클라이언트가 잘못된 데이터를 전송
401 Unauthorized → 인증되지 않음
403 Forbidden → 인증은 되었지만 권한 부족
404 Not Found → 리소스 존재하지 않음
409 Conflict → 상태 충돌 (예: 중복 이메일)
422 Unprocessable → 형식은 맞지만 비즈니스 규칙 위반
429 Too Many Req → Rate‑limit 적용
500 Server Error → 서버 내부 오류
REST는 설계상 캐시가 가능합니다:
GET /products/456
Cache-Control: max-age=3600 → 1시간 동안 캐시
ETag: "abc123" → 조건부 요청을 위한 지문
GET 요청은 멱등하고 안전하기 때문에 CDN과 브라우저가 적극적으로 캐시할 수 있습니다.
REST의 약점: Over‑Fetching과 Under‑Fetching
Over‑Fetching – 클라이언트가 필요로 하는 것보다 더 많은 데이터를 반환합니다.
GET /users/123
Response:
{
"id": 123,
"name": "Priya Sharma",
"email": "priya@example.com",
"phone": "...",
"address": "...",
"billing_info": { ... }, // 클라이언트는 name과 email만 필요했음
"subscription": { ... }, // 모든 정보를 받아버림
"preferences": { ... }
}
모바일 클라이언트는 “Hello, Priya”만 보여주면 되는데 50개의 필드를 받아와서 모바일 대역폭과 배터리를 낭비합니다.
Under‑Fetching – 필요한 데이터를 얻기 위해 여러 번 요청해야 합니다.
// 사용자의 피드를 보여주려면: 사용자 정보 + 게시물 + 각 게시물의 댓글이 필요
GET /users/123 → 1 요청
GET /posts?user_id=123 → 1 요청
GET /comments?post_id=1 → 게시물당 1 요청 (N+1 문제!)
GET /comments?post_id=2
GET /comments?post_id=3
...
화면 하나를 그리기 위해 수십 번의 요청이 발생할 수 있어 모바일 환경에서 큰 병목이 됩니다. 이러한 문제를 해결하고자 GraphQL이 탄생했습니다.
GraphQL: 클라이언트가 주도하는 데이터 요청
GraphQL은 2012년 Facebook이(2015년 오픈소스) 모바일 앱 성능을 저해하던 over/under‑fetching 문제를 해결하기 위해 만든 쿼리 언어이자 런타임입니다.
핵심 아이디어: 클라이언트가 정확히 필요한 데이터를 선언하고, 서버는 그에 맞는 결과만 반환한다.
GraphQL 작동 방식
단일 엔드포인트(/graphql)가 클라이언트가 원하는 데이터를 기술한 쿼리를 받습니다.
query GetUserFeed {
user(id: "123") {
name # ← 필요한 필드만
posts(first: 10) { # ← 10개의 게시물만
title
createdAt
comments(first: 3) { # ← 게시물당 3개의 댓글만
text
author {
name # ← 전체 프로필이 아니라 이름만
}
}
}
}
}
한 번의 요청으로 클라이언트가 정확히 원하는 데이터만 받아옵니다.
서버 응답 예시:
{
"data": {
"user": {
"name": "Priya Sharma",
"posts": [
{
"title": "My first post",
"createdAt": "2024-01-15",
"comments": [
{ "text": "Great!", "author": { "name": "Arjun" } }
]
}
]
}
}
}
GraphQL Mutations와 Subscriptions
# Mutation — 데이터 수정
mutation CreatePost {
createPost(input: { title: "Hello World", content: "..." }) {
id
title
createdAt
}
}
# Subscription — WebSocket을 통한 실시간 업데이트
subscription OnNewComment {
commentAdded(postId: "456") {
text
author { name }
}
}
GraphQL의 실제 트레이드‑오프
장점
- Over/Under‑Fetching을 완전히 해소 → 모바일 앱에 최적
- 단일 엔드포인트, 스키마 자체가 문서 역할
- 강력한 타입 시스템으로 컴파일 시 오류 검출
- 풍부한 개발자 도구 (GraphiQL, 코드 생성기 등)
- 백엔드 API 변경 없이 프론트엔드 빠른 반복 가능
단점
- 캐시가 어렵다: 기본적으로
POST /graphql은 URL 기반 캐시가 불가능합니다. persisted query 혹은 전용 GraphQL CDN 캐시가 필요합니다. - N+1 쿼리 문제: 비효율적인 구현은 하나의 요청에 수백 개의 DB 쿼리를 발생시킬 수 있습니다. DataLoader 같은 배칭 기법이 필수.
- 복잡한 쿼리는 비용이 크다: 악의적이거나 부실한 클라이언트가 깊게 중첩된 데이터를 요청하면 DB에 큰 부하가 걸립니다. 쿼리 깊이 제한 및 비용 분석이 필요합니다.
- 단순 API에 과도함: 엔드포인트가 몇 개 안 되는 경우 GraphQL 도입 비용이 이득보다 큽니다.
주요 사용자: Facebook(원조), GitHub API v4, Shopify, Twitter, Airbnb, Pinterest 등 복잡한 제품 API를 보유한 기업들.
gRPC: 내부 서비스 간 통신 프로토콜
gRPC(Google Remote Procedure Call)는 HTTP/2와 Protocol Buffers 위에 구축된 고성능 RPC 프레임워크로, 백