1. 라이브러리
  2. HTTP와 웹
  3. 최신 웹 프로토콜

업데이트됨 1개월 전

gRPC는 원격 서버의 함수를 마치 로컬에서 실행하는 것처럼 호출할 수 있게 해주는 원격 프로시저 호출(Remote Procedure Call) 프레임워크입니다.

함수를 호출합니다. 그 함수는 다른 머신에서 실행됩니다. 네트워크는 사라집니다.

이것이 RPC의 약속이며, Google이 개발한 gRPC는 현대적인 인프라로 이를 실현합니다: 효율적인 연결을 위한 HTTP/2, 압축된 바이너리 직렬화를 위한 Protocol Buffers, 그리고 원시 HTTP 요청을 직접 구성하는 대신 실제로 타입이 지정된 함수를 호출할 수 있도록 코드를 자동으로 생성해주는 코드 생성 기능이 바로 그것입니다.

gRPC가 해결하는 문제

현대 애플리케이션은 서로 통신하는 서비스들의 집합입니다. 사용자 요청 하나가 인증, 사용자 프로필, 재고, 결제, 배송, 알림 서비스를 모두 거칠 수 있습니다. 유용한 작업이 시작되기 전에 이미 여섯 번의 네트워크 호출이 발생합니다.

REST를 사용하면 각 호출마다 다음을 수행해야 합니다:

  • HTTP 요청 구성
  • 데이터를 JSON으로 직렬화
  • 네트워크를 통해 바이트 전송
  • 반대편에서 JSON 파싱
  • 오류를 수동으로 처리
  • 모든 호출마다 이 과정 반복

JSON 파싱은 비용이 많이 듭니다. 텍스트 프로토콜은 장황합니다. HTTP/1.1은 요청마다 새로운 연결이 필요합니다. REST는 서비스가 무엇을 받고 반환하는지를 공식적으로 정의하지 않습니다—문서가 정확하기를 바랄 뿐이죠.

gRPC는 이 모든 것을 하나로 대체합니다: 그냥 함수를 호출하면 됩니다.

Protocol Buffers: 계약

.proto 파일에서 서비스를 정의합니다:

syntax = "proto3";

service UserService {
    rpc GetUser (GetUserRequest) returns (User);
    rpc ListUsers (ListUsersRequest) returns (stream User);
    rpc CreateUser (CreateUserRequest) returns (User);
}

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

message GetUserRequest {
    string id = 1;
}

이것은 계약입니다. 컴파일러가 이를 읽고 실제 코드를 생성합니다—이 메서드들을 호출하는 클라이언트 코드와 이를 구현하는 서버 인터페이스를. Go, Python, Java, C++ 등 수십 가지 언어로.

필드 번호(= 1, = 2)는 변경해서는 안 되는 안정적인 식별자입니다. 새 필드를 추가할 수는 있지만 기존 번호는 절대 바꾸지 마세요. gRPC가 기존 클라이언트를 깨뜨리지 않으면서 버전을 관리하는 방법이 바로 이것입니다.

바이너리 형식은 JSON보다 3~10배 작고 직렬화 속도도 훨씬 빠릅니다. 서비스는 파싱에 시간을 덜 쓰고 실제 작업에 더 많은 시간을 쏟을 수 있습니다.

HTTP/2: 하나의 연결, 수많은 호출

gRPC는 HTTP/1.1의 근본적인 비효율성을 해결한 HTTP/2를 기반으로 합니다.

HTTP/1.1에서는 각 요청이 별도의 연결을 차지하거나, 이전 요청이 끝날 때까지 기다려야 합니다. 다른 서비스를 100번 호출하는 서비스는 연결 100개가 필요하거나, 순차적으로 기다리는 수밖에 없습니다.

HTTP/2는 단일 연결 위에서 여러 요청을 동시에 처리하는 멀티플렉싱을 지원합니다. 두 서비스 사이의 TCP 연결 하나로 모든 통신을 처리합니다. 헤더는 압축되고, 바이너리 프레이밍은 Protocol Buffers의 바이너리 형식과 잘 맞습니다.

실질적인 효과: 더 낮은 지연 시간, 더 적은 리소스 사용, 더 높은 처리량.

네 가지 통신 방식

단항(Unary): 요청 하나, 응답 하나. 가장 익숙한 패턴입니다.

rpc GetUser (GetUserRequest) returns (User);

서버 스트리밍: 요청 하나, 응답 여러 개. 서버가 모든 결과를 모아서 보내는 대신, 준비되는 대로 하나씩 전송합니다.

rpc ListUsers (ListUsersRequest) returns (stream User);

클라이언트 스트리밍: 요청 여러 개, 응답 하나. 데이터를 청크 단위로 업로드하고 완료되면 요약 결과를 받습니다.

rpc CreateUsers (stream CreateUserRequest) returns (CreateUsersResponse);

양방향 스트리밍: 양쪽이 원할 때마다 데이터를 주고받습니다. 채팅 앱, 공동 편집, 실시간 동기화에 적합합니다.

rpc Chat (stream ChatMessage) returns (stream ChatMessage);

REST는 요청-응답 방식 하나만 제공합니다. gRPC는 데이터의 특성에 따라 메서드별로 선택할 수 있는 네 가지 패턴을 제공합니다.

전파되는 데드라인

특히 눈에 띄는 기능이 하나 있습니다: gRPC의 데드라인은 시스템 전체로 전파됩니다.

