HTTP REST(JSON) vs. gRPC(Protocol Buffers): 언제 무엇을 쓸까
이 글은 Claude Opus 4.8 을 이용해 초안이 작성되었으며, 이후 퇴고를 거쳤습니다.
백엔드 서버를 설계할 때 거의 항상 마주치는 갈림길이 있습니다. “이 API 를 JSON 기반 HTTP REST 로 열까, 아니면 gRPC 로 열까?” 하는 질문입니다. 팀 컨벤션에 따라 별생각 없이 한쪽으로 정해지는 경우도 많지만, 두 방식은 성능·개발 경험·운영 부담·클라이언트 호환성 등 여러 축에서 뚜렷하게 다른 트레이드오프를 가집니다.
이 글은 웹 백엔드를 다루는 중급 이상 개발자를 대상으로, 두 방식의 작동 원리와 장단점을 정리하고 “어떤 상황에서 무엇을 고르는 것이 합리적인지” 를 구체적인 예시와 함께 설명합니다. 결론부터 말하면 “무조건 하나가 낫다"는 답은 없으며, 통신의 상대가 누구인지(외부 클라이언트 vs. 내부 서비스) 가 선택의 가장 강력한 기준이 됩니다.
두 방식의 기본 구조#
본격적인 비교에 앞서, 각 방식이 무엇을 어떻게 주고받는지 짚고 넘어가겠습니다.
HTTP REST + JSON#
REST 는 특정 프로토콜이 아니라 아키텍처 스타일입니다. 실무에서는 보통 다음 조합을 의미합니다.
- 전송 계층: HTTP/1.1 또는 HTTP/2
- 데이터 포맷: JSON (텍스트 기반)
- 의미 표현: URL 경로 + HTTP 메서드(GET/POST/PUT/DELETE) + 상태 코드(200/404/500 등)
예를 들어 사용자 정보를 조회하는 API 는 다음과 같은 모습입니다.
GET /users/42 HTTP/1.1
Host: api.example.com
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 42, "name": "Alice", "email": "alice@example.com"}
사람이 눈으로 읽을 수 있고, curl 이나 브라우저, Postman 으로 즉시 두드려볼 수 있다는 점이 핵심입니다.
gRPC + Protocol Buffers#
gRPC 는 Google 이 만든 RPC(Remote Procedure Call) 프레임워크입니다. 원격 서버의 함수를 로컬 함수 호출처럼 부르는 방식이며, 다음 요소로 구성됩니다.
- 전송 계층: HTTP/2 (필수)
- 데이터 포맷: Protocol Buffers(protobuf), 바이너리 기반
- 인터페이스 정의:
.proto파일에 서비스와 메시지 스키마를 선언 → 코드 생성
같은 사용자 조회를 gRPC 로 정의하면 먼저 스키마를 작성합니다.
syntax = "proto3";
message GetUserRequest {
int64 id = 1;
}
message User {
int64 id = 1;
string name = 2;
string email = 3;
}
service UserService {
rpc GetUser(GetUserRequest) returns (User);
}
이 .proto 로부터 protoc 가 서버 스텁과 클라이언트 스텁 코드를 자동 생성합니다. 클라이언트는 client.GetUser(GetUserRequest{Id: 42}) 처럼 타입이 보장된 함수 호출로 서버와 통신하게 됩니다. 네트워크로 오가는 실제 바이트는 사람이 읽을 수 없는 바이너리입니다.
HTTP REST(JSON) 의 장단점#
장점#
1. 압도적인 범용성과 접근성
브라우저, 모바일, 서버, IoT 기기 등 HTTP 를 말할 수 있는 거의 모든 환경이 별도 준비 없이 클라이언트가 됩니다. 특히 웹 브라우저에서 별도 프록시 없이 바로 호출 가능하다는 점은 프런트엔드와 직접 통신할 때 결정적인 장점입니다.
2. 사람이 읽을 수 있어 디버깅이 쉽다
JSON 은 텍스트라 로그에 그대로 남고, curl 한 줄로 재현할 수 있으며, 네트워크 탭에서 응답을 눈으로 확인할 수 있습니다. 장애 대응 속도에 직접적인 영향을 줍니다.
3. 성숙한 생태계와 표준 시맨틱
HTTP 캐싱(ETag, Cache-Control), CDN, 로드 밸런서, API 게이트웨이, 인증 미들웨어 등이 모두 HTTP 를 1급 시민으로 다룹니다. 상태 코드·헤더 같은 표준 의미 체계를 그대로 활용할 수 있습니다.
4. 느슨한 결합
클라이언트와 서버가 스키마를 강제로 공유하지 않아도 동작합니다. OpenAPI(Swagger)로 계약을 문서화할 수 있지만, 없어도 통신 자체는 성립합니다. 외부 개발자에게 공개하는 Public API 에 특히 잘 맞습니다.
단점#
1. 페이로드가 크고 직렬화 비용이 높다
JSON 은 필드명을 매번 문자열로 반복해 담고, 숫자도 텍스트로 표현합니다. 같은 데이터라도 protobuf 대비 payload 가 몇 배 커지고, 파싱·직렬화에 드는 CPU 도 더 많습니다. 대량 트래픽·저대역폭 환경에서 누적 비용이 됩니다.
2. 스키마·타입 안전성이 약하다
계약을 언어 차원에서 강제하지 않으므로, 필드 오타나 타입 불일치가 런타임에야 드러나는 경우가 많습니다. OpenAPI 로 코드 생성을 하면 완화되지만, gRPC 만큼 강하게 보장되지는 않습니다.
3. 스트리밍이 1급 기능이 아니다
서버 → 클라이언트 지속 전송은 SSE(Server-Sent Events), 양방향은 WebSocket 처럼 별도 메커니즘을 얹어야 합니다. 실시간·양방향 통신을 프로토콜 자체가 자연스럽게 지원하지는 않습니다.
4. 규격이 느슨해 팀마다 제각각
“REST 하다"의 기준이 모호해서, 에러 응답 형태·페이지네이션·필드 네이밍 등이 팀·서비스마다 달라지기 쉽습니다. 일관성을 위해서는 별도의 규약과 린팅이 필요합니다.
gRPC(Protocol Buffers) 의 장단점#
장점#
1. 작고 빠른 바이너리 전송
protobuf 는 필드를 이름 대신 번호로 식별하고 데이터를 바이너리로 인코딩합니다. 그 결과 payload 가 작고 직렬화·역직렬화가 빠릅니다. 응답 크기가 JSON 대비 크게 줄어드는 경우가 흔하며, 대량 호출에서 네트워크·CPU 를 함께 아낍니다.
2. 스키마 기반의 강한 타입 안전성과 코드 생성
.proto 라는 단일 계약에서 서버·클라이언트 코드가 생성됩니다. 필드 타입이 컴파일 타임에 보장되고, Go·Java·Python·Kotlin·C++ 등 여러 언어가 같은 계약을 공유할 수 있어 폴리글랏 환경에 강력합니다.
3. HTTP/2 기반의 멀티플렉싱과 스트리밍
하나의 커넥션에서 여러 요청을 동시에 처리(멀티플렉싱)하고 헤더를 압축합니다. 또한 네 가지 RPC 패턴을 프로토콜 차원에서 지원합니다.
- Unary: 요청 1 → 응답 1 (일반적인 호출)
- Server streaming: 요청 1 → 응답 N
- Client streaming: 요청 N → 응답 1
- Bidirectional streaming: 요청 N ↔ 응답 N (양방향)
4. 진화에 강한 스키마 규칙
필드에 부여한 번호를 유지하는 한, 새 필드를 추가해도 구버전 클라이언트가 깨지지 않습니다. 하위·상위 호환을 지키기 위한 규칙이 명확해 장기적인 API 진화에 유리합니다.
5. 내장된 데드라인·취소·인터셉터
호출 단위 타임아웃(deadline), 취소 전파, 인터셉터(미들웨어) 같은 분산 시스템 필수 기능이 프레임워크에 내장되어 있습니다.
단점#
1. 브라우저에서 직접 호출할 수 없다
브라우저는 HTTP/2 프레임을 임의로 제어할 수 없어 표준 gRPC 를 그대로 호출하지 못합니다. 웹에서 쓰려면 gRPC-Web 과 Envoy 같은 프록시가 추가로 필요하며, 이 경로에서는 양방향 스트리밍 등 일부 기능이 제한됩니다.
2. 바이너리라 사람이 읽기 어렵다
와이어를 눈으로 볼 수 없어 디버깅 도구(grpcurl, 리플렉션 등)가 별도로 필요합니다. 급한 장애 상황에서 curl 로 바로 찔러보는 편의성은 떨어집니다.
3. 러닝 커브와 빌드 파이프라인 부담
.proto 관리, protoc/plugin 설치, 코드 생성 단계가 빌드에 추가됩니다. 팀이 처음 도입하면 학습·정착 비용이 있습니다.
4. HTTP 캐싱·CDN 시맨틱을 그대로 못 쓴다
REST 가 공짜로 누리는 Cache-Control·CDN 캐싱 같은 표준 캐싱 체계를 활용하기 어렵습니다. 캐싱이 필요하면 애플리케이션 레벨에서 직접 설계해야 합니다.
5. 로드 밸런싱이 까다롭다
gRPC 는 하나의 HTTP/2 커넥션을 오래 유지하며 그 위로 요청을 멀티플렉싱합니다. 그래서 커넥션 단위(L4) 로드 밸런서로는 부하가 한쪽으로 쏠리기 쉽고, 요청 단위로 분산하려면 L7(예: Envoy)이나 클라이언트 사이드 로드 밸런싱이 필요합니다.
한눈에 보는 비교#
| 항목 | HTTP REST + JSON | gRPC + Protobuf |
|---|---|---|
| 데이터 포맷 | 텍스트(JSON) | 바이너리(protobuf) |
| payload 크기 | 큼 | 작음 |
| 직렬화 속도 | 느림 | 빠름 |
| 전송 계층 | HTTP/1.1 또는 HTTP/2 | HTTP/2 (필수) |
| 스키마/타입 안전성 | 약함(OpenAPI 로 보완) | 강함(.proto 강제) |
| 브라우저 직접 호출 | 가능 | 불가(gRPC-Web + 프록시 필요) |
| 스트리밍 | SSE/WebSocket 로 별도 구현 | 4종 스트리밍 기본 지원 |
| 디버깅 편의성 | 높음(curl, 브라우저) |
낮음(grpcurl 등 필요) |
| HTTP 캐싱/CDN | 표준으로 활용 | 직접 설계 필요 |
| 로드 밸런싱 | 단순(L4 로도 충분) | 까다로움(L7/클라이언트 LB) |
| 폴리글랏 코드 생성 | 선택(OpenAPI) | 기본 제공 |
| 러닝 커브 | 낮음 | 상대적으로 높음 |
언제 무엇을 쓸까: 상황별 가이드#
가장 강력한 판단 기준은 “이 통신의 상대가 누구인가” 입니다. 통제 불가능한 외부 클라이언트를 상대하면 REST 쪽으로, 우리가 통제하는 내부 서비스끼리면 gRPC 쪽으로 기웁니다.
웹 프런트엔드·모바일 앱을 직접 상대하는 API → 대체로 REST/JSON#
브라우저에서 바로 호출할 수 있고, 디버깅이 쉬우며, 캐싱·CDN 등 웹 인프라를 그대로 활용할 수 있습니다. 일반적인 CRUD 위주 서비스라면 REST 의 단순함이 개발·운영 양쪽에서 이깁니다.
예: 커머스 사이트의 상품 조회·장바구니 API, 콘텐츠 서비스의 게시글 CRUD. 트래픽이 극단적으로 크지 않고 클라이언트가 다양하다면 REST 가 무난합니다.
다만 모바일 앱처럼 클라이언트를 우리가 배포하고 대역폭이 중요한 경우에는 gRPC 도 후보가 됩니다. 저사양·저대역폭 네트워크에서 payload 절감 효과가 사용자 경험에 직접 반영되기 때문입니다. 실제로 gRPC 는 모바일-백엔드 통신을 겨냥해 설계된 측면이 있습니다.
내부 마이크로서비스 간 통신 → 대체로 gRPC#
서비스가 잘게 쪼개질수록 서비스 간 호출량이 폭발적으로 늘고, 여기서 payload 크기와 직렬화 비용이 누적 지연으로 돌아옵니다. 이 구간은 우리가 클라이언트와 서버를 모두 통제하므로 브라우저 호환성 문제도 없습니다.
예: 주문 서비스가 결제·재고·배송 서비스를 호출하는 내부 경로.
.proto로 계약을 고정하면 팀 간 인터페이스 오해가 줄고, 폴리글랏(Go 서비스 ↔ Java 서비스)이어도 같은 스키마를 공유할 수 있습니다.
실시간·양방향 스트리밍 → gRPC#
채팅, 실시간 위치 추적, IoT 텔레메트리, 서버 푸시처럼 지속적·양방향 데이터 흐름이 필요하면 gRPC 의 스트리밍이 자연스럽습니다. REST 로도 SSE·WebSocket 으로 가능하지만, gRPC 는 이를 타입 안전한 하나의 계약 안에서 제공합니다.
외부에 공개하는 Public API → 대체로 REST/JSON#
서드파티 개발자가 문서만 보고 curl 로 바로 시험해볼 수 있어야 채택률이 높습니다. 느슨한 결합과 광범위한 클라이언트 호환성이 중요하며, 이 지점에서는 REST 가 사실상 표준입니다.
데이터·스트리밍 파이프라인, 고QPS 내부 시스템 → gRPC#
머신러닝 추론 서버, 대량 이벤트 수집, 지연에 민감한 내부 고QPS 시스템은 protobuf 의 직렬화 효율과 HTTP/2 멀티플렉싱 덕을 크게 봅니다.
결정 흐름도#
flowchart TD
A[새 API 를 설계한다] --> B{통신 상대가<br/>브라우저 또는<br/>외부 개발자인가?}
B -->|예| C[REST/JSON 우선]
B -->|아니오<br/>내부 서비스| D{실시간 양방향<br/>스트리밍이<br/>필요한가?}
D -->|예| E[gRPC]
D -->|아니오| F{성능·폴리글랏·<br/>엄격한 계약이<br/>중요한가?}
F -->|예| E
F -->|아니오| G[REST/JSON 도 무난]
style A fill:#87CEEB,color:#000000
style B fill:#FFD700,color:#000000
style C fill:#90EE90,color:#000000
style D fill:#FFD700,color:#000000
style E fill:#90EE90,color:#000000
style F fill:#FFD700,color:#000000
style G fill:#90EE90,color:#000000
둘 중 하나만 고를 필요는 없다: 하이브리드 전략#
실무에서는 “전사적으로 하나만"보다 경계마다 다른 규격을 쓰는 조합이 더 흔하고 합리적입니다.
1. 엣지는 REST, 내부는 gRPC
외부·프런트엔드를 마주하는 API 게이트웨이(또는 BFF, Backend for Frontend)는 REST/JSON 으로 열고, 그 뒤 내부 서비스 간 통신은 gRPC 로 처리합니다. 접근성과 성능을 각각의 자리에서 챙기는 가장 흔한 패턴입니다.
2. gRPC-Gateway 로 하나의 계약에서 둘 다
grpc-gateway 같은 도구를 쓰면 .proto 하나에서 gRPC 서버와 그 앞단의 RESTful JSON 프록시를 함께 생성할 수 있습니다. 내부는 gRPC 로 부르고 외부에는 REST 로 노출하되, 계약은 단일 소스로 유지됩니다.
3. gRPC-Web 으로 브라우저까지
브라우저에서 gRPC 서비스를 직접 부르고 싶다면 gRPC-Web + Envoy 조합을 씁니다. 단, 프록시 운영 부담과 일부 스트리밍 제약을 감수해야 하므로, 얻는 이득이 그 비용을 넘는지 따져봐야 합니다.
4. Connect 프로토콜 같은 현대적 대안
최근에는 gRPC·gRPC-Web·HTTP/JSON 을 하나의 프레임워크로 아우르는 Connect(buf) 같은 선택지도 있습니다. protobuf 계약을 유지하면서 브라우저에서 프록시 없이 호출하고, 필요하면 JSON 으로도 응답을 받을 수 있어 두 세계의 간극을 좁혀줍니다. 새 프로젝트라면 검토해볼 만합니다.
실전 팁#
- 기본값은 상대에 맞춥니다. 외부·브라우저를 마주하면 REST 부터, 내부 서비스끼리면 gRPC 부터 검토하는 편이 후회가 적습니다.
- 성능 이득을 측정 없이 단정하지 마십시오. “gRPC 가 빠르다"는 일반론은 맞지만, 트래픽이 작은 서비스에서는 REST 의 단순함이 주는 개발·운영 이득이 더 큽니다. 병목이 실제로 직렬화·payload 인지 먼저 프로파일링하십시오.
- 계약을 먼저 정하십시오(contract-first). gRPC 는
.proto, REST 는 OpenAPI 로 계약을 코드보다 먼저 고정하면, 팀 간 인터페이스 오해와 브레이킹 체인지를 크게 줄일 수 있습니다. - gRPC 를 쓴다면 로드 밸런싱을 처음부터 설계하십시오. L4 로드 밸런서 뒤에 그냥 두면 부하가 한 인스턴스로 쏠립니다. L7 프록시나 클라이언트 사이드 LB 를 도입 시점에 함께 계획하십시오.
- 관측 가능성(observability)을 잊지 마십시오. 바이너리 특성상 gRPC 는 로그만으로 파악이 어렵습니다. 인터셉터로 구조화된 로깅·트레이싱을 초기에 심어두면 운영이 편해집니다.
- 마이그레이션은 점진적으로 진행하십시오. 기존 REST 시스템을 gRPC 로 옮길 때는 트래픽이 많고 내부적인 경로부터 골라 부분 전환하고, 하이브리드 상태를 정상으로 받아들이십시오.
정리#
HTTP REST(JSON)와 gRPC(protobuf)는 우열이 아니라 트레이드오프의 문제입니다.
- REST/JSON 은 범용성·접근성·디버깅 편의·웹 인프라 호환에서 강하고, 외부 클라이언트와 브라우저를 상대하는 자리에 잘 맞습니다.
- gRPC 는 성능·타입 안전성·스트리밍·폴리글랏 계약에서 강하고, 우리가 통제하는 내부 서비스 간 고QPS·실시간 통신에 잘 맞습니다.
선택이 애매할 때는 “이 통신의 상대가 브라우저·외부 개발자인가, 아니면 우리가 배포하는 내부 서비스인가"를 먼저 물어보십시오. 그리고 전사적으로 하나만 고르기보다, 엣지는 REST·내부는 gRPC 처럼 경계마다 알맞은 규격을 배치하는 하이브리드가 대부분의 현실 서비스에서 가장 균형 잡힌 답입니다.