Backend

gRPC vs REST, 내부 통신에는 왜 모두 gRPC를 쓰는가

DevNinja 2026. 4. 28. 23:24
728x90
반응형

마이크로서비스를 도입한 회사에 들어가보면 흥미로운 패턴이 있다. 외부 클라이언트에는 모두 REST API로 응답하면서, 내부 서비스끼리는 모두 gRPC로 통신한다. 토스도 그렇고 우아한형제들도 그렇고 넷플릭스도 그렇다. 왜 이렇게 안과 밖을 갈라놓는 것인가?

 

REST를 잘 쓰고 있었는데 왜 굳이 gRPC를 또 배워야 하는지 궁금했다. 그래서 gRPC vs REST 비교는 결국 어디서 갈라지는지, gRPC가 왜 만들어졌고 HTTP와 무엇이 다른지, 어디에 써야 하고 어디에 쓰지 말아야 하는지 직접 파봤다. 결론부터 말하면 "둘 중 무엇이 더 좋다"가 아니라 "각자 잘하는 영역이 다르다" 이며, 그 차이를 만드는 것은 결국 HTTP/2와 Protocol Buffer라는 두 기술이다.

 

 

출처: slideshare.net

 

RPC란 무엇인가, 네트워크 너머로 이루어지는 함수 호출이다

 

gRPC를 이해하려면 먼저 RPC(Remote Procedure Call)가 무엇인지부터 살펴봐야 한다. 이름 그대로 "원격 프로시저 호출"이다. 풀어 말하면 "내 코드에서 함수를 부르듯 다른 서버 함수도 부르고 싶다" 는 욕망에서 나온 개념이다.

 

로컬 함수 호출과 같은 인터페이스로 원격 호출하는 것이 RPC다

 

평소에 getUser(id) 같은 함수를 부르면 같은 프로세스 안에서 결과를 받아온다. 그런데 그 함수가 다른 서버에 있다면? REST 스타일이라면 fetch("https://api.example.com/users/123") 식으로 URL을 만들고 HTTP 메서드를 정하고 JSON을 파싱해야 한다. 사람이 매번 신경 써야 할 것이 너무 많다.

 

RPC는 이를 추상화하여 그저 userService.getUser(123) 처럼 부르면 알아서 네트워크 너머로 넘어가 결과를 받아오는 모델이다. 클라이언트가 보기에는 단순한 함수 호출이고 네트워크는 보이지 않는 척한다. 이것이 RPC의 핵심 철학이다.

 

gRPC는 구글이 만든 RPC 프레임워크다

 

RPC 개념 자체는 1980년대부터 있었다. CORBA, SOAP, Java RMI, Apache Thrift(페이스북 출신) 같은 것이 모두 RPC 계보다. 다만 다들 너무 무겁거나(SOAP은 XML 지옥), 특정 언어에 묶여 있거나(RMI는 자바 전용), 표준화가 약했다.

 

gRPC는 2015년 구글이 오픈소스로 공개했다. "g"가 무엇의 약자인지는 매 릴리스마다 바뀌는데(green, glorious, good 등) 사실상 "google"이라 봐도 무방하다. 구글이 사내에서 쓰던 Stubby라는 RPC 프레임워크를 외부에 다듬어 낸 것이다. 핵심은 HTTP/2 위에서 Protocol Buffer로 직렬화하는 RPC 라는 점이다. 이 두 가지가 gRPC vs REST 차이의 거의 전부다.

 

HTTP REST는 무엇이 부족했기에 gRPC가 등장했는가

 

REST가 잘 굴러가는데 왜 또 새로 만들었는가. REST가 못하는 것이 있었기 때문이다. REST API의 단점 몇 가지를 짚고 가야 한다.

 

REST는 사람이 읽으라고 만든 것이다

 

REST의 기본 페이로드는 JSON이다. JSON은 사람이 읽기 좋게 만들어진 텍스트 포맷이다. {"user_id": 123, "name": "홍길동"} 식으로 키 이름이 모두 박혀 있고 들여쓰기까지 한다. 사람에게는 친절하지만 기계 입장에서는 낭비가 심하다.

 