서비스 A가 2초 데드라인을 걸고 서비스 B를 호출합니다. 서비스 B는 서비스 C를 호출합니다. 서비스 C는 그 데드라인을 그대로 이어받고, 남은 시간이 얼마인지 확인할 수 있습니다. 데드라인이 지나면 모두가 작업을 멈춥니다.

원래 호출자가 이미 포기했는데도 응답을 계속 계산하는 낭비가 사라집니다. 타임아웃은 단순히 클라이언트만의 문제가 아닙니다—불필요한 작업을 막아주는 공유 컨텍스트입니다.

트레이드오프

curl로 직접 테스트할 수 없습니다. gRPC 트래픽은 바이너리입니다. 내용을 살펴보려면 전용 도구가 필요합니다. 디버깅 편의성 면에서는 REST가 앞섭니다.

브라우저에서 기본으로 지원하지 않습니다. gRPC-Web이 있지만 프록시가 필요합니다. 브라우저와 서버 간 통신에는 REST나 GraphQL이 더 나은 선택입니다.

배워야 할 것이 제법 있습니다. Protocol Buffers, 코드 생성, 스트리밍 개념까지—"URL로 JSON 보내기"보다 익혀야 할 것들이 훨씬 많습니다.

일부 네트워크 환경에서 문제가 생길 수 있습니다. HTTP 트래픽을 검사하는 기업용 프록시는 HTTP/2나 바이너리 프로토콜을 제대로 처리하지 못할 수 있습니다.

이것들이 gRPC를 피해야 할 이유는 아닙니다. gRPC가 진가를 발휘하는 곳—인프라 내부의 서비스 간 통신—에서 사용하라는 신호입니다.

보안

gRPC는 암호화된 전송을 위한 TLS, 메타데이터를 통한 토큰 기반 인증(HTTP 헤더와 유사한 방식), 클라이언트와 서버 모두 인증서를 검증하는 mTLS(mutual TLS)를 지원합니다. 인터셉터—gRPC의 미들웨어—는 서비스 로직을 건드리지 않고 인증, 로깅, 메트릭, 추적을 처리합니다.

gRPC를 언제 사용할까요

사용할 때: 서로 통신하는 마이크로서비스, 실시간 스트리밍, 서비스마다 다른 언어를 쓰는 환경, 성능이 중요하고 양쪽 모두를 직접 제어하는 모든 곳.

사용하지 않을 때: 브라우저를 대상으로 하는 API, 클라이언트를 제어할 수 없는 공개 API, 단순함이 성능보다 중요한 상황.

많은 시스템이 둘 다 활용합니다. 효율성이 우선인 내부 통신에는 gRPC, 호환성이 우선인 공개 API에는 REST.

핵심

gRPC가 성공하는 이유는 네트워크가 존재하지 않는 것처럼 느껴지게 만들기 때문입니다. 계약을 정의합니다. 컴파일러가 코드를 생성합니다. 함수를 호출합니다. 그 함수들은 마침 다른 머신에서 실행될 뿐입니다.

분산 시스템의 복잡성이 사라지는 건 아닙니다—장애, 타임아웃, 부분적 가용성은 여전히 처리해야 합니다. 하지만 네트워크 통신의 번거로운 작업은 사라집니다. 남은 것은 본질적인 작업뿐입니다: 서비스가 존재하는 이유, 그 로직을 구현하는 것.

gRPC 자주 묻는 질문

gRPC와 REST의 성능 차이는 얼마나 되나요?

실제 측정 결과, gRPC는 일반적으로 JSON REST API보다 5~10배 높은 처리량과 훨씬 낮은 지연 시간을 보입니다. 바이너리 직렬화(더 작은 페이로드, 더 빠른 파싱), HTTP/2 멀티플렉싱(연결 하나로 모든 것 처리), 헤더 압축이 이 차이를 만들어냅니다. 특히 고빈도 내부 호출에서 그 효과가 두드러집니다.

웹 브라우저에서 gRPC를 사용할 수 있나요?

직접적으로는 불가능합니다. 브라우저는 gRPC가 필요로 하는 HTTP/2 트레일러를 지원하지 않습니다. gRPC-Web이 프록시를 통해 gRPC-Web과 네이티브 gRPC 사이를 변환하는 방법을 제공하지만 한계가 있습니다—클라이언트 스트리밍과 양방향 스트리밍이 지원되지 않습니다. 브라우저 클라이언트에는 REST나 GraphQL이 대체로 더 나은 선택입니다.

Protocol Buffer 정의를 변경해야 한다면 어떻게 해야 하나요?

새 필드는 자유롭게 추가해도 됩니다—기존 클라이언트는 모르는 필드를 그냥 무시합니다. 필드 번호는 절대 변경하지 마세요. 영구적인 식별자입니다. 호환성을 깨뜨리는 방식으로 필드를 삭제하지 마세요. 대신 더 이상 사용되지 않음(deprecated)으로 표시하세요. 이 규칙만 지키면 기존 클라이언트를 깨뜨리지 않고 API를 계속 발전시킬 수 있습니다.

gRPC를 쓰려면 Protocol Buffers가 반드시 필요한가요?

Protocol Buffers가 기본이자 권장 직렬화 형식이지만, gRPC는 기술적으로 JSON 같은 다른 형식도 지원합니다. 다만 JSON을 쓰면 gRPC의 성능 이점 대부분을 포기하게 됩니다. gRPC + Protocol Buffers 조합에서 진정한 이점이 나타납니다.

이 페이지가 도움이 되었나요?

😔
🤨
😃
gRPC • 라이브러리 • Connected