요약

  • RFC 9996은application/protobuf와application/protobuf+json을 등록한다. 선택적version매개변수는 전송용 인코딩을 위해 예약된 것이며, Proto2, Proto3 또는 Protobuf 에디션을 위한 것이 아님을 명시한다.
  • 전송 호환 바이트도 서로 다른 스키마 정의 아래에서 서로 다른 의미를 가질 수 있다. 미디어 타입은 스키마 식별자, 다이제스트, 릴리스 권한, 메시지 타입, 무결성 결과, 디코더 정책 중 어느 것도 담고 있지 않다.
  • Daniel Kade는 전송 분류, 스키마 권한, 호환성 검토, 디코더 동작, 최종 신뢰 결정을 분리되고 검사 가능한 필드에 유지하는 유한한 스키마 컨텍스트 영수증을 제안한다.

헤더가 맞아도 해석은 틀릴 수 있다

내부 게이트웨이가application/protobuf로 표시된 바이너리 본문을 수신했다고 상상해 보자. 서버는 타입을 수용하고 Protobuf 파서를 선택해 오류 없이 객체를 생성한다. 필드 1은 계정 식별자, 필드 2는 권한 상태, 필드 7은 목적지가 된다. 다운스트림 자동화가 동작한다.

이제 숨겨진 전제 하나만 바꿔 보자. 발신자와 수신자가 그 필드 번호에 대해 서로 다른 정의를 로드했다. 바이트는 여전히 Protobuf 전송 문법에 부합한다. 미디어 타입도 여전히 정확하다. 파싱조차 여전히 성공할 수 있다. 그러나 수신자가 동작의 기준으로 삼는 객체는 발신자가 전달하려던 객체가 아니다.

이것은 미디어 타이핑의 결함이 아니다. 전송 라벨에 원래 답하도록 설계되지 않은 질문을 던진 데서 오는 결과다. 2026년 7월에 IETF 합의의 Informational 산출물로 공개된RFC 9996은 더 좁고 가치 있는 작업을 수행한다. 직렬화된 Protocol Buffer 객체를 전송하기 위한 두 개의 이름을 등록한다. 바이너리 표현용으로는application/protobuf, ProtoJSON용으로는application/protobuf+json이다.

이 문서는 경계에 대해 이례적으로 명확하다. 미디어 타입은 직렬화된 객체에만 적용된다. 인터페이스 정의 언어와 객체 정의는, 전송된다면, 적절한 텍스트 미디어 타입 아래에 속한다. 이 분리 덕분에 HTTP 또는 MIME 봉투는 표현을 식별할 수 있으면서 스키마는 다른 곳에 남는다.

따라서 봉투와 규칙집은 서로 다른 증거 평면을 차지한다. 첫째는 수신자가 처리 계열을 선택하는 데 도움을 준다. 둘째는 태그 2가 권한인지, 수량인지, 중첩 메시지인지, 부재가 의미를 가지는지, 어떤 열거 값이 존재하는지, 어떤 변경이 안전한지를 결정한다. 규격에 맞는 라벨이 빠진 규칙집을 대신할 수는 없다.

version매개변수는 전송을 이름 짓지 스키마를 이름 짓지 않는다

RFC 9996은 과잉 해석을 유도하는 두 개의 선택적 매개변수를 제공한다. 첫째는encoding이다. 바이너리는application/protobuf의 기본값이고 JSON은application/protobuf+json의 기본값이다. 모순된 조합은 거부해야 한다. JSON 직렬화는+json서브타입과 필수적인charset=utf-8매개변수를 사용해야 한다.

이 규칙들은 표현에 대한 모호함을 제거한다. 그러나 메시지 정의를 식별하지는 않는다.

둘째 매개변수는version이다. 두 등록 모두 기본값을1로 두고, 클라이언트는 지원하지 않는 전송 버전을 거부해야 한다. 그러나 RFC는 이 숫자가 전송 인코딩 명세에 관한 것이며 스키마 언어에 관한 것이 아니라고 직접 말한다. 현재의 Protobuf 전송 인코딩은 버전화되어 있지 않고, 이 매개변수는 미래 확장성을 위해 존재한다.

이 구별이 중요한 이유는 Protobuf에 스키마 언어 세대가 여럿 있기 때문이다. Proto2, Proto3, 에디션 2023, 에디션 2024. RFC 9996은 이들이 현재 전송 형식의 별개 버전이 아니라 IDL의 진화라고 설명한다. 한 세대에서 생산된 직렬화 객체가 다른 세대와 구조적으로 호환될 수 있다. 동시에 RFC는 IDL 버전의 의미 변화가 그렇지 않으면 호환되는 비트의 해석을 바꿀 수 있다고 경고한다. 그 예는 열거형 처리다. 한 세대에서 무효로 취급된 값이 다른 세대에서는 미지 값으로 보존될 수 있다.

