🔷 GraphQL8. 이론 & 대안03 — GraphQL vs gRPC

03 — GraphQL vs gRPC

한 줄 답: gRPC와 GraphQL은 직접 비교 불가능하다. gRPC는 서버-서버 RPC(Protobuf + HTTP/2 + 강타입 + streaming)에 최적화된 transport + IDL 묶음이고, GraphQL은 클라이언트가 응답 모양을 선언하는 query language다. 한 회사가 서버 간엔 gRPC, 클라이언트엔 GraphQL을 동시에 쓰는 것이 가장 흔한 그림.


Why — 왜 이 비교가 어색한가

흔한 잘못된 프레임진실
”gRPC vs GraphQL 중 뭐가 빠른가”다른 layer에 사는 도구라 벤치마크가 의미 없음
”gRPC도 강타입, GraphQL도 강타입이니 비슷”강타입의 목적이 다름 — gRPC는 함수 시그니처, GraphQL은 결과 모양
”둘 중 하나만 골라야 한다”보통 둘 다 쓴다 — 서버-서버는 gRPC, 클라이언트엔 GraphQL

GraphQL 도입을 검토할 때 자주 듣는 반대 — “우리는 gRPC를 잘 쓰고 있다”는 대안이 아니라 보완이다. 둘은 layer가 다르다.

같은 회사에서 두 도구가 다른 자리에 산다. 이 그림이 머리에 박히면 비교의 어색함이 풀린다.


How — 두 도구가 어떻게 다른가

1) 비교 매트릭스

차원gRPCGraphQL
본질RPC framework (transport + IDL + codegen)Query language
IDL.proto (Protobuf 3)SDL (GraphQL schema)
wire formatBinary (Protobuf)JSON (text)
transportHTTP/2 onlyHTTP/1.1·2·3, WS, SSE
응답 모양서버 결정 (proto가 고정)요청 결정 (selection set)
streamingbidirectional 내장subscription (transport 의존)
언어 지원10+ 공식 (C++/Java/Go/Py/Ruby/JS…)사실상 전부
브라우저 직접 호출불가 (grpc-web 필요)가능 (그냥 HTTP)
HTTP 캐시불가어려움
introspectionreflection 옵션 (gRPC Reflection)spec 의무
versioningproto field 번호로 진화schema deprecate로 진화
payload 크기작음 (binary)크지만 over-fetching 없음
latency매우 낮음 (HTTP/2 multiplex + binary)중간 (JSON parse)
잘 맞는 자리서버-서버, low-latency, streaming, 강타입클라이언트, 가변 모양, BFF

2) gRPC의 특징 — 서버-서버 RPC

.proto 정의:

service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc StreamPosts(StreamPostsRequest) returns (stream Post);
  rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}
 
message User {
  int64 id = 1;
  string name = 2;
  string email = 3;
}

자동 생성:

  • 서버: interface 구현만 하면 됨
  • 클라이언트: 함수처럼 호출 (stub.GetUser(req))
  • 둘 다 코드로 type 안전

전송: HTTP/2 위에서 binary Protobuf. 한 connection으로 multiplex. 가장 빠른 RPC.

streaming:

  • server streaming (서버 → 클라이언트 N개)
  • client streaming (클라이언트 → 서버 N개)
  • bidirectional (양쪽 N개)

→ GraphQL subscription은 한 방향만. gRPC는 진짜 양방향 streaming.

3) GraphQL과의 핵심 차이

  • gRPC: client는 함수를 호출한다. proto가 고정된 message를 정의했으니 그 모양 전부가 온다. over-fetching이 생길 수 있지만, binary라 작고 빨라서 보통 신경 안 쓴다.
  • GraphQL: client는 모양을 묘사한다. 원하는 필드만 온다. 클라이언트가 100가지 변종을 원해도 추가 endpoint·proto 변경 없이 흡수.

→ “Binary 효율”과 “모양 유연성”의 trade-off다.

4) Browser 호출의 결정적 차이

gRPC는 브라우저에서 직접 호출 불가다 — HTTP/2 trailer header 같은 저수준 기능이 fetch API에 없다. 이 한계 때문에 gRPC-Web(envoy proxy를 통해 변환)이나 Connect(현대적 대안)가 등장했지만, 어쨌든 직접 호출은 안 된다.

GraphQL은 그냥 HTTP POST다. 어떤 브라우저, 어떤 fetch library든 그대로 호출.

클라이언트가 브라우저라면 GraphQL이 훨씬 자연스럽다. 이게 BFF 패턴(06)에서 GraphQL이 이기는 큰 이유.