같은 데이터를 Protocol Buffer로 보내면 키 이름이 숫자 태그(1, 2)로 압축되고, 정수는 가변 길이 인코딩으로 들어간다. 결과적으로 같은 정보가 약 3~10배 작은 바이트 로 줄어든다. 트래픽이 많은 서비스에서는 이것이 그대로 비용이다.

 

JSON 직렬화 비용은 생각보다 크다

 

JSON은 텍스트 파싱이 필요하다. 문자열을 읽고, 따옴표를 찾고, 콜론을 찾고, 타입을 추론한다. 한 번이라면 무시할 수 있지만 초당 수만 건을 처리하는 백엔드에서는 CPU 점유율로 잡힌다. Protocol Buffer는 바이너리 스트림을 스키마대로 그저 읽으면 된다. 파싱이 아니라 디코딩에 가깝다. 직렬화/역직렬화 속도가 보통 2~5배 빠른 것으로 측정된다.

 

HTTP/1.1의 헤드 오브 라인 블로킹 문제

 

REST는 보통 HTTP/1.1 위에서 도는데, HTTP/1.1은 하나의 TCP 커넥션에서 한 번에 한 요청만 처리한다. 앞 요청이 끝나지 않으면 뒤 요청은 줄을 서야 한다. 이를 "헤드 오브 라인 블로킹(Head-of-Line Blocking)"이라 부른다. 우회하려고 브라우저는 도메인당 6개씩 커넥션을 열고, 백엔드는 커넥션 풀을 키우는 등 별의별 짓을 다 하지만 근본적인 해결은 아니다.

 

여기에 매 요청마다 헤더가 같은 내용을 또 실어 보낸다. Authorization: Bearer ..., User-Agent: ..., Accept: ... 같은 것이 매 요청마다 평문으로 다시 가는 것이다. 작은 API 호출인데 헤더가 페이로드보다 큰 경우도 흔하다.

 

 

출처: Stack Overflow

 

gRPC가 빠른 진짜 이유는 HTTP/2와 Protocol Buffers다

 

이제 gRPC와 REST 차이의 핵심으로 들어간다. gRPC는 위에서 말한 REST의 약점 두 가지를 정확히 정조준하여 풀어버린다.

 

HTTP/2 멀티플렉싱이 RTT를 어떻게 줄이는가

 

HTTP/2 표준(RFC 7540)은 한 TCP 커넥션 안에 여러 "스트림"을 동시에 흘려보낼 수 있게 한다. 요청 A, B, C가 동시에 출발하고 응답도 도착하는 대로 받는다. 줄을 서지 않아도 된다. 헤드 오브 라인 블로킹이 TCP 레이어에는 남아 있지만 HTTP 레이어에서는 사라진다.

 

거기에 HPACK 헤더 압축 이 붙는다. 첫 요청에서 본 헤더는 양쪽 모두 테이블에 저장하고, 다음 요청부터는 인덱스 번호만 보낸다. Authorization: Bearer eyJhbGc... 같은 길쭉한 토큰이 두 번째 요청부터는 62 같은 숫자 한 개로 줄어든다. 마이크로서비스끼리 초당 수천 번 호출할 때 이 절감 효과가 진짜 크다.

 

또 하나는 양방향 스트리밍(Bidirectional Streaming) 이다. 클라이언트와 서버가 동시에 메시지를 주고받을 수 있다. 채팅 서비스, 실시간 알림, 대용량 파일 업로드 같은 시나리오를 REST는 어색하게 풀어야 하는데 gRPC는 그저 지원한다.

 

Protocol Buffer는 바이너리이자 스키마 기반이라 작고 빠르다

 

Protocol Buffer(줄여서 protobuf)는 구글이 만든 직렬화 포맷이다. .proto 파일에 스키마를 정의하면 컴파일러(protoc)가 각 언어용 코드를 자동 생성해준다.

 

message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
}

 