따라서version=1을 "스키마 버전 1", "Proto3", "에디션 2024", 또는 "이 조직이 현재 승인한 스키마"로 정직하게 읽을 수는 없다. 그중 어느 것도 말하지 않는다. Content-Type 헤더에 추가한다고 해서 새로운 스키마 증거가 생기지도 않는다.

성공한 파싱은 성공한 정렬보다 적은 것을 증명한다

Proto3 언어 가이드는 이 위험을 운영 관점에서 기술한다. Protobuf의 간결한 전송 형식은 필드가 한 정의로 인코딩되고 다른 정의로 디코딩되었을 때 이를 감지하지 못한다. 필드 번호를 재사용하면 파스 또는 병합 오류로 이어질 수 있지만, 민감한 정보가 새어 나가거나 데이터가 손상될 수도 있다. 실패가 반드시 요란하지는 않다.

호환성 기능은 실재한다. 바이너리 메시지의 미지 필드는 흔한 메시지 지향 연산에 의해 보존될 수 있다. 필드 번호와 타입 규칙은 신중한 진화를 허용한다. 삭제된 번호와 이름을 예약하면 우발적 재사용이 줄어든다. 이 통제 덕분에 분산 시스템이 모든 생산자와 소비자가 같은 순간에 업그레이드하도록 요구받지 않고도 변화할 수 있다.

그러나 호환성은 동일성이 아니다. 호환성은 스키마 세대를 넘어 처리 가능할 수 있는 변경 집합을 기술한다. 발신자가 어떤 스키마를 사용했는지, 수신자가 어느 리비전을 기대했는지, 변경이 조직 검토를 통과했는지, 새로 해석된 객체 아래에서 현재 연산이 승인되었는지는 증명하지 않는다.

이 간극은 JSON에서 더 선명하다.ProtoJSON 명세는 JSON이 바이너리 표현보다 약한 스키마 진화 보장을 가진다고 말한다. 미지 필드를 지원하지 않으며 필드와 열거 이름을 인코딩된 메시지 안에 넣는다. 바이너리 메시지를 JSON으로 변환하면 미지 필드가 버려질 수 있다. 숫자로 안전하게 진화하던 이름이 전송상으로 드러나는 약속이 될 수 있다.

따라서application/protobuf+json은 단순히 거버넌스 증거를 모두 온전히 간직한 바이너리 객체의 읽기 쉬운 뷰가 아니다. 그것은 고유한 손실과 호환성 표면을 지닌 별개의 표현이다. 최종 미디어 타입만 기록하는 영수증은 변환 중에 미지 자료가 사라졌는지, 변환 경계에서 어느 스키마가 사용 가능했는지, 이후 파서가 인식하지 못한 필드를 무시했는지 보여줄 수 없다.

IANA 등록은 공통 토큰이지 페이로드 증명서가 아니다

IANA 미디어 타입 레지스트리는 내구성 있는 공공 조정을 제공한다. 구현자는 더 이상 Protobuf 트래픽을 위해x-이름을 즉흥적으로 만들 필요가 없다. 등록은 서브타입, 매개변수, 명세, 의도된 용도, 변경 관리자를 기록한다. RFC 9996은application/x-protobuf,application/x-protobuffer,application/x-protobuf+json을 별칭으로서 폐기 예정으로 분류한다.

이것은 중요한 정리 작업이다. 안정된 토큰은 콘텐츠 협상, 문서화, 필터링, 브라우저 처리를 개선한다.+json접미사는 일반 프로세서가 JSON 기반 표현을 인식하게 한다. 바이너리와 JSON 타입의 구별은 하나의 토큰이 두 개의 호환되지 않는 인코딩을 감추는 일을 막는다.

그러나 바이너리 등록에는 매직 넘버도 파일 확장자도 없다. 어느 등록도 스키마 URI, 스키마 다이제스트, 패키지 이름, 메시지 이름, 생산자 정체성, 서명, 승인 상태를 요구하지 않는다. 변경 관리자는 IETF다. 그렇다고 해서 IETF가 그 타입을 달고 있는 모든 페이로드의 발급자가 되는 것은 아니다.

