REST vs GraphQL vs tRPC: API 스타일 선택 기준

5 분 소요

새 서비스의 API를 설계할 때 “REST로 갈까, GraphQL로 갈까, 요즘은 tRPC도 있다던데"라는 논쟁은 한 번쯤 반드시 벌어집니다. 결론을 먼저 적으면, 셋은 우열 관계가 아니라 서로 다른 제약 조건에 최적화된 도구이고, 결정 축은 기술이 아니라 “누가 이 API를 소비하는가"입니다. 이 글은 각 스타일이 무엇을 풀고 무엇을 새로 떠안는지, 그리고 선택 기준을 정리합니다.

REST: 리소스와 HTTP 의미론 위의 기본값 #

REST는 데이터를 리소스(URL)로 표현하고 HTTP 메서드(GET, POST, PUT, DELETE)로 조작하는 스타일입니다. 지난 20년의 웹 API 기본값이었고, 그 지위에는 이유가 있습니다.

  • HTTP 인프라를 공짜로 얻습니다. GET의 캐싱(CDN, 브라우저), 상태 코드, 인증 헤더, 리다이렉트까지, 웹의 모든 중간 장치가 REST를 이해합니다.
  • 학습·도구 비용이 최저입니다. curl 하나로 디버깅되고, 어떤 언어의 어떤 클라이언트도 호출할 수 있으며, OpenAPI 명세로 문서·클라이언트 생성 생태계가 성숙해 있습니다.

약점은 엔드포인트의 모양이 서버가 정한 대로 고정된다는 것입니다. 화면 하나를 그리는 데 필요한 데이터가 서버가 만든 응답과 어긋나면 두 가지 문제가 생깁니다. 필요 없는 필드까지 받아 오는 오버페칭, 그리고 한 화면을 위해 여러 엔드포인트를 연달아 불러야 하는 언더페칭입니다. 클라이언트 종류가 늘고 화면이 복잡해질수록 이 어긋남이 커지고, “화면 전용 엔드포인트"가 늘어나며 API가 지저분해집니다.

GraphQL: 클라이언트가 필요한 모양을 선언한다 #

GraphQL은 이 어긋남을 정면으로 풉니다. 서버는 타입으로 정의된 스키마(데이터 그래프)를 하나 노출하고, 클라이언트가 필요한 필드만 쿼리로 선언해서 한 번의 요청으로 받아 갑니다. 오버페칭과 언더페칭이 정의상 사라지고, 스키마가 곧 계약이자 문서가 됩니다. 모바일, 웹, 파트너 등 서로 다른 데이터 요구를 가진 클라이언트가 여럿일 때 힘이 극대화됩니다.

대신 새로 떠안는 비용이 명확합니다.

  • 서버 복잡도: 리졸버 구조에서 N+1 문제가 기본으로 발생해 DataLoader 같은 배칭 장치가 필수이고, 클라이언트가 임의로 깊은 쿼리를 만들 수 있어 쿼리 복잡도 제한·깊이 제한 같은 방어가 필요합니다.
  • HTTP 캐싱 상실: 모든 요청이 사실상 POST 하나로 들어오므로 CDN·브라우저 캐시를 그대로 쓸 수 없고, 캐싱이 클라이언트 라이브러리(Apollo 등)의 정규화 캐시 층으로 옮겨 갑니다.
  • 운영 학습 비용: 스키마 설계, 버저닝 관행, 모니터링(어떤 쿼리가 느린가)까지 REST와는 다른 운영 기술이 필요합니다.

요약하면 GraphQL은 유연성을 클라이언트에게 주고 복잡성을 서버가 떠안는 거래입니다. 그 거래가 이득이 되려면 클라이언트 쪽 다양성이 실제로 있어야 합니다.

tRPC: 타입을 끝에서 끝까지, 단 TypeScript 안에서만 #

tRPC는 질문 자체를 바꿉니다. “서버와 클라이언트가 같은 저장소의 TypeScript라면, 스키마도 코드 생성도 필요 없지 않은가?” 서버에 프로시저(함수)를 정의하면, 클라이언트는 그 타입을 컴파일 타임에 그대로 공유해서 자동 완성과 타입 체크가 되는 원격 호출을 얻습니다.

  • 스키마 파일도, 코드 생성 단계도 없습니다. 서버 함수의 시그니처를 바꾸면 클라이언트 코드가 그 자리에서 타입 에러로 깨집니다. API 불일치가 런타임이 아니라 컴파일 타임에 잡히는 개발 경험이 tRPC의 전부이자 최대 강점입니다.
  • 적용 조건이 명확합니다. 풀스택 TypeScript + 모노레포(또는 타입을 공유할 수 있는 구조) + 내부 소비자. Next.js 풀스택 앱의 프런트-백 통신이 전형적 적합 사례입니다.

한계도 조건의 뒷면 그대로입니다. TypeScript 밖의 소비자(모바일 네이티브, 다른 언어의 서버, 외부 파트너)가 등장하는 순간 타입 공유라는 전제가 무너집니다. 즉 tRPC는 공개 API의 도구가 아니라 한 팀이 소유한 앱의 내부 통신 도구입니다.

선택 기준: 세 개의 질문 #

질문답권장
외부에 공개되는 API인가?예REST (+ OpenAPI). 소비자를 통제할 수 없으면 보편성이 이깁니다
데이터 요구가 다른 클라이언트가 여럿인가?예GraphQL. 서버 복잡성 비용을 지불할 명분이 생깁니다
풀스택 TS 단일 팀의 내부 통신인가?예tRPC. 스키마·코드 생성 없는 타입 안전이 최적입니다

몇 가지 보충 기준이 있습니다.

  • 애매하면 REST가 기본값입니다. 잘못 골라도 비용이 가장 낮고, 나중에 특정 화면 조합용으로 GraphQL 층(BFF)을 얹는 진화 경로도 열려 있습니다.
  • 혼합은 정상입니다. 외부 공개는 REST, 자사 클라이언트용은 GraphQL 또는 tRPC라는 구성은 절충이 아니라 각 소비자에 맞는 도구를 쓴 결과입니다.
  • 성능은 결정 축이 아닙니다. 셋 다 잘 만들면 빠르고 못 만들면 느립니다. API가 느릴 때의 진단은 스타일과 무관하게 병목을 찾는 순서의 문제입니다.

정리 #

  • REST는 HTTP 인프라와 보편성을 공짜로 얻는 기본값이고, 약점은 서버가 고정한 응답 모양에서 오는 오버·언더페칭입니다.
  • GraphQL은 클라이언트가 필요한 데이터를 선언하게 해서 그 어긋남을 없애는 대신, N+1·쿼리 방어·캐싱 재설계라는 서버 복잡성을 떠안습니다. 다양한 클라이언트가 있을 때 이득이 되는 거래입니다.
  • tRPC는 풀스택 TypeScript 내부 통신에서 스키마 없이 컴파일 타임 타입 안전을 주지만, TS 밖 소비자가 생기는 순간 전제가 무너집니다.
  • 결정 축은 소비자입니다. 공개 API는 REST, 다양한 클라이언트는 GraphQL, TS 단일 팀 내부는 tRPC입니다. 애매하면 REST에서 시작해 필요할 때 얹습니다.
X