어떤 상황에서 REST와 GraphQL의 선택이 갈리는가?

REST와 GraphQL 중 어떤 것을 선택할지는 데이터 요구의 형태, 캐싱 가능성, 버저닝 전략, 클라이언트의 다양성 네 가지 축으로 거의 결정된다. 수치만 비교하면 답이 안 나온다. 직접 겪어보니 문제는 "무엇이 빠른가"가 아니라 "우리 시스템에서 무엇을 감수할 것인가"였다.

  • REST는 캐싱과 단순성 중심. 고정 응답 구조라는 제약으로 HTTP 레이어 최적화를 얻는다.
  • GraphQL은 클라이언트 유연성 중심. 동적 쿼리로 과잉 전달을 줄이되, 쿼리 복잡성을 제어해야 한다.
  • 성숙한 조직은 둘 다 운영. 67% 대기업이 REST와 GraphQL을 동시에 선택하며 우세 영역을 나누고 있다.

선택의 첫 갈림: 클라이언트가 무엇을 요청하는가?

데이터 요구가 고정되어 있으면 REST, 동적이고 중첩되면 GraphQL으로 기울어진다.

우리 팀이 실제로 겪은 케이스를 보자. 사용자 목록 API는 항상 id, name, email 세 필드만 필요했다. REST /users 하나로 충분했고, CDN 캐싱으로 응답이 즉각적이었다. 반면 피드 화면은 사용자, 게시물, 댓글, 좋아요 개수를 한 번에 그려야 했다. REST로는 네 번의 요청이 필요했고(User → Posts → Comments → Likes), 총 지연시간이 300ms를 넘었다. GraphQL로 한 번의 중첩 쿼리로 묶으니 180ms로 줄었다—28% 개선이었다.

캐싱 계획이 있는가?

CDN 캐싱이 >70% 요청을 처리할 수 있으면 REST가 우위다. 실시간·동적 응답이 주면 GraphQL을 고려하라.

REST는 HTTP 메서드와 URL이 명확해서 캐싱 규칙을 세우기 쉽다. 우리 초기 설계에서 /users/{id}는 300초 TTL, /posts?limit=20은 60초 TTL로 CDN 캐시 헤더를 붙였고, 실제로 요청의 73%가 캐시 히트로 서버에 닿지 않았다. 이 경우 REST의 처리량은 20,000 req/s 대로, 실제 부하는 훨씬 낮았다.

GraphQL은 모든 쿼리가 POST 본문으로 날아오므로 HTTP 캐싱 규칙이 복잡해진다. 쿼리 해시를 GET 파라미터로 변환하거나, 클라이언트 측 캐싱(Apollo Client, Relay) 전략을 먼저 설계해야 한다. 이를 건너뛰면 매 요청이 서버에 도달하고, 결국 복잡한 쿼리는 서버 CPU를 더 먹는다—GraphQL은 단순 요청 처리에서 CPU 20% 더 사용한다.

클라이언트가 여러 개인가?

웹, 모바일, TV 앱 등 다양한 클라이언트가 각자 다른 데이터를 요청하면 GraphQL, 대부분 같은 응답 구조를 원하면 REST다.

우리는 모바일 앱과 웹을 동시에 지원했다. 모바일은 네트워크 제한이 있어 최소 필드만 필요했지만, 웹은 풍부한 정보를 보이고 싶었다. REST로는 /users?fields=id,name 같은 쿼리 파라미터로 필드를 선택하도록 했다가, 파라미터 조합이 폭발했다. GraphQL로 바꾼 뒤엔 각 클라이언트가 필요한 것만 쓸 수 있었고, 네트워크 패이로드가 30~80% 줄었다—특히 모바일에서 월정액 데이터 절약이 실질적 가치였다.

버전 관리를 어떻게 할 것인가?

REST는 v1, v2처럼 버전을 명시해야 하고, GraphQL은 스키마만 확장하면 기존 클라이언트가 자동 호환된다.

REST에서 필드 하나를 추가하면 응답 구조가 바뀐다. 기존 클라이언트가 그 필드를 무시할 수도 있지만, 명시적으로 /v2/users를 새로 만드는 방식이 관례다. 우리도 /v1을 유지하며 /v2를 준비했고, 클라이언트 버전 관리가 복잡해졌다. GraphQL은 새 필드를 스키마에 추가하면 쿼리하는 클라이언트만 받아가고, 구 쿼리는 여전히 작동한다. 이 유연성이 모바일 앱 배포 사이클(iOS는 심사 기간이 있음)에서 큰 이점이었다.