등록은 "이 표현을 위해 생태계가 어떤 토큰을 쓸 수 있는가?"에 답한다. "이 바이트는 누가 만들었는가?", "어떤 스키마가 그것에 의미를 부여하는가?", "그 스키마는 누가 승인했는가?", "이 메시지는 변경되었는가?", "이 수신자가 그 위에 동작해도 되는가?"에는 답하지 않는다. 레지스트리 항목을 이런 주장으로 끌어올리는 것은 IANA의 기관적 권위를 빌려 IANA가 검토한 적 없는 사적 애플리케이션 결정에 부착하는 일이다.

콘텐츠 안전도 또 하나의 별개 경계다

RFC 9996은 또한 Protobuf를 보안 래퍼로 만드는 것을 거부한다. 이 형식은 보안, 프라이버시, 무결성, 압축 서비스를 제공하지 않는다. TLS 같은 전송 보호는 일부 채널 리스크를 다룰 수 있지만, 여전히 채널 안에 표현된 비즈니스 연산의 스키마를 이름 짓거나 승인하지 않는다.

RFC는 변형된 입력이 대량의 메모리를 소비할 수 있다고 경고한다. 애플리케이션은 내장된 문자열과 바이트를 기대하는 형식으로 검증해야 하며, Protobuf 문자열 필드의 UTF-8 검증은 일률적으로 강제되지 않는다. 웹 배포는 바이너리 콘텐츠 스니핑을 막아야 하고, ProtoJSON은 브라우저가 순수 JSON에 적합한 보호를 적용할 수 있도록+json접미사와 UTF-8 선언이 필요하다.

각 조치에는 유한한 임무가 있다. 자원 한도는 파서 비용을 억제한다.nosniff방식의 처리는 브라우저의 재해석을 제한한다. TLS는 선택된 인증 모델 아래의 연결을 보호한다. 서명은 바이트를 키에 결합할 수 있다. 그중 어느 것 하나만으로는 필드 7이 이 서비스에 승인된 스키마 릴리스에 의해 정의되었다는 사실이 확립되지 않는다.

Protobuf의Any타입조차 만능 탈출구를 제공하지 않는다. 그것은 스키마를 식별하고 원리상으로는 찾아가기 위한 타입 URL을 운반한다. RFC 9996은 이 링크의 역참조가 널리 쓰이는 구현에서 지원되지 않음을 언급한다. 타입 URL은 유용한 애플리케이션 메타데이터가 될 수 있지만, 보편적으로 강제되는 스키마 발견 및 신뢰 체계로 묘사되어서는 안 된다.

빠져 있는 결정은 파서 바깥에 있다

두 팀이 Protobuf를 교환할 때 어떤 정의가 권위적인지 누군가는 결정해야 한다. 그 결정은 소스 저장소, 패키지 레지스트리, 빌드 규칙, API 계약, 배포 매니페스트 또는 사적 스키마 서비스에 있을 수 있다. 호환성 검토, 소유권 승인, 릴리스 서명, 긴급 롤백 경로를 포함할 수도 있다.

RFC 9996은 그런 제도적 배치 중에서 선택하지 않으며, 선택해서도 안 된다. 일반 미디어 타입 등록은 모든 애플리케이션의 거버넌스를 알 수 없다. 문제는 지역 배치가 암묵적일 때 시작된다. 그러면 네트워크 구성이 선택한 디코더가 사실상의 권위자가 된다. 서비스에 우연히 번들된 스키마가 수신되는 숫자들의 의미를 조용히 결정한다.

이것은 편의로 위장된 대리 의사결정 문제다. 잘못된 해석의 결과는 애플리케이션 소유자가 부담하지만, 빌드 시스템, 라이브러리 유지자 또는 게이트웨이 운영자가 사실상 스키마를 골랐을 수 있다. 기록된 조인이 없으면 검토자는 그 선택이 승인된 것인지 아니면 단지 사용 가능했던 것인지 알 수 없다.

Lu Heng이 관찰 가능한 사실과 제도적 주장을 분리하라고 강조한 점은 여기서 유용하다. 미디어 타입, 성공한 파싱, 검증된 연결, 스키마 승인은 네 가지 서로 다른 관찰이다. 책임 있는 인프라는 그 근접성을 동등함으로 바꿔 놓아서는 안 된다.

스키마 컨텍스트 영수증을 공개하라

답은 사적.proto파일을 공개 헤더에 넣거나 하나의 전역 스키마 권위를 만드는 것이 아니다. 많은 배포가 사적 정의를 필요로 하고, 미디어 타입 매개변수는 팽창하는 애플리케이션 계약을 위한 좋은 장소가 아니다. 빠진 통제는 신뢰 경계에 보관되는 작은 영수증이 될 수 있다.

