Protocol Buffers 와이어 포맷의 구조
gRPC 통신은 Protocol Buffers 바이너리 포맷을 기반으로 하며, 메시지는 일련의 Tag-Length-Value(TLV) 구조 레코드로 인코딩됩니다. 각 레코드는 필드 번호와 와이어 타입을 조합한 "태그"로 시작하며, 태그는 (필드번호 << 3) | 와이어타입 수식으로 계산됩니다.
와이어 타입은 총 6가지로, 0(Varint), 1(64비트), 2(길이 구분), 3/4(그룹/폐기됨), 5(32비트)가 있습니다. 예를 들어 필드 1번에 int32를 저장하면 태그는 08(hex)이 되고, 실제 값은 Varint로 인코딩됩니다.
Base 128 Varint 인코딩
Protobuf의 효율성은 Varint 기반 인코딩에서 비롯됩니다. 각 바이트의 최상위 비트는 연속 비트로 기능하며, 나머지 7비트가 페이로드입니다. 작은 값(0-127)은 1바이트로, 더 큰 값도 필요한 만큼만 바이트를 소비하므로 대역폭을 절감합니다.
음수는 타입에 따라 다르게 처리되는데, int32/int64는 2의 보수로 인코딩되어 음수일 경우 10바이트를 사용하고, sint32/sint64는 ZigZag 인코딩을 사용하여 음수를 효율적으로 표현합니다. ZigZag는 양수 p를 2×p로, 음수 n을 2×|n|-1로 매핑합니다.
실무 최적화 전략
필드 번호 1-15는 1바이트 태그로 인코딩되고 16-2047은 2바이트이므로, 자주 전송되는 필드에 1-15 범위의 번호를 할당해야 합니다. 또한 반복되는 원시 수치 타입 필드는 기본적으로 "packed" 포맷으로 압축 인코딩되어 태그 오버헤드를 절감합니다. 필드를 제거할 때는 반드시 reserved를 선언하여 역호환성을 보장해야 합니다.