Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/schema-and-wire-models/concept/concept-messaging-schema-avro-c03.md
T
DongHyeonkaandClaude Fable 5.1 b25357c48a docs(clean-architecture-backend-template): fold analysis into final and re-select one topic
- analysis/·source-index·state.json 을 final/document.md 제2부·제3부로 접었다. SSOT 는 하나다
- 파일럿 — commit-ambiguity-as-a-result 를 새 기준으로 재선별. 후보 14 → 글감 5
  (PROMOTE 5 · MERGE_INTO 3 · KEEP_IN_SSOT 4 · 보류 2). 기록 5건을 다시 썼고 그림 1개를
  techviz 로 만들었다
- 재선별이 잡은 것: 제1부 §6.2·§11.1 이 자기 §13.2 와 어긋나 있었다(레인을 안 돌렸다 vs
  돌렸다) — 정정. 이미 답이 나와 있던 Question 을 HEAD 재실행 질문으로 다시 세웠다.
  Concept 이 인용한 코드가 SSOT 에 없어 뺐다
- candidateScope·sourceRepository 기록. 나머지 43개 주제는 재선별 대기(PENDING 905)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 12:39:20 +09:00

4.2 KiB

kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, assets, evidence, source, module
kind slug title topic project status sourceRevision rootTreeNode evidenceCapturedOn assets evidence source module
CONCEPT messaging-schema-avro-c03 틀린 스키마로 디코딩해도 실패하지 않는다 schema-and-wire-models clean-architecture-backend-template 게시 전 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 concept:messaging-schema-avro-c03 2026-09-01
key file
messaging-schema-avro-c03 ../../../final/evidence/rendered/messaging-schema-avro-c03.svg
key file
messaging-schema-avro-c03-diagram ../../../final/assets/diagrams/messaging-schema-avro-c03.svg
../../../final/evidence/raw/messaging-schema-avro-c03.txt
원본 분석 절은 final/document.md#a19-messaging-schema-avro#L106 이다.
messaging-schema-avro

틀린 스키마로 디코딩해도 실패하지 않는다

"does not fail — it produces plausible garbage"가 이 leaf의 모든 방어의 전제다. JSON이나 Protobuf와 달리 Avro는 잘못된 스키마로 디코딩해도 예외를 던지지 않는 경우가 있다.

관계

  • 모드 enum을 분기 조건으로 쓰면 각 분기에 테스트를 둔다 같은 분석 리프에서 끌어낸 규칙이다.
  • 컬렉션 순서가 계약이면 양쪽에서 같은 방향으로 적는다 같은 분석 리프에서 끌어낸 규칙이다.
  • 안정 코드는 판단 단위로 정하고 구현 단위로 정하지 않는다 같은 분석 리프에서 끌어낸 규칙이다.

본문

"does not fail — it produces plausible garbage"가 이 leaf의 모든 방어의 전제다. JSON이나 Protobuf와 달리 Avro는 잘못된 스키마로 디코딩해도 예외를 던지지 않는 경우가 있다. single-object encoding에 헤더를 붙이지 않는 것도 명시적 결정이다 — "The framing that would carry a schema fingerprint belongs to the transport headers, where the platform already carries schema identity for every format, rather than being duplicated inside the Avro payload for this one format."

얕은 복사가 만든 구멍

:::evidence key="messaging-schema-avro-c03-diagram" alt="중첩 맵 쪽에 바깥 맵만 복사와 안쪽 맵은 호출자 소유가 빗금으로 놓이고 평탄화된 키 쪽에 두 레벨 모두 복사와 버전이 키의 일부가 놓인다" caption="얕은 복사가 만든 구멍" zoom="false" :::

생성자가 받는 것은 중첩 맵 Map<MessageType, Map<SchemaVersion, Schema>>이고, Map.copyOf바깥 레벨만 복사한다 — 안쪽 맵은 호출자 객체로 남아, 참조를 쥔 호출자가 생성 후에 버전을 추가·교체·제거하면 codec이 조용히 그것으로 인코딩하기 시작했다. (type, version) 키로 평탄화하면 두 레벨이 모두 복사되고 버전이 조회 identity의 일부가 된다. 이 결함이 위험했던 이유는 위와 곱해진다 — 스키마가 바뀌어도 디코딩이 실패하지 않고 그럴듯한 쓰레기를 낸다.

AvroRegistryBoundsTest 참조 위치

:::evidence key="messaging-schema-avro-c03" alt="코드베이스에서 AvroRegistryBoundsTest 를 검색한 출력 1줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="AvroRegistryBoundsTest 코드베이스 검색 — 1줄 · exit 0" zoom="true" :::

mutatingTheCallersMapAfterConstructionChangesNothing이 세 가지를 한 번에 확인한다 — 생성 후 추가한 버전은 미등록, 생성 후 추가한 타입도 미등록, 원래 등록한 스키마는 그대로. 평탄화가 MessageContractKey(schema-api)를 키로 쓰므로 §4.5의 2단 에러 구분도 자연히 따라온다.

direct encoder 한 줄에 방어가 걸려 있다

BoundedByteSink(schema-api)의 경계가 실제로 작동하려면 인코더가 증분적으로 써야 한다. EncoderFactory.get().binaryEncoder(...)는 버퍼링하므로 sink가 첫 write를 보기 전에 큰 레코드가 이미 할당된다. 즉 schema-api의 방어가 이 한 줄에 의존한다.

인코딩 전 검사 둘

payload가 GenericRecord인가 → AVRO_PAYLOAD_NOT_A_RECORD. schema.equals(record.getSchema())인가 → AVRO_SCHEMA_MISMATCH. 두 번째는 테스트가 이유를 적는다 — as("encoding v2 data under the v1 version would produce bytes nothing can decode").