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) 비교 매트릭스
| 차원 | gRPC | GraphQL |
|---|---|---|
| 본질 | RPC framework (transport + IDL + codegen) | Query language |
| IDL | .proto (Protobuf 3) | SDL (GraphQL schema) |
| wire format | Binary (Protobuf) | JSON (text) |
| transport | HTTP/2 only | HTTP/1.1·2·3, WS, SSE |
| 응답 모양 | 서버 결정 (proto가 고정) | 요청 결정 (selection set) |
| streaming | bidirectional 내장 | subscription (transport 의존) |
| 언어 지원 | 10+ 공식 (C++/Java/Go/Py/Ruby/JS…) | 사실상 전부 |
| 브라우저 직접 호출 | 불가 (grpc-web 필요) | 가능 (그냥 HTTP) |
| HTTP 캐시 | 불가 | 어려움 |
| introspection | reflection 옵션 (gRPC Reflection) | spec 의무 |
| versioning | proto 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는?