grpc

gRPC 아키텍처와 기본 동작

gRPC는 Google에서 개발한 고성능 원격 프로시저 호출(RPC) 프레임워크로, 다양한 프로그래밍 언어 간에 효율적인 통신을 지원합니다. gRPC의 아키텍처는 서비스, 클라이언트, 서버, 그리고 Protocol Buffers를 기반으로 하여, 명확하게 정의된 인터페이스와 자동 코드 생성을 통해 높은 성능과 타입 안전성을 보장합니다. 이 글에서는 gRPC의 아키텍처와 기본 동작 방식, .proto 파일의 정의 및 구조, 그리고 gRPC 서비스 정의와 클라이언트/서버 구현에 대해 자세히 살펴보겠습니다.

1. gRPC의 기본 아키텍처

gRPC는 클라이언트와 서버가 미리 정의된 인터페이스를 공유하여 원격 프로시저 호출(RPC)을 수행하는 프레임워크입니다. 이 인터페이스는 .proto 파일에 작성되며, 서비스와 메시지의 구조를 명확하게 규정합니다. gRPC 아키텍처는 크게 네 가지 주요 요소로 구성되며, 각각이 전체 통신 구조에서 중요한 역할을 합니다.

서비스 (Service)

서비스는 gRPC의 기능적 계약서로, 클라이언트가 호출할 수 있는 함수(메서드)들의 집합입니다. 예를 들어, 사용자 관리 서비스라면, 사용자 생성, 조회, 수정, 삭제 등의 RPC 메서드가 포함될 수 있습니다. 이 서비스 정의는 .proto 파일 내에 작성되어, 클라이언트와 서버 모두가 동일한 메서드 시그니처와 데이터 구조를 사용하게 되어 일관성을 보장합니다.

클라이언트 (Client)

클라이언트는 서버의 원격 메서드를 호출하는 주체입니다. gRPC는 .proto 파일을 바탕으로 각 언어에 맞는 클라이언트 스텁을 자동 생성해줍니다. 이 스텁은 로컬 함수 호출처럼 보이지만, 내부적으로는 요청 데이터를 Protocol Buffers 형식으로 직렬화하여 네트워크를 통해 전송합니다. 이를 통해 복잡한 네트워크 통신 로직 없이도 원격 호출을 쉽게 구현할 수 있습니다.

서버 (Server)

서버는 클라이언트의 요청을 받아 실제 비즈니스 로직을 처리하고, 결과를 반환하는 역할을 수행합니다. 서버 구현 시, .proto 파일에 정의된 서비스 인터페이스에 맞추어 RPC 메서드를 구현하게 됩니다. 클라이언트로부터 받은 요청은 서버에서 역직렬화되어 처리되고, 처리 결과는 다시 Protocol Buffers를 사용해 직렬화된 후 클라이언트에 전달됩니다.

프로토콜 버퍼 (Protocol Buffers)

프로토콜 버퍼는 gRPC에서 데이터를 직렬화하는 데 사용하는 이진 포맷 방식입니다. .proto 파일에 정의된 메시지 구조를 바탕으로 데이터를 인코딩하고 디코딩하므로, JSON과 같은 텍스트 기반 포맷에 비해 전송 데이터 크기가 작고 처리 속도가 빠릅니다. 이 방식은 네트워크 대역폭 사용을 줄이고, 컴파일 타임에 타입 검사를 가능하게 하여 클라이언트와 서버 간의 데이터 불일치 문제를 미연에 방지합니다.

이와 같이 gRPC의 아키텍처는 .proto 파일을 통해 정의된 명확한 인터페이스를 바탕으로, 서비스, 클라이언트, 서버, 프로토콜 버퍼가 유기적으로 결합되어 작동합니다. 이러한 구조는 높은 성능과 타입 안전성을 보장하며, 복잡한 분산 시스템에서도 안정적이고 효율적인 원격 호출을 가능하게 합니다.


2. .proto 파일 정의 및 구조

gRPC에서의 모든 서비스와 메시지 정의는 .proto 파일에 기록됩니다. 이 파일은 다음과 같은 요소들로 구성됩니다.

  • 메시지(Message): 서비스가 주고받을 데이터의 구조를 정의합니다. 각 메시지는 여러 필드를 포함하며, 필드에는 번호와 타입이 할당되어 데이터 직렬화 시 효율성을 극대화합니다.
  • 서비스(Service): 클라이언트가 호출할 수 있는 원격 메서드들을 정의합니다. 각 메서드는 요청 메시지와 응답 메시지 타입을 명시하여, 클라이언트와 서버 간의 명확한 데이터 계약을 형성합니다.

예를 들어, 간단한 계산 서비스를 정의하는 .proto 파일은 다음과 같이 작성될 수 있습니다:

grpc proto
syntax = "proto3";
 
package calculator;
 
// 메시지 정의
message CalculationRequest {
  int32 a = 1;
  int32 b = 2;
}
 
message CalculationResponse {
  int32 result = 1;
}
 
