포스트

REST, gRPC, GraphQL - 선택 기준과 각자가 내는 비용

세 가지를 “REST는 무난하고, gRPC는 빠르고, GraphQL은 유연하다”로 요약하면 선택에 쓸 수 없다. 셋은 같은 문제의 다른 답이 아니라 다른 문제를 풀고, 각자 비용을 다른 곳에 놓는다. 그 비용이 어디에 놓이는지가 선택 기준이다.

세 가지가 실제로 다른 점

 RESTgRPCGraphQL
계약느슨함(OpenAPI는 선택).proto가 강제스키마가 강제
전송HTTP/1.1 또는 2, JSONHTTP/2, protobuf주로 HTTP/1.1, JSON
호출 모양리소스 중심메서드 호출질의
응답 형태서버가 정함서버가 정함클라이언트가 정함
스트리밍SSE·WebSocket 별도양방향 내장subscription
브라우저그대로프록시 필요(gRPC-Web)그대로
캐싱HTTP 캐시 그대로직접 구현어려움(POST 단일 엔드포인트)

핵심 차이를 한 줄로 줄이면 이렇다. REST는 HTTP의 의미를 쓰고, gRPC는 타입과 성능을 얻는 대신 HTTP의 의미를 버리고, GraphQL은 응답 모양의 결정권을 클라이언트에 넘긴다.

REST가 공짜로 얻는 것

HTTP를 그대로 쓰기 때문에 주변 생태계가 전부 동작한다.

  • 브라우저, curl, 프록시, CDN, API 게이트웨이가 별도 작업 없이 붙는다.
  • 상태 코드와 메서드 의미가 표준이다. 멱등성과 재시도 규약이 여기서 나온다(멱등한 API 설계).
  • Cache-Control, ETag로 캐싱이 무료다.
  • 관측 도구가 URL과 상태 코드 기준으로 이미 만들어져 있다.

대가는 계약이 느슨하다는 것이다. OpenAPI를 쓰지 않으면 필드 하나가 바뀐 것을 컴파일러가 잡아 주지 않는다. 그리고 리소스 모델에 안 맞는 동작(예: “정산 마감을 실행한다”)을 억지로 명사로 만드는 문제가 늘 남는다.

gRPC가 가져오는 것과 잃는 것

.proto 하나에서 서버와 클라이언트 코드가 생성된다. 필드를 지우면 빌드가 깨진다. 서비스가 많고 팀이 여럿일 때 이 강제력이 가장 큰 이점이다.

HTTP/2 위에서 동작하므로 연결 하나에 스트림이 여럿 흐르고, 헤더가 압축되며, 양방향 스트리밍이 기본이다. 직렬화가 바이너리라 payload가 작고 파싱이 빠르다.

잃는 것이 분명하다.

  • 브라우저에서 직접 못 쓴다. gRPC-Web과 프록시가 필요하다.
  • HTTP 캐싱을 못 쓴다. 캐시가 필요하면 직접 만든다.
  • 디버깅이 불편하다. 바이너리라 curl로 못 보고 grpcurl 같은 도구가 필요하다.
  • HTTP/2 커넥션 하나에 스트림이 몰려 L4 로드 밸런서가 부하를 못 나눈다. 연결 단위로 분산하는 LB 뒤에서는 한 서버로 쏠린다. L7 프록시나 클라이언트 측 분산이 필요하다.

GraphQL이 옮기는 비용

클라이언트가 필요한 필드만 질의하므로 over-fetching이 사라지고, 화면 하나를 그리기 위한 왕복(under-fetching)도 준다. 화면 요구가 자주 바뀌는 프런트엔드에 유리하다.

