• Feed
  • Explore
  • Ranking
/
/
    Go

    gRPC에 대하여

    gRPC 개요
    gRPCGoNetworkDevOps
    A
    Antonio
    2025.03.10
    ·
    12 min read

    3849

    1. gRPC란 무엇인가?

    gRPC(Google Remote Procedure Call)는 구글에서 개발한 오픈소스 RPC(Remote Procedure Call) 프레임워크로서, HTTP/2 프로토콜을 기반으로 높은 성능, 스트리밍, 양방향 통신 등을 지원합니다. Protobuf(Protocol Buffers)라는 IDL(Interface Definition Language)를 사용하여 직렬화/역직렬화 성능이 뛰어나고, 다양한 언어에서 클라이언트/서버를 작성할 수 있다는 장점이 있습니다.

    • 주요 특징

      • HTTP/2 기반: 멀티플렉싱 및 스트리밍을 지원하므로, 동시성에 유리하고 양방향 스트리밍 같은 고급 기능을 제공

      멀티플렉싱(multiplexing)이란, 하나의 TCP 연결에서 여러 개의 스트림(stream)을 동시에 전송할 수 있게 하는 기능을 말합니다. 전통적인 HTTP/1.1에서는 요청 하나당 하나의 연결(또는 시리얼하게 순차 요청) 방식을 주로 사용했으나, HTTP/2에서는 여러 요청 및 응답을 병렬로 처리 가능합니다.

      - 단일 TCP 연결: 하나의 TCP 연결 위에서 여러 스트림을 병렬로 처리하여, 연결 수를 제한하면서도 높은 처리량과 빠른 응답을 얻을 수 있습니다.

      - 헤더 압축: HTTP 헤더의 중복 정보를 압축하여 전송 효율을 높입니다.

      - 서버 푸시(Server Push): 서버가 클라이언트의 요청 없이도 추가 리소스를 푸시할 수 있습니다(웹 브라우저 환경에서 유리)

      - 스트리밍 및 양방향 통신: gRPC에서 클라이언트와 서버가 동시에 스트림을 주고받을 수 있도록 지원합니다.

    • Protocol Buffers 사용: 가볍고 빠르며 언어에 독립적인 데이터 직렬화 포맷

    • 여러 언어 간 상호운용성: Go, Java, C++, Python, Node.js, C#, Ruby 등 다양한 언어에서 공식 지원

    1.1 Protocol Buffers

    Protocol Buffers(프로토콜 버퍼)는 구글에서 개발한 언어 중립적, 플랫폼 중립적 데이터 직렬화/역직렬화 방식입니다. .proto라는 IDL(Interface Definition Language) 파일에 메시지 구조를 정의하고, 해당 파일을 컴파일하여 언어별 코드를 생성해 사용합니다.

    • 주요 특징

      • 가볍고 빠른 직렬화/역직렬화: JSON, XML 등 다른 포맷 대비 크기가 작고 처리 속도가 빠릅니다.

      • 언어/플랫폼 독립성: C++, Java, Go, Python 등 다양한 언어를 지원하므로, 서로 다른 환경에서 쉽게 데이터를 교환할 수 있습니다.

      • 버전 관리 용이: .proto 정의에 필드를 추가/삭제해도 과거 클라이언트와 어느 정도 호환되도록 설계되었습니다(규칙에 따라 필드 번호와 타입만 주의하면 하위 호환성 유지 가능).

    2. gRPC 구성

    2.1 .proto 파일

    syntax = "proto3"; // messaage type 정의
    
    package example;
    message Person {
        string name = 1;
        int32 age = 2;
    }
    
    • 코드 생성

      protoc --go_out=. --go_opt=paths=source_relative \\
          --go-grpc_out=. --go-grpc_opt=paths=source_relative \\
          sample/helloworld.proto
      
      • protoc: 프로토콜 버퍼(Protocol Buffers) 컴파일러

      • --go_out=.: .proto 파일을 Go 언어용 코드로 생성(*.pb.go 파일) 할 때, 출력 위치 지정

      • --go_opt=paths=source_relative: Go 코드 생성 시, 생성된 .pb.go 파일의 경로 구조를 어떻게 설정할지 지정하는 옵션

      • --go-grpc_out=.: gRPC 서비스 스텁(.pb.go 안에 gRPC 관련 인터페이스, 클라이언트/서버 구조체 등)을 Go 언어로 생성 할 때의 출력 위치

      • --go-grpc_opt=paths=source_relative: gRPC 코드 생성 시에도, 생성된 파일의 경로 구조를 어떻게 설정할지 지정하는 옵션

    2.2 _pb.go 파일

    Protobuf 메시지 정의 및 직렬화 관련 코드를 포함합니다.

    • .proto 파일에서 정의한 메시지(message) 구조체를 생성합니다.

    • Protobuf 메시지를 직렬화(Serialize) 및 역직렬화(Deserialize)가 가능하도록 합니다.

    • proto.Marshal() 및 proto.Unmarshal() 같은 함수를 통해 바이너리 데이터를 변환할 수 있도록 지원합니다.

      • .proto 파일

      syntax = "proto3";
      
      package example;
      
      message Point {
        int32 latitude = 1;
        int32 longitude = 2;
      }
      
      • point_pb.go 파일

        Protobuf 메시지 자체를 정의하며, gRPC 서비스 관련 코드는 포함되지 않습니다.

      package example
      
      import (
      	"google.golang.org/protobuf/proto"
      )
      
      type Point struct {
      	Latitude  int32 `protobuf:"varint,1,opt,name=latitude,proto3" json:"latitude,omitempty"`
      	Longitude int32 `protobuf:"varint,2,opt,name=longitude,proto3" json:"longitude,omitempty"`
      }
      
      func (m *Point) Reset()         { *m = Point{} }
      func (m *Point) String() string { return proto.CompactTextString(m) }
      func (*Point) ProtoMessage()    {}
      

    2.3 _grpc.pb.go 파일

    gRPC 서비스 관련 코드를 포함합니다.

    • .proto 파일에서 정의한 서비스(service) 인터페이스를 생성합니다.

    • gRPC 클라이언트 및 서버 인터페이스를 자동으로 생성하여 RPC 호출을 처리할 수 있도록 합니다.

      • .proto 파일

      syntax = "proto3";
      
      package example;
      
      service MyService {
        rpc GetPoint (Point) returns (Point);
      }
      
      message Point {
        int32 latitude = 1;
        int32 longitude = 2;
      }
      
      • point_grpc.pb.go 파일

      package example
      
      import (
      	"context"
      	"google.golang.org/grpc"
      )
      
      // MyServiceClient는 클라이언트 인터페이스
      type MyServiceClient interface {
      	GetPoint(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Point, error)
      }
      
      // MyServiceServer는 서버 인터페이스
      type MyServiceServer interface {
      	GetPoint(context.Context, *Point) (*Point, error)
      }
      
      // UnimplementedMyServiceServer는 기본 구현
      type UnimplementedMyServiceServer struct{}
      
      func (UnimplementedMyServiceServer) GetPoint(context.Context, *Point) (*Point, error) {
      	return nil, status.Errorf(codes.Unimplemented, "method GetPoint not implemented")
      }
      
      // MyServiceHandler 등록 함수
      func RegisterMyServiceServer(s *grpc.Server, srv MyServiceServer) {
      	s.RegisterService(&_MyService_serviceDesc, srv)
      }
      

    2.4 stub(스텁)

    Stub(스텁)은 클라이언트와 서버가 서로 통신할 때, RPC(Remote Procedure Call) 요청을 보낼 수 있도록 생성된 코드를 의미합니다.

    • 클라이언트 Stub: 클라이언트가 gRPC 서버에 요청을 보낼 때 사용하는 코드

    • 서버 Stub: gRPC 서버에서 클라이언트의 요청을 처리할 때 사용하는 코드

    gRPC에서는 .proto 파일을 작성하고 protoc을 실행하면, 자동으로 클라이언트와 서버 Stub이 생성됩니다.

    2.4.1 stub의 역할

    • 클라이언트에서 원격 gRPC 함수를 호출하는 역할 클라이언트는 Stub을 통해 마치 로컬 함수를 호출하는 것처럼 gRPC 서버의 메서드를 호출할 수 있습니다.

    • 서버에서 gRPC 요청을 처리하는 역할 서버는 Stub을 통해 클라이언트로부터 받은 요청을 처리하고 응답을 반환합니다.

    2.4.2 Unimplemented Structure(미구현 구조체)

    gRPC의 Go 코드 생성 시, 서비스 인터페이스와 함께 자동으로 만들어지는 Unimplemented<ServiceName>Server 구조체는 해당 서비스 인터페이스의 메서드를 “미구현(Stub)” 상태로 둔 기본 구현체입니다. 예를 들어 .proto 파일에 service Greeter를 정의했다면, helloworld_grpc.pb.go 안에 UnimplementedGreeterServer와 같은 구조체가 생성됩니다.

    • 사용 목적

      • 호환성(Forward Compatibility) 유지

        • .proto 파일에 정의된 서비스에 새로운 RPC 메서드를 추가하거나 기존 메서드가 변경되었을 때, 이미 배포된 서버 코드가 빌드 에러 없이 동작하도록 하기 위함입니다.

        • 만약 새 메서드가 추가되어도, UnimplementedGreeterServer 구조체가 기본적으로 “빈” 메서드를 제공하기 때문에, 기존 서버 구현이 “메서드가 없음” 오류를 일으키지 않도록 해 줍니다.

      • 인터페이스 준수 보조

        • 실제 서버 구현에서 UnimplementedGreeterServer를 임베디드(embedded) 필드로 넣어 두면, 서비스 인터페이스에 요구되는 모든 메서드를 자동으로 포함하게 됩니다.

          type server struct {
              helloworld.UnimplementedGreeterServer
              // 여기에 추가 필드를 넣어서 사용
          }
          

          server가 GreeterServer 인터페이스를 만족한다는 점을 Go 컴파일러가 인지하게 되어, 빌드 시점에 누락된 메서드가 있는지를 검증할 수 있습니다.

      • 사용자 정의 구현 덮어쓰기(Override)

        • UnimplementedGreeterServer가 제공하는 기본 메서드는 항상 에러만 반환하거나 아무 동작도 하지 않는 “빈” 구현입니다. 개발자는 필요한 메서드만 server 구조체에 직접 구현해서 사용하면 됩니다.

          func (s *server) SayHello(ctx context.Context, req *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
              return &helloworld.HelloReply{
                  Message: "Hello, " + req.GetName(),
              }, nil
          }
          

    Go Generated Code Guide (Opaque)
    Describes exactly what Go code the protocol buffer compiler generates for any given protocol definition.
    https://protobuf.dev/reference/go/go-generated-opaque/
    GitHub - grpc-ecosystem/grpc-gateway: gRPC to JSON proxy generator following the gRPC HTTP spec
    gRPC to JSON proxy generator following the gRPC HTTP spec - grpc-ecosystem/grpc-gateway
    https://github.com/grpc-ecosystem/grpc-gateway
    GitHub - protocolbuffers/protobuf-go: Go support for Google's protocol buffers
    Go support for Google's protocol buffers. Contribute to protocolbuffers/protobuf-go development by creating an account on GitHub.
    https://github.com/protocolbuffers/protobuf-go
    gRPC,gRPC
    A high-performance, open source universal RPC framework,A high-performance, open source universal RPC framework
    https://grpc.io/






    - 컬렉션 아티클