이 스키마로 User{id: 123, name: "홍길동"} 을 직렬화하면 키 이름은 사라지고 태그 번호(1, 2)와 값만 바이너리로 남는다. 같은 데이터의 JSON 페이로드 대비 보통 30~70% 작아진다. 거기에 정수 가변 길이 인코딩, 패킹 최적화 같은 것이 들어가면 더 줄어들기도 한다.

 

코드 제너레이션으로 타입 안정성을 확보한다

 

스키마가 있으므로 클라이언트와 서버 양쪽 모두 같은 타입을 공유한다. REST에서 흔히 보이는 "API 응답 필드 이름이 바뀌었는데 클라이언트는 모르고 NPE가 터지는" 사고가 거의 발생하지 않는다. 컴파일 타임에 막힌다. 자바, 고, 파이썬, 코틀린, 러스트, C++을 모두 지원하므로 언어가 다양한 마이크로서비스 환경에서 진짜 강력하다.

 

여기에 인터셉터(Interceptor) 개념이 있어서 인증·로깅·재시도·메트릭 같은 공통 로직을 한 군데에 박아놓고 모든 RPC 호출에 적용 가능하다. REST에서는 미들웨어로 비슷하게 처리하기는 하지만 클라이언트와 서버 쪽의 통일된 추상화가 없어서 매번 다시 짜야 한다.

 

 

출처: YouTube

 

그렇다면 왜 외부 API에는 gRPC를 쓰지 않는가

 

여기서부터가 진짜 중요한 부분이다. gRPC가 그토록 좋다면 왜 다들 외부 API는 여전히 REST로 쓰는 것인가. gRPC 장단점 중 단점이 명확하기 때문이다.

 

브라우저는 HTTP/2 트레일러를 읽지 못한다

 

gRPC는 응답의 메타데이터 일부(특히 상태 코드)를 HTTP/2 trailer 헤더에 실어 보낸다. 그런데 브라우저의 fetch API는 trailer를 읽지 못한다. 그래서 브라우저에서 gRPC를 직접 호출할 수 없다. gRPC-Web 이라는 우회 프로토콜이 나오기는 했지만 양방향 스트리밍이 빠지고 프록시 한 단계가 더 필요하다. 외부 클라이언트가 브라우저라면 gRPC는 그저 손해다.

 

디버깅이 어렵다

 

REST는 curl https://api.example.com/users/123 한 줄로 테스트된다. Postman, 브라우저 개발자 도구도 모두 가능하다. 페이로드는 JSON이라 사람이 그대로 읽는다.

 

gRPC는 바이너리라 페이로드를 사람이 읽지 못한다. 디버깅하려면 grpcurl 같은 전용 도구가 필요하고, 그것도 서버가 reflection을 활성화해두지 않았다면 .proto 파일을 직접 들고 다녀야 한다. 외부 개발자가 우리 API에 붙이려고 할 때 진입장벽이 너무 높다.

 

클라이언트가 스키마(.proto)를 알아야 한다

 

REST는 OpenAPI 스펙이 없어도 일단 호출은 된다. 응답을 받아서 dict를 까보면 끝이다. gRPC는 스키마가 없으면 호출 자체가 되지 않는다. 외부 파트너에게 .proto 파일을 보내고, 컴파일하여 쓰라고 하는 것은 보통 부담스러운 일이 아니다. 외부 API의 본질은 자유도 인데 gRPC는 그 자유도를 줄여서 성능을 사는 거래다.

 

그래서 결론은 명확하다. 외부에 노출되는 API는 클라이언트 환경을 통제하지 못하므로 REST(혹은 GraphQL)로 가는 것이 맞고, 내부 마이크로서비스끼리는 환경을 통제할 수 있으므로 gRPC가 압도적으로 유리하다.

 

그렇다면 언제 gRPC를 쓰면 되는가, 의사결정 가이드

 

이는 마이크로서비스 통신 아키텍처를 짜는 사람들이 진짜 궁금해하는 부분이다. 단순화하면 아래 표 정도로 정리된다.

 