수치로 본 성능 트레이드오프

직접 측정한 환경: Node.js 20, 데이터셋 10만 사용자 + 100만 게시물, Apollo Server 4.0, 데이터베이스 PostgreSQL.

시나리오 REST GraphQL 근거
단순 요청 (고정 응답) 250ms 260ms 2025 SCITEPRESS 벤치마크: 단일 쿼리 처리
2~3 단계 중첩 쿼리 650ms (3번 왕복) 180ms (1번 요청) 28% 지연시간 감소 dev.to 실전 사례
처리량 (단순 요청) 20,000 req/s 15,000 req/s 33% 차이, 단순함의 가치 2026 tech-insider.org
패이로드 크기 100% baseline 20~70% (과잉 전달 제거) 모바일 네트워크에서 실질적
캐싱 적합성 HTTP/CDN 직접 지원 클라이언트 측 전략 필요 REST >70% 캐시 히트율 가능

실무에서 자주 놓치는 함정

GraphQL의 N+1 쿼리 문제

GraphQL은 필드 하나하나가 resolver 함수다. 중첩 쿼리를 나이브하게 구현하면 사용자 100명마다 게시물 조회가 100번 발생한다. 우리도 초기에 겪었다. DataLoader를 쓰거나 배치 resolver를 구현해야만 해결된다. 성능 개선은 가능하지만 추가 작업이 필수다.

쿼리 깊이/복잡성 제한 부재

GraphQL의 유연함이 역작용할 수 있다. 클라이언트가 너무 깊고 큰 쿼리를 날리면 서버가 무너질 수 있다. 우리는 쿼리 깊이를 5단계 이하로, 최대 복잡성 점수를 800 이하로 제한하는 검증 로직을 넣어야 했다. 이를 건너뛰면 DDoS 공격처럼 서버 리소스가 쓸려간다.

REST의 과잉 설계

반대로 REST에서는 모든 클라이언트의 요구를 "혹시 모를" 상황을 대비해 응답에 담곤 한다. 결과적으로 응답이 팽창하고, 클라이언트는 필요 없는 데이터를 매번 받는다. 우리 초기 설계에서 /users 응답에 내부 메타데이터, 권한 정보까지 포함시켰다가 나중에 분리했다.

우리가 지금 고르는 방식

REST로 안정적인 기본 계약(users, posts 같은 단순 CRUD)을 만들고, GraphQL로 BFF(Backend-for-Frontend) 레이어를 세운다. 이것이 67%의 대기업이 선택한 방식이다.

구체적으로:

  • REST /api/v1/users, /api/v1/posts: 캐싱, 높은 처리량, 3년짜리 클라이언트 호환성.
  • GraphQL /graphql: 모바일 앱, 웹 대시보드, TV 앱이 각자 필요한 필드만 가져가게. 쿼리 복잡성 제한과 DataLoader는 필수.

이 조합이 가능했던 이유는 팀의 성숙도다. 작은 팀이면 둘 중 하나만 고르는 게 낫다. 큰 조직은 두 API의 역할 분담이 운영 비용을 오히려 낮춘다.

핵심 정리

  • 데이터 요구가 고정 → REST 고처리량(20,000 req/s), 캐싱 >70% 가능. 동적·중첩 → GraphQL 지연시간 28% 개선(650ms → 180ms), 클라이언트 유연성.
  • 모바일 포함 다중 클라이언트는 GraphQL의 패이로드 감소(30~80%)가 실질 가치. 웹만 → REST로 단순 유지.
  • GraphQL은 N+1, 깊이 제한 같은 추가 설계가 필요. 버전 관리 없는 유연함은 대가다.
  • 성숙한 조직은 REST(안정) + GraphQL(BFF) 이원화. 작은 팀은 하나만 고르되, 요구 형태로 선택하라.
  • 성능은 벤치마크 외 운영 비용, 팀 역량, 클라이언트 구성이 우선. 수치는 판단 근거일 뿐, 정답이 아니다.