// 서비스 정의
service CalculatorService {
  rpc Add(CalculationRequest) returns (CalculationResponse);
}
  • proto3이란 Protocol Buffers의 세 번째 버전으로, gRPC와 Protocol Buffers를 사용하여 데이터를 직렬화하고, 서비스와 메시지를 정의하는 데 사용됩니다.
  • calculator는 이 프로토콜 파일에 정의된 서비스와 메시지가 속하는 패키지 이름입니다. 패키지는 이름 충돌을 방지하고, 파일을 잘 조직하는 데 유용합니다. 예를 들어, calculator라는 패키지 내에서 여러 개의 서비스나 메시지를 정의할 수 있습니다.
  • CalculationRequest 메시지는 클라이언트가 계산을 요청할 때 필요한 입력값을 정의합니다. int32는 32비트 정수를 나타내며, 두 필드는 각각 a와 b라는 이름을 가진 정수 값으로 요청이 들어옵니다.
  • CalculationResponse 메시지는 계산이 완료된 후 결과를 반환하기 위한 응답 메시지입니다.
  • CalculatorService는 gRPC 서비스를 정의하는 부분입니다. 이 서비스는 서버 측에서 제공하는 기능을 클라이언트가 호출할 수 있도록 정의됩니다.
  • rpc Add 메서드는 클라이언트가 호출할 수 있는 원격 프로시저 호출(RPC)입니다.(add라는 메서드는 클라이언트에서 정의됨.) 이 메서드는 CalculationRequest 타입의 메시지를 입력받고, CalculationResponse 타입의 메시지를 반환합니다.

이 파일은 CalculatorService라는 서비스를 정의하고, Add라는 메서드를 통해 두 숫자의 합을 계산하는 요청과 응답 메시지를 명시합니다.


3. gRPC의 기본 동작 방식: 요청-응답 및 스트리밍

gRPC는 네트워크 통신의 새로운 패러다임을 제시합니다. 기본적으로, gRPC는 단순한 요청-응답 모델을 채택하여, 클라이언트가 자동 생성된 스텁( stub - 클라이언트와 서버 간의 통신을 간편하게 하기 위해 자동으로 생성되는 코드를 의미 )을 통해 마치 로컬 함수를 호출하듯이 서버의 메서드를 호출하면, 서버는 해당 요청을 처리한 후 결과를 반환합니다.

이 과정은 내부적으로 Protocol Buffers를 사용하여 데이터를 이진 포맷으로 직렬화 및 역직렬화하고, HTTP/2 프로토콜 위에서 전송되기 때문에 낮은 지연 시간과 높은 처리량을 보장합니다. 또한, gRPC는 단순 요청-응답을 넘어 다양한 스트리밍 통신 방식을 지원합니다.

  • 서버 스트리밍: 클라이언트가 단일 요청을 보내면, 서버는 해당 요청에 대해 여러 메시지를 순차적으로 스트리밍 방식으로 전송합니다. 예를 들어, 서버가 실시간 로그나 센서 데이터를 지속적으로 클라이언트에 전달할 때, 이 방식은 한 번의 요청으로 연속적인 데이터 흐름을 생성할 수 있게 해줍니다.
  • 클라이언트 스트리밍: 클라이언트가 여러 메시지를 연속적으로 서버에 전송하고, 서버는 이 모든 데이터를 집계하여 하나의 응답으로 반환합니다. 이 방식은 대량의 데이터를 한 번에 보내야 하는 상황에서 네트워크 연결의 효율성을 극대화하는 데 유리합니다.
  • 양방향 스트리밍: 클라이언트와 서버가 동시에 독립적인 스트림을 통해 데이터를 주고받을 수 있습니다. 이 패턴은 채팅, 실시간 협업 툴, 온라인 게임 등과 같이 양측 모두 실시간 데이터 교환이 필요한 애플리케이션에 최적화되어 있습니다.

이와 같이 gRPC의 스트리밍 기능은 HTTP/2의 멀티플렉싱과 헤더 압축 같은 최신 네트워킹 기능과 결합되어, 대용량 데이터 전송과 실시간 통신 요구를 효율적으로 처리할 수 있습니다. 결과적으로, gRPC는 명확하게 정의된 인터페이스와 강력한 타입 안전성을 바탕으로, 복잡한 분산 시스템에서도 안정적이고 고성능의 통신을 구현하는 데 큰 역할을 합니다.


4. gRPC 서비스 정의와 클라이언트/서버 구현

gRPC 서비스를 구현하기 위해 먼저 .proto 파일을 통해 서비스 인터페이스를 정의합니다. 이후, 각 언어별 gRPC 도구를 사용하여 클라이언트 스텁과 서버 스텁 코드를 자동으로 생성합니다.

  • 서버 측 구현: 개발자는 자동 생성된 서버 스텁을 확장하여, 실제 비즈니스 로직을 구현합니다. 서버는 클라이언트의 RPC 호출을 받아, 요청을 처리한 후 응답 메시지를 반환합니다.
  • 클라이언트 측 구현: 클라이언트는 생성된 클라이언트 스텁을 사용해, 원격 서버의 메서드를 마치 로컬 함수처럼 호출합니다. 이 호출은 내부적으로 데이터가 Protocol Buffers 형식으로 직렬화되어 전송되고, 응답을 받아 역직렬화한 후 반환됩니다.

이 과정에서 HTTP/2의 멀티플렉싱 및 스트리밍 기능이 활용되어, 여러 RPC 호출이 동시에 이루어지더라도 효율적인 통신이 보장됩니다.

결론

gRPC는 서비스, 클라이언트, 서버 및 Protocol Buffers를 중심으로 명확하게 정의된 아키텍처와, 요청-응답 및 스트리밍을 포함한 다양한 통신 방식을 통해 고성능 분산 시스템을 지원합니다. .proto 파일을 통해 서비스 인터페이스를 명시하고, 자동으로 생성되는 클라이언트와 서버 스텁 덕분에 개발자는 안정적이고 확장 가능한 애플리케이션을 구축할 수 있습니다.