첫째, 수신된 미디어 타입과 모든 매개변수를 평가된 그대로 기록한다. 객체가 바이너리로 들어왔는지 ProtoJSON으로 들어왔는지, 지원되지 않거나 모순된 값이 거부되었는지 보여준다.

둘째, 애플리케이션이 사용한 메시지 타입 선택자를 기록한다. 엔드포인트 계약, RPC 메서드, 큐 토픽,Any타입 URL 또는 다른 지역 판별자일 수 있다. 그 필드는 미디어 타입이 선택을 공급했다는 인상 대신 어떤 메커니즘이 선택을 공급했는지 말해야 한다.

셋째, 권위적인 스키마 패키지, 언어 세대 또는 에디션, 리비전, 불변 다이제스트를 결합한다. 사람이 읽기 쉬운 버전 이름은 유용하지만 다이제스트가 검토된 바이트를 식별한다. 스키마가 기밀이라면 영수증은 정의를 공개하지 않으면서 다이제스트와 통제된 참조를 보관할 수 있다.

넷째, 출처와 릴리스 권한을 식별한다. 저장소 또는 레지스트리 네임스페이스, 책임 소유자, 릴리스 이벤트, 서명 또는 무결성 증거, 폐기 또는 대체 상태. 출처 위치와 승인은 뭉개져서는 안 된다. 통제가 바뀐 뒤에 파일이 익숙한 호스트로부터 올 수 있다.

다섯째, 호환성 결정을 보존한다. 이전 및 제안된 스키마 정체성, 도구와 규칙 집합, 파괴적 변경 분류, 예외, 승인 역할, 영향받는 생산자나 소비자를 기록한다. "파서가 수용했다"는 결과이지 호환성 검토가 아니다.

여섯째, 디코더 빌드와 정책을 기록한다. 구현과 버전, 미지 필드 동작, 자원 한도, UTF-8 검증, 열거형 처리, 해석을 바꾸는 모든 옵션. ProtoJSON의 경우 미지 필드가 무시될 수 있는지 명시한다.

일곱째, 변환을 기록한다. 바이너리-JSON 브리지, 필드별 복사, 편집, 정규화 또는 재직렬화는 무엇이 살아남는지를 바꿀 수 있다. 영수증은 허용될 때 입력과 출력 다이제스트를 결합하고 알려진 손실, 특히 미지 필드 손실을 식별해야 한다.

여덟째, 무결성과 인증 결과를 스키마 정체성과 분리해 유지한다. 채널 또는 객체 보호, 신뢰된 키 또는 피어, 검증 시각과 범위를 기록한다. 잘못된 스키마 컨텍스트 위의 유효한 서명은 여전히 잘못된 컨텍스트다.

마지막으로 신뢰 결과를 보존한다. 수용, 격리, 거부, 롤백 또는 대체, 책임 있는 결정 소유자, 배포 범위, 사유, 정정 링크. 이것이 메시지 본문을 저장하지 않고 운영 결정을 검사 가능하게 만든다.

이 스키마 컨텍스트 영수증은 Daniel Kade의 편집 제안이다. RFC 9996, IANA 또는 Protobuf 프로젝트가 요구하는 것은 아니다. 스키마 소유권을 중앙화하거나, 페이로드를 공개하거나, 미디어 레지스트리를 애플리케이션 규제기관으로 바꾸지도 않는다. 단지 전송 토큰이 지역적이고 책임 있는 스키마 절차에 속한 권위를 받지 않도록 막을 뿐이다.

증거의 한계

검증된 출처들은 등록, 명시된 의미론, 문서화된 호환성 리스크를 확립한다. 그들은 새 타입이 얼마나 널리 배포되었는지, 어떤 명명된 서비스가 이를 오용하는지, 스키마 불일치로 인한 실제 사건이 있었는지, 어느 스키마 레지스트리 아키텍처가 최선인지 확립하지 않는다.

RFC 9996은 표준 트랙에 있지 않다. 그렇다고 해서 그 등록이 비공식적이거나 중요하지 않은 것은 아니다. 상태를 정확히 기술해야 한다는 뜻이다. 본 문서는 Protobuf에 스키마 진화 메커니즘이 없다거나, JSON이 본질적으로 위험하다거나, 타입 URL이 무용하다거나, 모든 사적 스키마가 공개되어야 한다고 주장하지 않는다.

주장은 더 좁다. 미디어 타입 유효성, 전송 호환성, 스키마 권한은 별개의 사실이다. 앞의 둘은 책임 있는 절차가 빠진 조인을 기록할 때만 셋째를 뒷받침할 수 있다.

출처