What — 실제 사용 시나리오

시나리오 A — Netflix 내부 구조 (단순화)

  • 클라이언트 → Gateway: GraphQL (가변 모양, 한 화면 한 쿼리)
  • Gateway → 서비스: gRPC (저지연, 강타입, streaming)
  • 서비스 간: gRPC (마이크로서비스 표준)

이 패턴이 현대 큰 회사의 default다.

시나리오 B — Linkerd·Envoy·etcd (인프라 컴포넌트)

이들은 전부 gRPC만 쓴다. 이유 — (a) 서버-서버, (b) 저지연이 critical, (c) streaming이 본질 (watch, observe), (d) 클라이언트도 다 서비스라 codegen 안전. GraphQL은 후보에도 못 올라간다.

시나리오 C — 모바일 앱의 News Feed

GraphQL이 압도적. gRPC를 모바일에 직접 쓰는 시도(Square 등)도 있었지만, learning curve · 브라우저 호환 · BFF 통합 때문에 GraphQL이 우세.


What-if — 잘못 선택하면

1) 서버-서버에 GraphQL 사용

→ Binary 효율도 못 가지고, streaming도 약하고, 클라이언트 codegen 강점도 서버 간엔 의미 없음. 대응: 서버 간은 gRPC를 default로.

2) 브라우저 클라이언트에 gRPC 직접

→ gRPC-Web/Connect 도입 비용 + 캐시 어려움 + 디버깅 곤란. 대응: 브라우저는 GraphQL or REST.

3) “gRPC가 빠르다고 들었다”며 모바일까지 gRPC

→ proto 한 번 바뀌면 앱 강제 업데이트 필요. GraphQL은 스키마 진화가 쉬움. 대응: 모바일은 GraphQL의 backwards compatibility가 더 유리.

4) gRPC streaming을 GraphQL subscription으로 대체하려 함

→ bidirectional streaming은 GraphQL이 못 한다. subscription은 server → client 단방향. 대응: 진짜 양방향 streaming이 필요하면 gRPC.

5) 한 회사 안에서 둘을 대립시킴

→ 팀이 *“gRPC파”·“GraphQL파”*로 나뉘어 정치 싸움. 실제로 자주 본다. 대응: 위 매트릭스를 팀 합의 문서로 박는다. 자리가 다르다.


Insight — 흥미로운 이야기

”Google이 gRPC를 만든 이유”

gRPC는 Google 내부의 Stubby(2001~)를 2015년 외부에 공개한 것이다. Google은 수만 개 마이크로서비스수억 RPC/초를 주고받는 회사다. 이 규모에서는 *binary 효율 1%*도 의미가 있다. 즉 gRPC는 “인터넷 규모 서버-서버” 라는 매우 특수한 자리를 위해 설계됐다.

GraphQL은 Facebook의 모바일 앱 화면 한 장을 위해 설계됐다. 같은 회사(Meta)가 내부 서비스 통신에는 Thrift(또 다른 RPC)를 쓴다. 만든 회사조차 GraphQL을 서버-서버에 안 쓴다.

”gRPC-Web의 한계”

gRPC를 브라우저에서 쓰려는 시도(gRPC-Web)는 7년 넘게 시도되고 있지만, bidirectional streaming은 여전히 불가다. HTTP/2 trailer를 브라우저 fetch API가 받지 못한다는 근본적 한계 때문. Connect(2022, Buf)가 이걸 우회하려 하지만 완전한 호환은 아니다.

→ 이 한계 자체가 *“클라이언트엔 GraphQL/REST가 더 자연스럽다”*는 결론의 한 근거.

”Buf의 Connect — 둘의 중간”

2022년 Buf가 발표한 Connect는 gRPC의 IDL은 유지하되 HTTP/1.1과 호환되는 wire format을 제공한다. “브라우저에서 잘 동작하는 gRPC”를 표방. 그래도 클라이언트 주도 모양 선택은 못 한다 — Connect는 여전히 RPC 패러다임이다.


요약 + 다이어그램

gRPC와 GraphQL은 직접 비교 불가능하다. gRPC는 서버-서버 binary RPC, GraphQL은 클라이언트 주도 query language. 현대 시스템의 default 그림은 “외부엔 GraphQL/REST, 내부엔 gRPC” — 한 회사가 둘 다 쓰는 게 정상이다.

다음 문서: 04-graphql-vs-trpc.mdx — 그럼 TypeScript 모노레포의 tRPC는?