{ "schema_version": "1.0", "document": "/home/donghyeon/workspace/chat-gpt-container/document-haness/docs/clean-architecture-backend-template/final/document.md", "document_sha256": "8071fe71b3359d9cf60b95909c26c7b50653ce2f22bbc5fcf6988719bb91236d", "line_count": 47035, "line_number_space": "canonical-source-with-managed-blocks-collapsed", "anchor": { "kind": "line", "value": 36619, "line": 36619 }, "current_section": { "heading": { "line": 36619, "level": 4, "text": "1. 모듈의 정체와 경계" }, "start_line": 36619, "end_line": 36643, "text": "#### 1. 모듈의 정체와 경계\n\nStable JSON codec 하나. `MessageCodec`(schema-api)을 구현하고 Jackson 3(`tools.jackson.*` 네임스페이스)을 쓴다.\n\njavadoc이 \"기본 codec으로 노출해도 안전한 이유\" 셋을 명시한다.\n\n```java\n// JacksonMessageCodec.java:29-37\n *
Three things make this safe to expose as the default. The message-type registry is closed, so\n * a payload class only becomes reachable when someone registered it. The parser is constrained on\n * depth, document length, and duplicate keys, so a hostile document cannot exhaust the consumer\n * before the handler ever runs. And the encoded size is checked against the destination limit here\n * rather than at the broker, so an oversized payload fails locally with {@code NOT_TRANSMITTED}\n * evidence instead of ambiguously mid-flight.\n *\n *
Polymorphic default typing is never enabled. It is the mechanism behind most JSON\n * deserialization gadget chains, and no legitimate message contract needs it.\n```\n\n세 번째가 `messaging-core-api`의 3상태 발행 결과와 직접 연결된다 — 크기 초과를 브로커가 아니라 여기서 잡으면 `NOT_TRANSMITTED` 증거가 붙은 `REJECTED`가 되고, 브로커에서 잡히면 `AMBIGUOUS`가 된다. 전자는 버려도 안전하고 후자는 아니다.\n\nJackson 의존성은 `implementation`이다 — public 시그니처에 Jackson 타입이 없기 때문이다. 형제 leaf(`schema-avro`, `schema-protobuf`, `cloudevents`)는 vendor 타입이 public 시그니처에 나오므로 `api`로 선언했고 build.gradle에 그 이유를 주석으로 적었다. `src/messaging/CLAUDE.md:40-43`의 게이트가 이 구분을 강제한다.\n\n---\n"
},
"previous_section": {
"heading": {
"line": 36584,
"level": 4,
"text": "0. SSOT identity / 커버리지와 숫자 지도"
},
"start_line": 36584,
"end_line": 36618,
"text": "#### 0. SSOT identity / 커버리지와 숫자 지도\n\n- registered leaf id: `messaging-schema-json`\n- canonical state `analysisFile`: §A19-MESSAGING-SCHEMA-JSON\n- source path: `src/messaging/messaging-schema-json`\n- registry `allowed_dependencies`: `[\"messaging-core-api\", \"messaging-schema-api\"]`\n- registry `runtime_memberships`: `[\"app-bootstrap\"]`\n\n##### 숫자\n\n| 항목 | 수 |\n|---|---:|\n| production Java 파일 | **1** |\n| production LOC | 226 |\n| 패키지 | 1 (`dev.caskeleton.messaging.schema.json`) |\n| test 파일 | 3 |\n| test 메서드(실행 확인) | 18 |\n| 외부 의존성 | 1 (`tools.jackson.core:jackson-databind`, `implementation`) |\n\n이 leaf는 클래스 하나다: `JacksonMessageCodec`. **그리고 messaging 플랫폼에서 production 소비자를 가진 유일한 codec이다**(§12.1).\n\n##### Coverage ledger\n\n| scope/file group | count | disposition | reason |\n|---|---:|---|---|\n| `.../json/JacksonMessageCodec.java` | 1 | `FULL_READ` | 226줄 전문 |\n| `src/test/java/**` | 3 | `FULL_READ` | 전문 |\n| `build.gradle` | 1 | `FULL_READ` | 8줄 |\n| `gradle.lockfile` | 1 | `STRUCTURAL_ONLY` | 잠금 파일 |\n| `build/**` | — | `EXCLUDED` | 빌드 산출물 |\n\n`UNCLASSIFIED` 0.\n\n---\n"
},
"next_section": {
"heading": {
"line": 36644,
"level": 4,
"text": "2. 의존성과 런타임 배선"
},
"start_line": 36644,
"end_line": 36679,
"text": "#### 2. 의존성과 런타임 배선\n\n들어오는 것: `messaging-core-api`(api), `messaging-schema-api`(api), `jackson-databind`(implementation).\n\n나가는 것: `messaging-spring-boot-starter`(registry `allowed_dependencies`에 포함).\n\n**실제 배선 지점이 하나 있다** — 이 플랫폼에서 유일하게 조립되는 codec이다.\n\n```java\n// messaging-spring-boot-starter/.../MessagingCoreAutoConfiguration.java:360-366\n@ConditionalOnMissingBean(dev.caskeleton.messaging.schema.MessageCodecRegistry.class)\npublic dev.caskeleton.messaging.runtime.RegisteredMessageCodecs messagingCodecs(\n ObjectProvider Three things make this safe to expose as the default. The message-type registry is closed, so"
},
{
"line": 36628,
"text": " * a payload class only becomes reachable when someone registered it. The parser is constrained on"
},
{
"line": 36629,
"text": " * depth, document length, and duplicate keys, so a hostile document cannot exhaust the consumer"
},
{
"line": 36630,
"text": " * before the handler ever runs. And the encoded size is checked against the destination limit here"
},
{
"line": 36631,
"text": " * rather than at the broker, so an oversized payload fails locally with {@code NOT_TRANSMITTED}"
},
{
"line": 36632,
"text": " * evidence instead of ambiguously mid-flight."
},
{
"line": 36633,
"text": " *"
},
{
"line": 36634,
"text": " * Polymorphic default typing is never enabled. It is the mechanism behind most JSON"
},
{
"line": 36635,
"text": " * deserialization gadget chains, and no legitimate message contract needs it."
},
{
"line": 36636,
"text": "```"
},
{
"line": 36637,
"text": ""
},
{
"line": 36638,
"text": "세 번째가 `messaging-core-api`의 3상태 발행 결과와 직접 연결된다 — 크기 초과를 브로커가 아니라 여기서 잡으면 `NOT_TRANSMITTED` 증거가 붙은 `REJECTED`가 되고, 브로커에서 잡히면 `AMBIGUOUS`가 된다. 전자는 버려도 안전하고 후자는 아니다."
},
{
"line": 36639,
"text": ""
},
{
"line": 36640,
"text": "Jackson 의존성은 `implementation`이다 — public 시그니처에 Jackson 타입이 없기 때문이다. 형제 leaf(`schema-avro`, `schema-protobuf`, `cloudevents`)는 vendor 타입이 public 시그니처에 나오므로 `api`로 선언했고 build.gradle에 그 이유를 주석으로 적었다. `src/messaging/CLAUDE.md:40-43`의 게이트가 이 구분을 강제한다."
},
{
"line": 36641,
"text": ""
},
{
"line": 36642,
"text": "---"
},
{
"line": 36643,
"text": ""
},
{
"line": 36644,
"text": "#### 2. 의존성과 런타임 배선"
},
{
"line": 36645,
"text": ""
},
{
"line": 36646,
"text": "들어오는 것: `messaging-core-api`(api), `messaging-schema-api`(api), `jackson-databind`(implementation)."
},
{
"line": 36647,
"text": ""
},
{
"line": 36648,
"text": "나가는 것: `messaging-spring-boot-starter`(registry `allowed_dependencies`에 포함)."
},
{
"line": 36649,
"text": ""
},
{
"line": 36650,
"text": "**실제 배선 지점이 하나 있다** — 이 플랫폼에서 유일하게 조립되는 codec이다."
},
{
"line": 36651,
"text": ""
},
{
"line": 36652,
"text": "```java"
},
{
"line": 36653,
"text": "// messaging-spring-boot-starter/.../MessagingCoreAutoConfiguration.java:360-366"
},
{
"line": 36654,
"text": "@ConditionalOnMissingBean(dev.caskeleton.messaging.schema.MessageCodecRegistry.class)"
},
{
"line": 36655,
"text": "public dev.caskeleton.messaging.runtime.RegisteredMessageCodecs messagingCodecs("
},
{
"line": 36656,
"text": " ObjectProvider Three things make this safe to expose as the default. The message-type registry is closed, so\n36628 | * a payload class only becomes reachable when someone registered it. The parser is constrained on\n36629 | * depth, document length, and duplicate keys, so a hostile document cannot exhaust the consumer\n36630 | * before the handler ever runs. And the encoded size is checked against the destination limit here\n36631 | * rather than at the broker, so an oversized payload fails locally with {@code NOT_TRANSMITTED}\n36632 | * evidence instead of ambiguously mid-flight.\n36633 | *\n36634 | * Polymorphic default typing is never enabled. It is the mechanism behind most JSON\n36635 | * deserialization gadget chains, and no legitimate message contract needs it.\n36636 | ```\n36637 | \n36638 | 세 번째가 `messaging-core-api`의 3상태 발행 결과와 직접 연결된다 — 크기 초과를 브로커가 아니라 여기서 잡으면 `NOT_TRANSMITTED` 증거가 붙은 `REJECTED`가 되고, 브로커에서 잡히면 `AMBIGUOUS`가 된다. 전자는 버려도 안전하고 후자는 아니다.\n36639 | \n36640 | Jackson 의존성은 `implementation`이다 — public 시그니처에 Jackson 타입이 없기 때문이다. 형제 leaf(`schema-avro`, `schema-protobuf`, `cloudevents`)는 vendor 타입이 public 시그니처에 나오므로 `api`로 선언했고 build.gradle에 그 이유를 주석으로 적었다. `src/messaging/CLAUDE.md:40-43`의 게이트가 이 구분을 강제한다.\n36641 | \n36642 | ---\n36643 | \n36644 | #### 2. 의존성과 런타임 배선\n36645 | \n36646 | 들어오는 것: `messaging-core-api`(api), `messaging-schema-api`(api), `jackson-databind`(implementation).\n36647 | \n36648 | 나가는 것: `messaging-spring-boot-starter`(registry `allowed_dependencies`에 포함).\n36649 | \n36650 | **실제 배선 지점이 하나 있다** — 이 플랫폼에서 유일하게 조립되는 codec이다.\n36651 | \n36652 | ```java\n36653 | // messaging-spring-boot-starter/.../MessagingCoreAutoConfiguration.java:360-366\n36654 | @ConditionalOnMissingBean(dev.caskeleton.messaging.schema.MessageCodecRegistry.class)\n36655 | public dev.caskeleton.messaging.runtime.RegisteredMessageCodecs messagingCodecs(\n36656 | ObjectProvider