비용은 서버로 옮겨간다.

  • N+1 질의. 중첩 필드마다 리졸버가 돌면서 DB를 친다. DataLoader 류의 배치·캐시 계층이 사실상 필수다.
  • 질의 비용의 상한이 없다. 깊게 중첩된 질의 하나가 서버를 마비시킬 수 있다. 깊이 제한, 복잡도 점수, 영속 질의(persisted query)가 방어 장치다.
  • 캐싱이 어렵다. 단일 엔드포인트에 POST라 HTTP 캐시가 안 붙는다.
  • 관측이 어렵다. 모든 요청이 POST /graphql이라 URL 기준 지표가 무의미하다. 오퍼레이션 이름을 지표 라벨로 써야 하고, 그러면 카디널리티를 신경 써야 한다.

고르는 기준

질문 순서로 정리하면 이렇다.

  1. 클라이언트가 브라우저인가? 그렇다면 gRPC는 프록시 비용을 먼저 계산한다.
  2. 호출자가 내부 서비스인가 외부 개발자인가? 외부 공개 API는 REST가 진입 장벽이 가장 낮다.
  3. 계약 위반을 빌드 타임에 잡아야 하는가? 서비스 수가 많고 변경이 잦으면 gRPC나 스키마 강제가 값을 한다.
  4. 화면마다 필요한 데이터가 크게 다른가? 그렇다면 GraphQL의 이점이 실제로 있다. 화면이 몇 개 안 되면 REST 엔드포인트 몇 개가 더 싸다.
  5. 스트리밍이 필요한가? 양방향이면 gRPC, 서버→클라이언트 단방향이면 SSE로 충분한 경우가 많다.

셋은 배타적이지 않다. 외부는 REST, 내부 서비스 간은 gRPC, BFF 계층만 GraphQL 같은 조합이 흔하다.

이 설명이 깨지는 곳

  • “gRPC가 빠르다”는 대개 직렬화와 HTTP/2 이야기다. 전체 지연에서 그 비중이 작다면(DB가 대부분) 체감 차이가 없다.
  • protobuf의 호환 규칙을 지켜야 계약의 이점이 산다. 필드 번호 재사용, 타입 변경은 조용히 깨진다(API 버저닝).
  • REST도 스키마를 강제할 수 있다. OpenAPI에서 코드를 생성하면 gRPC와 비슷한 강제력을 얻는다. 차이가 절대적이지 않다.
  • GraphQL의 유연성은 권한 검사를 어렵게 만든다. 필드 단위 인가를 리졸버마다 걸어야 하고, 한 곳만 빠져도 노출이 된다.

무엇을 재면 확인되는가

  1. 같은 응답을 JSON과 protobuf로 만들어 크기와 직렬화 시간을 비교한다. 전체 지연에서 차지하는 비중까지 봐야 의미가 있다.
  2. gRPC를 L4 LB 뒤에 두고 서버별 요청 분포를 본다. 쏠림이 실제로 보인다.
  3. GraphQL 질의의 깊이를 늘려 가며 DB 질의 수를 센다. DataLoader 전후로.

실무와의 접점

JSP 기반 시스템의 구조 전환에서 조회 경로를 옮길 때 가장 어려웠던 것은 프로토콜이 아니라 기존 응답 계약을 깨지 않는 것이었다. 세 방식 중 무엇을 고르든 그 문제는 남는다. 프로토콜 선택은 계약을 어떻게 강제하고 어떻게 진화시킬지의 문제이지, 성능 비교표의 문제가 아니라는 것이 실무에서 배운 순서다.

정리

  • REST는 HTTP의 의미와 생태계를 공짜로 얻고 계약의 느슨함을 대가로 낸다.
  • gRPC는 타입 강제와 스트리밍을 얻고 브라우저 접근성, HTTP 캐싱, L4 분산을 잃는다.
  • GraphQL은 응답 모양의 결정권을 클라이언트에 주고, N+1과 질의 비용 상한과 관측 문제를 서버가 받는다.
  • 고르는 순서는 클라이언트 종류 → 호출자 → 계약 강제 필요성 → 화면 다양성 → 스트리밍이다.
  • 셋은 배타적이지 않고, 계층마다 다르게 고르는 것이 일반적이다.

참고

아키텍처와 마이그레이션
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

댓글

아직 댓글이 없습니다