gRPC란?

gRPC는 Google에서 개발한 고성능 원격 프로시저 호출(Remote Procedure Call, RPC) 프레임워크입니다. HTTP/2를 기반으로 동작하며, 다양한 언어를 지원하고, 효율적인 바이너리 직렬화(Protocol Buffers, Protobuf)를 사용하여 빠르고 안정적인 서비스 간 통신을 제공합니다. 기존의 REST API와 비교했을 때 더 적은 오버헤드와 높은 성능을 제공하며, 기존 RPC 시스템(예: Thrift, CORBA)보다 표준화된 인터페이스와 광범위한 언어 지원을 통해 활용성이 높습니다. gRPC는 마이크로서비스 아키텍처, 실시간 스트리밍 서비스, IoT 장치 간 통신 등에서 자주 사용됩니다.


🛠️ gRPC의 특징

✅ 1. HTTP/2 기반

  • 멀티플렉싱 지원(하나의 연결에서 여러 요청 처리)
  • 헤더 압축으로 네트워크 비용 절감
  • 서버 푸시 기능 지원

✅ 2. Protocol Buffers (Protobuf) 사용

  • JSON보다 빠르고 용량이 작음
  • 정형화된 스키마로 안정적인 데이터 구조 유지

✅ 3. 다양한 언어 지원

  • Go, Python, Java, C++, C#, JavaScript 등 여러 언어에서 사용 가능

✅ 4. 4가지 통신 방식 지원

  1. Unary RPC: 클라이언트가 요청을 보내고, 서버가 응답을 반환하는 가장 기본적인 방식입니다.

    • ✅ 장점: 간단하고 직관적이며, REST API와 유사하여 적용이 쉬움
    • ❌ 단점: 요청-응답 패턴이므로 실시간 데이터 스트리밍에는 적합하지 않음
    • ✏️ 예제:
      response = stub.SayHello(greet_pb2.HelloRequest(name="Alice"))
  2. Server Streaming RPC: 클라이언트가 하나의 요청을 보내면 서버가 여러 개의 응답을 스트리밍으로 보냅니다.

    • ✅ 장점: 대량의 데이터를 순차적으로 처리 가능 (예: 실시간 로그 스트리밍)
    • ❌ 단점: 클라이언트는 응답을 모두 받을 때까지 추가 요청을 보낼 수 없음
    • ✏️ 예제:
      responses = stub.GetLogs(greet_pb2.LogRequest())
      for response in responses:
          print(response.message)
  3. Client Streaming RPC: 클라이언트가 여러 개의 요청을 스트리밍 방식으로 보내고, 서버는 최종적으로 한 번 응답을 반환합니다.

    • ✅ 장점: 대량의 요청을 하나의 응답으로 처리 가능 (예: 데이터 업로드)
    • ❌ 단점: 서버가 클라이언트의 모든 데이터를 받을 때까지 응답하지 않음
    • ✏️ 예제:
      stream = stub.UploadData()
      for data in data_list:
          stream.send(data)
      response = stream.close_and_receive()
  4. Bidirectional Streaming RPC: 클라이언트와 서버가 동시에 여러 개의 메시지를 스트리밍 방식으로 주고받을 수 있습니다.

    • ✅ 장점: 양방향 실시간 통신이 가능하여 채팅, 금융 데이터 스트리밍 등에 유용
    • ❌ 단점: 구현이 복잡하며 상태 관리가 필요함
    • ✏️ 예제:
      responses = stub.Chat(stream_of_messages())
      for response in responses:
          print(response.message)

🏷️ gRPC 서버 및 클라이언트 예제 (Python)

gRPC를 사용한 간단한 서버와 클라이언트를 만들어보겠습니다.

📌 1. Protocol Buffers 정의 (greet.proto)

syntax = "proto3";

package greet;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string message = 1;
}

이 파일은 gRPC 서비스의 인터페이스를 정의하는 역할을 합니다.


📌 2. Protobuf 컴파일

.proto 파일을 컴파일하여 Python 코드를 생성해야 합니다.

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. greet.proto

📌 3. gRPC 서버 구현 (server.py)

import grpc
from concurrent import futures
import greet_pb2
import greet_pb2_grpc

class GreeterServicer(greet_pb2_grpc.GreeterServicer):
    def SayHello(self, request, context):
        # 요청 로그 추가
        print(f"Received request from: {request.name}")

        # 인증 로직 (예제)
        if request.name == "UnauthorizedUser":
            context.set_code(grpc.StatusCode.UNAUTHENTICATED)
            context.set_details("Unauthorized access")
            return greet_pb2.HelloResponse()

        # 응답 반환
        return greet_pb2.HelloResponse(message=f"Hello, {request.name}!")

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    greet_pb2_grpc.add_GreeterServicer_to_server(GreeterServicer(), server)
    server.add_insecure_port("[::]:50051")
    server.start()
    print("gRPC Server is running on port 50051...")
    server.wait_for_termination()

if __name__ == "__main__":
    serve()

gRPC 서버 실행: python server.py


📌 4. gRPC 클라이언트 구현 (client.py)

import grpc
import greet_pb2
import greet_pb2_grpc

def run():
    with grpc.insecure_channel("localhost:50051") as channel:
        stub = greet_pb2_grpc.GreeterStub(channel)
        response = stub.SayHello(greet_pb2.HelloRequest(name="Alice"))
        print("Server Response:", response.message)

if __name__ == "__main__":
    run()

gRPC 클라이언트 실행: python client.py


🚀 마무리

이제 gRPC를 사용하여 클라이언트와 서버가 효율적으로 데이터를 주고받을 수 있습니다. gRPC는 마이크로서비스 아키텍처에서 강력한 성능과 확장성을 제공하므로, 효율적인 통신이 필요한 프로젝트에서 적극 활용할 수 있습니다.

다음엔 어떤 gRPC 기능을 다뤄볼까? 😃

+ Recent posts