시스템 설계 - 12. REST vs GraphQL vs gRPC: 작업별 최적 API 선택

발행: (2026년 6월 12일 AM 12:19 GMT+9)
10 분 소요
원문: Dev.to

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 프레임워크로, 백

0 조회
Back to Blog

관련 글

더 보기 »