gRPC 프로토콜 버전 관리 전략
문제 상황
결제 서비스와 주문 서비스 간 gRPC 통신에서 Payment 메시지에 새 필드를 추가하는 작업을 진행했다. 문제는 두 서비스의 배포 타이밍이 달라 일시적으로 프로토콜 불일치가 발생할 수 있다는 점이었다.
하위 호환성 유지 원칙
1. 필드 번호 재사용 금지
message Payment {
string id = 1;
int64 amount = 2;
// string old_field = 3; // 삭제된 필드
reserved 3; // 번호 예약 필수
string payment_method = 4;
}
삭제된 필드 번호는 reserved로 명시해야 한다. 재사용 시 역직렬화 오류가 발생할 수 있다.
2. 필드 추가는 항상 안전
message Payment {
string id = 1;
int64 amount = 2;
string payment_method = 3;
string receipt_url = 4; // 새 필드 추가
}
새 필드를 추가하는 것은 안전하다. 구버전 클라이언트는 해당 필드를 무시하고, 신버전은 기본값으로 처리한다.
3. 필드 타입 변경 제한
타입 변경은 매우 제한적이다. int32와 int64, sint32와 sint64 간 변경은 가능하지만, string에서 int64로의 변경은 불가능하다.
배포 전략
- Consumer First: 메시지를 받는 쪽(Consumer)을 먼저 배포
- Producer Second: 메시지를 보내는 쪽(Producer)을 나중에 배포
- Monitoring: 프로토콜 버전 불일치 메트릭 수집
// 버전 정보를 헤더에 포함
md := metadata.Pairs(
"proto-version", "v2.1.0",
)
ctx := metadata.NewOutgoingContext(context.Background(), md)
Deprecation 처리
필드를 제거하려면 최소 2번의 배포 사이클이 필요하다.
message Payment {
string id = 1;
int64 amount = 2;
string old_method = 3 [deprecated = true];
string payment_method = 4;
}
첫 배포에서 deprecated 마킹 후, 모든 서비스가 신규 필드로 전환했음을 확인한 뒤 제거한다.
결론
gRPC의 하위 호환성은 신경 쓸 부분이 많지만, 원칙을 지키면 무중단 배포가 가능하다. 프로토콜 변경 시 리뷰에서 필드 번호 재사용 여부를 반드시 체크하도록 팀 가이드라인에 추가했다.