--- kind: CASE slug: grpc-core-api-f05 title: 메타데이터 예산의 두 성분 중 하나는 강제되지 않고, 나머지 하나는 바이트가 아니라 문자를 센다 topic: contract-domain-and-bounds project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:grpc-core-api-f05 evidenceCapturedOn: 2026-09-01 assets: - key: grpc-core-api-f05 file: ../../../final/evidence/rendered/grpc-core-api-f05.svg evidence: - ../../../final/evidence/raw/grpc-core-api-f05.txt source: - 원본 분석 절은 analysis/grpc/grpc-core-api.md#L219 이다. module: grpc-core-api priority: P3 --- # 메타데이터 예산의 두 성분 중 하나는 강제되지 않고, 나머지 하나는 바이트가 아니라 문자를 센다 GrpcMetadataBudget 은 세 성분을 갖는다 — maxTotalBytes·maxUserDefinedBytes·maxEntries. check(...) 가 보는 것은 뒤의 둘뿐이다. ## 문제 GrpcMetadataBudget 은 세 성분을 갖는다 — maxTotalBytes·maxUserDefinedBytes·maxEntries. check(...) 가 보는 것은 뒤의 둘뿐이다. ## 결론 저장소 전체에서 이 접근자를 부르는 곳은 compact constructor 의 순서 가드와 테스트 단언 하나뿐이다. 자바독은 그 이유를 설명한다 — 하드 총계를 넘기는 것은 프레임워크가 던지는 전송 거절이고, 여기서 함께 검사하면 "고칠 수 있는 쪽" 과 "고칠 수 없는 쪽" 이 한 자리에서 발견된다는 것. 판단은 옳다. 다만 그 결과로 이 record 는 자기가 쓰지 않는 수를 성분으로 들고 있고, 이름은 그것이 강제된다고 읽힌다. 성분 이름은 ...Bytes 인데 세는 것은 String.length(), 즉 UTF-16 코드 단위다. 키는 [a-z0-9._-] 로 제한되어 ASCII 지만 값에는 문자 집합 제약이 없다. 다중 바이트 문자를 담은 값은 실제 프레임보다 적게 계산된다. gRPC 의 ASCII 메타데이터 값은 프로토콜 상 인쇄 가능 ASCII 여야 하므로 실무에서는 대개 일치한다. ## 검증 환경 OpenJDK : 21.0.12 java -version 으로 확인 Gradle : 9.0.0 src/gradle/wrapper/gradle-wrapper.properties 의 distributionUrl 로 확인 확인 방식 : GrpcMetadataBudget 참조 26건 검색과 check 가 실제로 보는 성분 확인 소스 수정 : x ## 재현 조건 원문은 analysis/grpc/grpc-core-api.md#L219 에 있다. ## 본문 `GrpcMetadataBudget` 은 세 성분을 갖는다 — `maxTotalBytes`·`maxUserDefinedBytes`·`maxEntries`. `check(...)` 가 보는 것은 뒤의 둘뿐이다. 저장소 전체에서 `maxTotalBytes` 접근자를 부르는 곳은 compact constructor 의 순서 가드와 테스트 단언 하나뿐이다. ## GrpcMetadataBudget 참조 위치 :::evidence key="grpc-core-api-f05" alt="코드베이스에서 GrpcMetadataBudget 를 검색한 출력 26줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="GrpcMetadataBudget 코드베이스 검색 — 26줄 · exit 0" zoom="true" ::: ## 그 판단은 옳다 자바독이 이유를 설명한다 — 하드 총계를 넘기는 것은 프레임워크가 던지는 전송 거절이고, 여기서 함께 검사하면 "고칠 수 있는 쪽" 과 "고칠 수 없는 쪽" 이 한 자리에서 발견된다. 다만 그 결과로 이 record 는 자기가 쓰지 않는 수를 성분으로 들고 있고, 이름은 그것이 강제된다고 읽힌다. ## 이름은 Bytes 인데 세는 것은 문자다 `String.length()`, 즉 UTF-16 코드 단위다. 키는 `[a-z0-9._-]` 로 제한되어 ASCII 지만 값에는 문자 집합 제약이 없으므로, 다중 바이트 문자를 담은 값은 실제 프레임보다 적게 계산된다. ## 실무에서는 대개 일치한다 gRPC 의 ASCII 메타데이터 값은 프로토콜 상 인쇄 가능 ASCII 여야 한다. 다만 그 제약을 이 클래스가 검사하지 않으므로, 일치는 보장이 아니라 관행이다. 수정은 둘 다 작다 — `value.getBytes(StandardCharsets.US_ASCII).length` 로 세거나 값의 문자 집합을 `GrpcMetadataKey.Kind.ASCII` 에 맞춰 검증하고, `maxTotalBytes` 는 성분에서 빼고 javadoc 의 서술로 남긴다. ## 확인하지 못한 것 다국어 헤더 값으로 문자 수와 바이트 수의 차이를 실행으로 관측하지 않았다. 계산 대상의 코드 형태로 판정했다.