상황 추천 이유
마이크로서비스 간 내부 통신 gRPC 페이로드가 작고, 타입이 안전하고, 스트리밍이 가능하다
모바일 앱 → 백엔드 트래픽 많으면 gRPC 데이터 사용량·배터리 절감, 그 외에는 REST
브라우저 → 백엔드 REST 또는 GraphQL gRPC-Web 한계, 디버깅 편의성
공개 API, 외부 통합 REST 진입장벽이 낮고 도구가 풍부하다
실시간 채팅·푸시 gRPC 양방향 스트리밍 또는 WebSocket REST는 polling을 해야 한다
단순 CRUD 백오피스 REST gRPC 도입 비용이 효익을 따라가지 못한다

 

마이크로서비스 간 내부 통신은 거의 무조건 gRPC다

 

내부 서비스끼리는 환경을 모두 통제 가능하다. 모든 서비스가 .proto를 공유하고, 같은 사내 도구로 모니터링하고, 디버깅도 통합 트레이싱으로 한다. 이 환경에서는 gRPC의 단점(브라우저 호환성, 디버깅 도구)은 거의 의미가 없고 장점(성능, 타입 안정성, 스트리밍)만 남는다. 우아한형제들이 배달 시스템 내부 통신에 gRPC를 쓴다고 공개한 적이 있고, 토스도 코어 서비스 내부 통신을 gRPC로 통일했다고 알려져 있다.

 

트래픽이 적으면 쓰지 않아도 된다

 

다만 솔직히 트래픽이 1초에 수십 건도 안 되는 작은 서비스라면 그저 REST를 써도 된다. gRPC를 도입하면 빌드 파이프라인에 protoc 추가, 스키마 버저닝 정책 수립, 디버깅 도구 셋업, 팀원 온보딩 비용까지 모두 들어간다. 성능 차이가 비즈니스에 보이지 않는 수준이라면 단순함이 이긴다. "구글이 만든 것이니 좋겠지"라는 이유로 도입하면 절반은 후회한다.

 

팀이 작고 언어가 단일하면 효익이 줄어든다

 

자바만 쓰는 10명 팀이라면 그저 자바 RPC 라이브러리나 REST를 써도 충분하다. gRPC의 진가는 언어가 다양할수록, 서비스가 많을수록 커진다. 자바·고·파이썬·코틀린이 섞여 있고 서비스가 30개를 넘으면 그때부터는 gRPC를 쓰지 않는 것이 손해다.

 

gRPC vs REST 선택, 누가 클라이언트인지가 모든 것을 결정한다

 

정리하면 gRPC vs REST 논쟁은 결국 "누가 클라이언트인가" 의 문제다. 클라이언트가 우리 통제 안에 있다면(=내부 서비스) gRPC가 거의 무조건 좋다. 클라이언트가 우리 통제 밖에 있다면(=외부 개발자, 브라우저) REST가 안전하다.

 

핵심만 추려보면 다음과 같다.

 

  • gRPC가 빠른 이유는 마법이 아니다: HTTP/2 멀티플렉싱·HPACK 헤더 압축과 Protocol Buffer 바이너리 직렬화 두 가지의 합작품이다
  • REST가 느린 이유도 명확하다: JSON 텍스트 파싱 비용과 HTTP/1.1 헤드 오브 라인 블로킹, 매 요청 헤더 중복
  • gRPC를 외부에 쓰지 않는 것은 단점 때문이다: 브라우저 비호환, 디버깅 어려움, 스키마 공유 부담
  • 의사결정 기준: 내부 마이크로서비스 통신이면 gRPC, 외부 공개 API면 REST, 둘 다 필요하면 둘 다 쓴다

 

마이크로서비스 도입을 검토 중이라면 처음부터 모든 통신을 gRPC로 통일하려 하지 말고, 내부 서비스 간 통신부터 gRPC로 점진적으로 옮기는 것이 안전하다. 외부 API는 REST로 유지하고 그 앞단에 BFF(Backend for Frontend) 같은 레이어를 두어 내부에서는 gRPC로 호출하는 패턴이 요즘 표준이다. 결국 gRPC vs REST 선택은 누가 클라이언트인지의 문제이며, 그래서 회사들은 둘 다 쓰는 것이다.

728x90
반응형