서버 간 통신 방식을 고를 때 REST는 여전히 가장 익숙한 선택지다. URL, HTTP method, JSON 응답이라는 조합은 브라우저와 curl만 있어도 확인할 수 있고, 대부분의 팀이 이미 운영 경험을 가지고 있다. 그런데 내부 마이크로서비스가 늘어나거나 모바일 네트워크에서 응답 크기와 지연 시간이 민감해지면 gRPC가 후보로 올라온다.
처음에는 "gRPC가 더 빠르다" 정도로만 이해하기 쉽다. 하지만 실제 선택은 속도 하나로 끝나지 않는다. gRPC 공식 문서에 따르면 gRPC는 Protocol Buffers로 서비스를 정의하고, 클라이언트가 원격 서버의 메서드를 로컬 객체처럼 호출할 수 있게 해주는 RPC 프레임워크다. 이 관점은 REST의 리소스 중심 설계와 출발점부터 다르다.
gRPC와 REST의 차이는 빠르냐 느리냐보다, 계약을 어디에 두고 어떤 운영 비용을 감수하느냐에 가깝다.
이 글에서는 gRPC와 REST를 "무엇을 얻고 무엇을 잃는가"라는 관점으로 정리한다. 특히 내부 서비스 통신, 외부 공개 API, 스트리밍, 디버깅, 배포 호환성에서 어떤 판단 기준이 필요한지 살펴본다.
REST는 엄밀히 말하면 특정 라이브러리나 프로토콜이 아니라 아키텍처 스타일이다. 실무에서는 보통 HTTP 위에서 리소스를 URL로 표현하고, GET, POST, PUT, PATCH, DELETE 같은 method로 상태 전이를 표현하는 API를 REST API라고 부른다. 메시지 포맷은 JSON이 가장 흔하지만 필수는 아니다.
gRPC는 접근 방식이 다르다. 먼저 .proto 파일에 서비스와 메시지 타입을 정의한다. 예를 들어 UserService.GetUser 같은 RPC 메서드와 요청, 응답 메시지를 선언한 뒤, 언어별 코드를 생성한다. 클라이언트는 생성된 stub을 통해 서버 메서드를 호출한다. gRPC 공식 문서의 Core concepts도 서비스 정의, RPC 종류, deadline, cancellation 같은 개념을 중심으로 설명한다.
두 방식의 출발점을 간단히 비교하면 다음과 같다.
| 구분 | REST | gRPC |
|---|---|---|
| 설계 중심 | 리소스 | 서비스 메서드 |
| 계약 위치 | 문서/OpenAPI 관례 | .proto 스키마 |
| 기본 메시지 | JSON이 흔함 | Protocol Buffers |
| 호출 형태 | HTTP request/response | unary, server streaming, client streaming, bidirectional streaming |
| 확인 방법 | 브라우저, curl로 비교적 쉬움 | grpcurl, generated client, reflection 등이 필요 |
REST는 느슨한 합의와 높은 접근성을 준다. 반대로 gRPC는 더 강한 계약과 생성 코드를 통해 컴파일 타임에 잡을 수 있는 오류를 늘린다. 어느 쪽이 더 좋다기보다, API를 사용하는 사람이 누구이고 변경을 어떻게 통제할지에 따라 선택이 달라진다.
REST 호출은 보통 "HTTP 요청 하나와 응답 하나"로 생각한다. 클라이언트가 /users/1로 GET 요청을 보내면 서버는 JSON을 내려준다. 캐시, 프록시, CDN, 브라우저 개발자 도구 같은 HTTP 생태계와 잘 맞는다. API Gateway나 observability 도구도 REST/JSON을 기본값으로 잘 지원하는 경우가 많다.
gRPC는 HTTP/2 위에서 동작하며, 메시지는 Protocol Buffers로 직렬화된다. HTTP/2 연결 하나 위에서 여러 스트림을 다룰 수 있고, gRPC 자체도 네 가지 호출 방식을 제공한다. 공식 문서 기준으로 unary RPC는 REST의 일반 요청/응답과 가장 비슷하고, server streaming은 서버가 여러 응답을 흘려보내며, client streaming은 클라이언트가 여러 요청을 보낸 뒤 응답 하나를 받는다. bidirectional streaming은 양쪽이 독립적으로 메시지를 주고받는다.
흐름을 단순화하면 다음과 같다.
REST
client -> HTTP request(JSON) -> server
client <- HTTP response(JSON) <- server
gRPC
client -> generated stub -> protobuf message -> HTTP/2 stream -> server method
client <- generated stub <- protobuf message <- HTTP/2 stream <- server method
여기서 gRPC의 장점은 계약이 실행 흐름에 더 깊게 들어온다는 점이다. .proto를 바꾸면 생성 코드도 바뀌고, 타입이 맞지 않는 호출은 컴파일 단계에서 드러날 수 있다. 여러 언어로 클라이언트를 만들어야 하는 조직에서는 이 점이 꽤 크다. API 문서와 실제 구현이 어긋나는 문제도 줄어든다.
대신 REST보다 "눈으로 보기 어려운" 비용이 생긴다. JSON은 로그에 그대로 남겨도 사람이 읽기 쉽지만, protobuf는 바이너리 직렬화라 별도 도구가 필요하다. curl로 빠르게 때려보는 경험도 gRPC에서는 grpcurl, server reflection, proto 파일 공유 같은 준비가 있어야 비슷해진다. 장애 대응 중에는 이런 작은 차이가 체감된다.
같은 사용자 조회 기능을 REST와 gRPC로 표현해보면 차이가 선명하다.
REST에서는 다음처럼 리소스와 HTTP method가 먼저 보인다.
GET /users/42 HTTP/1.1
Host: api.example.com
Accept: application/json
응답은 사람이 읽기 쉬운 JSON일 수 있다.
{
"id": "42",
"name": "Kim",
"status": "ACTIVE"
}
gRPC에서는 .proto가 중심이 된다.
syntax = "proto3";
service UserService {
rpc GetUser (GetUserRequest) returns (User);
}
message GetUserRequest {
string id = 1;
}
message User {
string id = 1;
string name = 2;
string status = 3;
}
REST는 "이 URL에 GET을 보내면 된다"는 설명만으로도 사용자가 빠르게 붙을 수 있다. gRPC는 처음에 proto와 생성 코드가 필요하지만, 일단 세팅이 끝나면 호출부가 명확해진다. 특히 Java, Go, Kotlin처럼 타입이 중요한 서버 코드에서는 요청/응답 필드가 바뀔 때 영향 범위를 더 빨리 발견할 수 있다.
스트리밍이 필요한 경우에는 gRPC 쪽이 더 자연스럽다. 예를 들어 서버가 작업 진행률을 계속 내려줘야 한다면 REST에서는 polling, Server-Sent Events, WebSocket 같은 선택지를 따로 검토해야 한다. gRPC는 server streaming을 서비스 정의 안에서 직접 표현할 수 있다.
반대로 공개 API라면 REST가 더 편할 때가 많다. 외부 개발자는 방화벽, 프록시, 언어 지원, 디버깅 도구가 제각각이다. JSON 기반 REST는 진입 장벽이 낮고, API 문서 페이지에서 바로 호출해보는 경험도 만들기 쉽다. gRPC-Web 같은 선택지도 있지만, 브라우저에서의 기본 경험은 REST가 더 넓게 닦여 있다고 보는 편이 안전하다.
내부 서비스 간 호출이라면 gRPC가 잘 맞는 경우가 많다. 호출자와 제공자가 같은 조직 안에 있고, proto 변경 규칙을 관리할 수 있으며, 서비스 수가 많아질수록 타입 계약과 코드 생성의 이점이 커진다. 특히 낮은 지연 시간, 작은 메시지 크기, 양방향 스트리밍, 다국어 클라이언트 생성을 중요하게 본다면 gRPC를 검토할 만하다.
외부 공개 API, 관리자용 단순 API, 브라우저 중심 제품이라면 REST가 여전히 강하다. HTTP 캐시와 프록시를 적극적으로 쓰거나, 사용자가 curl과 문서만 보고 붙어야 하거나, 운영자가 로그를 보고 바로 문제를 파악해야 하는 환경에서는 REST의 단순함이 장점이 된다.
운영 관점에서는 다음 질문이 도움이 됐다.
| 질문 | REST 쪽으로 기우는 경우 | gRPC 쪽으로 기우는 경우 |
|---|---|---|
| API 사용자는 누구인가 | 외부 개발자, 브라우저, 파트너사 | 내부 서비스, 통제 가능한 클라이언트 |
| 계약 변경은 어떻게 관리하는가 | 문서와 호환성 규칙 중심 | proto와 generated code 중심 |
| 메시지를 사람이 자주 봐야 하는가 | 로그와 수동 호출이 중요 | 도구 기반 디버깅을 받아들일 수 있음 |
| 스트리밍이 핵심인가 | 별도 기술을 붙여도 충분 | RPC 모델 안에서 표현하고 싶음 |
| 인프라 호환성은 어떤가 | 기존 HTTP 도구를 최대한 활용 | HTTP/2, gRPC 지원 체계가 준비됨 |
또 하나 중요한 것은 혼합 전략이다. 모든 API를 하나로 통일할 필요는 없다. 외부에는 REST를 제공하고, 내부 서비스 간 통신은 gRPC로 두는 식의 구조도 흔히 선택할 수 있다. 이 경우 API Gateway나 BFF 계층이 외부 JSON 계약과 내부 proto 계약 사이를 변환한다. 변환 계층의 비용은 생기지만, 각 경계에 맞는 장점을 취할 수 있다.
gRPC는 강한 스키마, 생성 코드, HTTP/2 기반 스트리밍을 통해 내부 서비스 통신에서 매력적인 선택지가 된다. REST는 리소스 중심 모델, 넓은 도구 생태계, 낮은 진입 장벽 덕분에 공개 API와 단순한 CRUD 인터페이스에서 여전히 강하다.
이 둘은 상하 관계라기보다 경계가 다른 도구다. gRPC 공식 docs가 강조하는 서비스 정의와 RPC 호출 모델을 보면, gRPC는 "HTTP API를 더 빠르게 만든 것"이라기보다 "서비스 계약을 코드 가까이에 둔 RPC 시스템"에 가깝다. REST는 반대로 HTTP의 보편성과 사람이 읽기 쉬운 표현을 활용해 느슨한 연결을 만든다.
다음에 더 파고들 주제로는 HTTP/2 multiplexing이 실제 지연 시간에 어떤 영향을 주는지, 그리고 proto 필드 번호와 하위 호환성 규칙을 어떻게 운영해야 하는지가 남는다. gRPC를 도입한다면 성능 벤치마크보다 먼저 이 두 가지 운영 규칙을 팀 안에서 합의하는 편이 더 중요하다고 느꼈다.
.proto 기반 메시지 정의와 코드 생성 흐름