{ "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": 29089, "line": 29089 }, "current_section": { "heading": { "line": 29089, "level": 3, "text": "messaging-kafka-share-experimental 완전 해부" }, "start_line": 29089, "end_line": 29637, "text": "### messaging-kafka-share-experimental 완전 해부\n\n> 상태: COMPLETE\n> 기준 revision: `21234e38cdb9a926cbc92bb97a2aee2e4a7d2916`\n> 분석 범위: `src/messaging/messaging-kafka-share-experimental`\n> SSOT owner: `messaging-kafka-share-experimental`\n> integration/family document: §A19 (secondary, INTEGRATION_ONLY)\n\n---\n\n#### 0. SSOT identity / 커버리지와 숫자 지도\n\n- registered leaf id: `messaging-kafka-share-experimental`\n- canonical state `analysisFile`: §A19-MESSAGING-KAFKA-SHARE-EXPERIMENTAL\n- source path: `src/messaging/messaging-kafka-share-experimental`\n- registry `allowed_dependencies`: `[\"messaging-core-api\", \"messaging-policy\", \"messaging-transport-spi\", \"messaging-kafka\"]`\n- registry `runtime_memberships`: **`[]`** — build-only / incubating\n\n##### 숫자\n\n| 항목 | 수 |\n|---|---:|\n| production Java 파일 | **4** |\n| production LOC | **190** — messaging family에서 가장 작다 |\n| 패키지 | 1 (`dev.caskeleton.messaging.kafka.share`) |\n| test 파일 | 1 |\n| test 메서드(실행 확인) | 6 |\n| 선언된 외부 의존성 | 1 (`org.apache.kafka:kafka-clients`, `implementation`) |\n| **실제 사용된 외부 의존성** | **0**(§12.4) |\n\n네 타입:\n\n| 타입 | 종류 | LOC | leaf 밖 참조 |\n|---|---|---:|---:|\n| `KafkaShareGroupRegistrar` | class | 88 | **0** |\n| `KafkaShareProfileValidator` | class | 42 | **0** |\n| `KafkaShareProfile` | record | 33 | **0** |\n| `KafkaShareWorkQueueCapability` | class | 27 | **0** |\n\n##### Coverage ledger\n\n| scope/file group | count | disposition | reason |\n|---|---:|---|---|\n| `src/main/java/**` (4) | 4 | `FULL_READ` | 전 파일 본문 확인 |\n| `src/test/java/**` (1) | 1 | `FULL_READ` | 6개 테스트 확인 |\n| `build.gradle` | 1 | `FULL_READ` | 10줄 |\n| `gradle.lockfile` | 1 | `STRUCTURAL_ONLY` | 잠금 파일 |\n| `build/**` | — | `EXCLUDED` | 빌드 산출물 |\n\n`UNCLASSIFIED` 0.\n\n---\n\n#### 1. 모듈의 정체와 경계\n\nKafka **Share Group**(KIP-932, 경쟁 소비자 work queue)을 실험적 어댑터로 감싼다. `runtime_memberships: []`이고 이름 자체가 `-experimental`이다.\n\n이 leaf의 실질은 **거절**이다. 190줄 중 실제 동작을 하는 코드는 거의 없고, 세 가지를 거절한다.\n\n| 거절 | 코드 | 이유 |\n|---|---|---|\n| 비활성 상태의 사용 | `KAFKA_SHARE_DISABLED` | experimental이 기본 켜지지 않게 |\n| 순서 보장 목적지 | `IllegalArgumentException` | share group이 순서를 줄 수 없음 |\n| pause/resume | `KAFKA_SHARE_NO_PAUSE`/`_NO_RESUME` | 일시정지할 파티션 할당이 없음 |\n\n핵심 진술이 validator javadoc에 있다.\n\n```java\n// KafkaShareProfileValidator.java:10-17\n *
A share group hands individual records to competing consumers and acknowledges them\n * individually. That is a work queue, and it is fundamentally incompatible with partition ordering:\n * two consumers in the same share group can process records from one partition concurrently and\n * finish in either order. Configuring an ordered destination on a share group would therefore\n * advertise a guarantee the broker is not providing, so it is refused rather than degraded.\n *\n *
The adapter is also off unless explicitly enabled, so an Experimental capability cannot drift\n * into a Stable deployment by default.\n```\n\n두 번째 문단이 이 저장소의 experimental 정책을 한 문장으로 담는다 — **기본 꺼짐이 drift 방지 수단이다.**\n\n---\n\n#### 2. 의존성과 런타임 배선\n\n들어오는 것(project): `messaging-core-api`, `messaging-policy`, `messaging-transport-spi`, `messaging-kafka` — 넷 다 `api`.\n\n들어오는 것(vendor): `org.apache.kafka:kafka-clients`(`implementation`) — **어떤 소스도 import하지 않는다**(§12.4).\n\n나가는 것: **없다.** 어떤 leaf의 `allowed_dependencies`에도 이 leaf가 없고 starter 목록에도 없다.\n\n런타임 배선: 없음. `runtime_memberships: []`. bean 없음(Spring 주석 0개).\n\n**소비자 없음·membership 없음·조립 없음의 삼중 정합**이다 — `messaging-schema-avro`·`messaging-schema-protobuf`와 같은 형태이고, incubating leaf의 올바른 상태다.\n\n**`messaging-policy`와 `messaging-kafka` 의존이 실제로 쓰이는가.**\n\n| 의존 | 사용 |\n|---|---|\n| `messaging-core-api` | `OrderingScope`, `DestinationName`, `MessagingCapabilities`, `MessagingCapabilityUnavailableException` — **사용** |\n| `messaging-transport-spi` | `TransportConsumerRegistration`, `TransportConsumerSpec` — **사용** |\n| `messaging-policy` | 어떤 타입도 import하지 않음 — **미사용** |\n| `messaging-kafka` | 어떤 타입도 import하지 않음 — **미사용** |\n\n네 project 의존 중 둘, 벤더 의존 하나가 미사용이다. §17.\n\n---\n\n#### 3. 패키지/컴포넌트 지도\n\n```\nKafkaShareProfile (record)\n destination · shareGroup · orderingScope · enabled · maxDeliveryCount\n ↓\nKafkaShareProfileValidator.validate(profile)\n ├── !enabled → MessagingCapabilityUnavailableException(KAFKA_SHARE_DISABLED)\n └── orderingScope != NONE → IllegalArgumentException\n ↓\nKafkaShareGroupRegistrar.register(profile, spec)\n └── ShareRegistration implements TransportConsumerRegistration\n ├── pause(scope) → failedFuture(KAFKA_SHARE_NO_PAUSE)\n ├── resume(scope) → failedFuture(KAFKA_SHARE_NO_RESUME)\n ├── isActive() → true until close()\n └── close() → active = false\n\nKafkaShareWorkQueueCapability.capabilities() → MessagingCapabilities(12 booleans)\n```\n\n---\n\n#### 4. 계약·불변식·상태 모델\n\n##### 4.1 `KafkaShareProfile`\n\n다섯 필드. 생성자가 `shareGroup` 공백과 `maxDeliveryCount < 1`을 거절한다.\n\n`maxDeliveryCount`가 javadoc에서 \"how many times a record may be re-acquired before it is released\"라고 정의된다 — Share Group의 재획득 한계다. **이 필드를 읽는 코드가 이 leaf에 없다.** validator도 registrar도 쓰지 않는다.\n\n##### 4.2 `KafkaShareProfileValidator` — 두 거절\n\n```java\nif (!profile.enabled()) {\n throw new MessagingCapabilityUnavailableException(\n \"KAFKA_SHARE_DISABLED\",\n \"the Kafka Share Group adapter is experimental and disabled unless \"\n + \"backend.messaging.experimental.kafka-share=true\");\n}\nif (profile.orderingScope() != OrderingScope.NONE) {\n throw new IllegalArgumentException(\n \"a Kafka share group cannot provide ordered delivery: \" + profile.destination().value());\n}\n```\n\n**두 거절의 예외 타입이 다르다.** 첫째는 `MessagingCapabilityUnavailableException`(카테고리 `CONFIGURATION`, 안정 코드 있음), 둘째는 `IllegalArgumentException`(코드 없음). 둘 다 설정 오류인데 하나만 플랫폼 실패 어휘를 쓴다. §17.\n\n에러 메시지가 **프로퍼티 키를 직접 적는다** — `backend.messaging.experimental.kafka-share=true`. 그 키를 읽는 코드가 이 저장소에 없다(§12.4).\n\n##### 4.3 `KafkaShareGroupRegistrar` — spec을 받고 쓰지 않는다\n\n```java\npublic TransportConsumerRegistration register(\n KafkaShareProfile profile, TransportConsumerSpec spec) {\n Objects.requireNonNull(spec, \"spec must not be null\");\n validator.validate(profile);\n return new ShareRegistration(profile);\n}\n```\n\n`spec`은 **null 검사만 받는다.** `ShareRegistration`은 `profile`과 `AtomicBoolean active` 둘만 갖는다.\n\n`TransportConsumerSpec`은 `(DestinationProfile profile, Function Pause and resume are refused rather than silently ignored. A share group has no partition\n * assignment to pause, so accepting the call would let a retry policy that depends on pausing\n * appear to work while doing nothing.\n```\n\n**예외를 던지지 않고 실패한 `CompletionStage`를 반환한다** — `TransportConsumerRegistration.pause`의 반환 타입이 `CompletionStage A share group hands individual records to competing consumers and acknowledges them"
},
{
"line": 29159,
"text": " * individually. That is a work queue, and it is fundamentally incompatible with partition ordering:"
},
{
"line": 29160,
"text": " * two consumers in the same share group can process records from one partition concurrently and"
},
{
"line": 29161,
"text": " * finish in either order. Configuring an ordered destination on a share group would therefore"
},
{
"line": 29162,
"text": " * advertise a guarantee the broker is not providing, so it is refused rather than degraded."
},
{
"line": 29163,
"text": " *"
},
{
"line": 29164,
"text": " * The adapter is also off unless explicitly enabled, so an Experimental capability cannot drift"
},
{
"line": 29165,
"text": " * into a Stable deployment by default."
},
{
"line": 29166,
"text": "```"
},
{
"line": 29167,
"text": ""
},
{
"line": 29168,
"text": "두 번째 문단이 이 저장소의 experimental 정책을 한 문장으로 담는다 — **기본 꺼짐이 drift 방지 수단이다.**"
},
{
"line": 29169,
"text": ""
},
{
"line": 29170,
"text": "---"
},
{
"line": 29171,
"text": ""
},
{
"line": 29172,
"text": "#### 2. 의존성과 런타임 배선"
},
{
"line": 29173,
"text": ""
},
{
"line": 29174,
"text": "들어오는 것(project): `messaging-core-api`, `messaging-policy`, `messaging-transport-spi`, `messaging-kafka` — 넷 다 `api`."
},
{
"line": 29175,
"text": ""
},
{
"line": 29176,
"text": "들어오는 것(vendor): `org.apache.kafka:kafka-clients`(`implementation`) — **어떤 소스도 import하지 않는다**(§12.4)."
},
{
"line": 29177,
"text": ""
},
{
"line": 29178,
"text": "나가는 것: **없다.** 어떤 leaf의 `allowed_dependencies`에도 이 leaf가 없고 starter 목록에도 없다."
},
{
"line": 29179,
"text": ""
},
{
"line": 29180,
"text": "런타임 배선: 없음. `runtime_memberships: []`. bean 없음(Spring 주석 0개)."
},
{
"line": 29181,
"text": ""
},
{
"line": 29182,
"text": "**소비자 없음·membership 없음·조립 없음의 삼중 정합**이다 — `messaging-schema-avro`·`messaging-schema-protobuf`와 같은 형태이고, incubating leaf의 올바른 상태다."
},
{
"line": 29183,
"text": ""
},
{
"line": 29184,
"text": "**`messaging-policy`와 `messaging-kafka` 의존이 실제로 쓰이는가.**"
},
{
"line": 29185,
"text": ""
},
{
"line": 29186,
"text": "| 의존 | 사용 |"
},
{
"line": 29187,
"text": "|---|---|"
},
{
"line": 29188,
"text": "| `messaging-core-api` | `OrderingScope`, `DestinationName`, `MessagingCapabilities`, `MessagingCapabilityUnavailableException` — **사용** |"
},
{
"line": 29189,
"text": "| `messaging-transport-spi` | `TransportConsumerRegistration`, `TransportConsumerSpec` — **사용** |"
},
{
"line": 29190,
"text": "| `messaging-policy` | 어떤 타입도 import하지 않음 — **미사용** |"
},
{
"line": 29191,
"text": "| `messaging-kafka` | 어떤 타입도 import하지 않음 — **미사용** |"
},
{
"line": 29192,
"text": ""
},
{
"line": 29193,
"text": "네 project 의존 중 둘, 벤더 의존 하나가 미사용이다. §17."
},
{
"line": 29194,
"text": ""
},
{
"line": 29195,
"text": "---"
},
{
"line": 29196,
"text": ""
},
{
"line": 29197,
"text": "#### 3. 패키지/컴포넌트 지도"
},
{
"line": 29198,
"text": ""
},
{
"line": 29199,
"text": "```"
},
{
"line": 29200,
"text": "KafkaShareProfile (record)"
},
{
"line": 29201,
"text": " destination · shareGroup · orderingScope · enabled · maxDeliveryCount"
},
{
"line": 29202,
"text": " ↓"
},
{
"line": 29203,
"text": "KafkaShareProfileValidator.validate(profile)"
},
{
"line": 29204,
"text": " ├── !enabled → MessagingCapabilityUnavailableException(KAFKA_SHARE_DISABLED)"
},
{
"line": 29205,
"text": " └── orderingScope != NONE → IllegalArgumentException"
},
{
"line": 29206,
"text": " ↓"
},
{
"line": 29207,
"text": "KafkaShareGroupRegistrar.register(profile, spec)"
},
{
"line": 29208,
"text": " └── ShareRegistration implements TransportConsumerRegistration"
},
{
"line": 29209,
"text": " ├── pause(scope) → failedFuture(KAFKA_SHARE_NO_PAUSE)"
},
{
"line": 29210,
"text": " ├── resume(scope) → failedFuture(KAFKA_SHARE_NO_RESUME)"
},
{
"line": 29211,
"text": " ├── isActive() → true until close()"
},
{
"line": 29212,
"text": " └── close() → active = false"
},
{
"line": 29213,
"text": ""
},
{
"line": 29214,
"text": "KafkaShareWorkQueueCapability.capabilities() → MessagingCapabilities(12 booleans)"
},
{
"line": 29215,
"text": "```"
},
{
"line": 29216,
"text": ""
},
{
"line": 29217,
"text": "---"
},
{
"line": 29218,
"text": ""
},
{
"line": 29219,
"text": "#### 4. 계약·불변식·상태 모델"
},
{
"line": 29220,
"text": ""
},
{
"line": 29221,
"text": "##### 4.1 `KafkaShareProfile`"
},
{
"line": 29222,
"text": ""
},
{
"line": 29223,
"text": "다섯 필드. 생성자가 `shareGroup` 공백과 `maxDeliveryCount < 1`을 거절한다."
},
{
"line": 29224,
"text": ""
},
{
"line": 29225,
"text": "`maxDeliveryCount`가 javadoc에서 \"how many times a record may be re-acquired before it is released\"라고 정의된다 — Share Group의 재획득 한계다. **이 필드를 읽는 코드가 이 leaf에 없다.** validator도 registrar도 쓰지 않는다."
},
{
"line": 29226,
"text": ""
},
{
"line": 29227,
"text": "##### 4.2 `KafkaShareProfileValidator` — 두 거절"
},
{
"line": 29228,
"text": ""
},
{
"line": 29229,
"text": "```java"
},
{
"line": 29230,
"text": "if (!profile.enabled()) {"
},
{
"line": 29231,
"text": " throw new MessagingCapabilityUnavailableException("
},
{
"line": 29232,
"text": " \"KAFKA_SHARE_DISABLED\","
},
{
"line": 29233,
"text": " \"the Kafka Share Group adapter is experimental and disabled unless \""
},
{
"line": 29234,
"text": " + \"backend.messaging.experimental.kafka-share=true\");"
},
{
"line": 29235,
"text": "}"
},
{
"line": 29236,
"text": "if (profile.orderingScope() != OrderingScope.NONE) {"
},
{
"line": 29237,
"text": " throw new IllegalArgumentException("
},
{
"line": 29238,
"text": " \"a Kafka share group cannot provide ordered delivery: \" + profile.destination().value());"
},
{
"line": 29239,
"text": "}"
},
{
"line": 29240,
"text": "```"
},
{
"line": 29241,
"text": ""
},
{
"line": 29242,
"text": "**두 거절의 예외 타입이 다르다.** 첫째는 `MessagingCapabilityUnavailableException`(카테고리 `CONFIGURATION`, 안정 코드 있음), 둘째는 `IllegalArgumentException`(코드 없음). 둘 다 설정 오류인데 하나만 플랫폼 실패 어휘를 쓴다. §17."
},
{
"line": 29243,
"text": ""
},
{
"line": 29244,
"text": "에러 메시지가 **프로퍼티 키를 직접 적는다** — `backend.messaging.experimental.kafka-share=true`. 그 키를 읽는 코드가 이 저장소에 없다(§12.4)."
},
{
"line": 29245,
"text": ""
},
{
"line": 29246,
"text": "##### 4.3 `KafkaShareGroupRegistrar` — spec을 받고 쓰지 않는다"
},
{
"line": 29247,
"text": ""
},
{
"line": 29248,
"text": "```java"
},
{
"line": 29249,
"text": "public TransportConsumerRegistration register("
},
{
"line": 29250,
"text": " KafkaShareProfile profile, TransportConsumerSpec spec) {"
},
{
"line": 29251,
"text": " Objects.requireNonNull(spec, \"spec must not be null\");"
},
{
"line": 29252,
"text": " validator.validate(profile);"
},
{
"line": 29253,
"text": " return new ShareRegistration(profile);"
},
{
"line": 29254,
"text": "}"
},
{
"line": 29255,
"text": "```"
},
{
"line": 29256,
"text": ""
},
{
"line": 29257,
"text": "`spec`은 **null 검사만 받는다.** `ShareRegistration`은 `profile`과 `AtomicBoolean active` 둘만 갖는다."
},
{
"line": 29258,
"text": ""
},
{
"line": 29259,
"text": "`TransportConsumerSpec`은 `(DestinationProfile profile, Function Pause and resume are refused rather than silently ignored. A share group has no partition"
},
{
"line": 29280,
"text": " * assignment to pause, so accepting the call would let a retry policy that depends on pausing"
},
{
"line": 29281,
"text": " * appear to work while doing nothing."
},
{
"line": 29282,
"text": "```"
},
{
"line": 29283,
"text": ""
},
{
"line": 29284,
"text": "**예외를 던지지 않고 실패한 `CompletionStage`를 반환한다** — `TransportConsumerRegistration.pause`의 반환 타입이 `CompletionStage A share group hands individual records to competing consumers and acknowledges them\n29159 | * individually. That is a work queue, and it is fundamentally incompatible with partition ordering:\n29160 | * two consumers in the same share group can process records from one partition concurrently and\n29161 | * finish in either order. Configuring an ordered destination on a share group would therefore\n29162 | * advertise a guarantee the broker is not providing, so it is refused rather than degraded.\n29163 | *\n29164 | * The adapter is also off unless explicitly enabled, so an Experimental capability cannot drift\n29165 | * into a Stable deployment by default.\n29166 | ```\n29167 | \n29168 | 두 번째 문단이 이 저장소의 experimental 정책을 한 문장으로 담는다 — **기본 꺼짐이 drift 방지 수단이다.**\n29169 | \n29170 | ---\n29171 | \n29172 | #### 2. 의존성과 런타임 배선\n29173 | \n29174 | 들어오는 것(project): `messaging-core-api`, `messaging-policy`, `messaging-transport-spi`, `messaging-kafka` — 넷 다 `api`.\n29175 | \n29176 | 들어오는 것(vendor): `org.apache.kafka:kafka-clients`(`implementation`) — **어떤 소스도 import하지 않는다**(§12.4).\n29177 | \n29178 | 나가는 것: **없다.** 어떤 leaf의 `allowed_dependencies`에도 이 leaf가 없고 starter 목록에도 없다.\n29179 | \n29180 | 런타임 배선: 없음. `runtime_memberships: []`. bean 없음(Spring 주석 0개).\n29181 | \n29182 | **소비자 없음·membership 없음·조립 없음의 삼중 정합**이다 — `messaging-schema-avro`·`messaging-schema-protobuf`와 같은 형태이고, incubating leaf의 올바른 상태다.\n29183 | \n29184 | **`messaging-policy`와 `messaging-kafka` 의존이 실제로 쓰이는가.**\n29185 | \n29186 | | 의존 | 사용 |\n29187 | |---|---|\n29188 | | `messaging-core-api` | `OrderingScope`, `DestinationName`, `MessagingCapabilities`, `MessagingCapabilityUnavailableException` — **사용** |\n29189 | | `messaging-transport-spi` | `TransportConsumerRegistration`, `TransportConsumerSpec` — **사용** |\n29190 | | `messaging-policy` | 어떤 타입도 import하지 않음 — **미사용** |\n29191 | | `messaging-kafka` | 어떤 타입도 import하지 않음 — **미사용** |\n29192 | \n29193 | 네 project 의존 중 둘, 벤더 의존 하나가 미사용이다. §17.\n29194 | \n29195 | ---\n29196 | \n29197 | #### 3. 패키지/컴포넌트 지도\n29198 | \n29199 | ```\n29200 | KafkaShareProfile (record)\n29201 | destination · shareGroup · orderingScope · enabled · maxDeliveryCount\n29202 | ↓\n29203 | KafkaShareProfileValidator.validate(profile)\n29204 | ├── !enabled → MessagingCapabilityUnavailableException(KAFKA_SHARE_DISABLED)\n29205 | └── orderingScope != NONE → IllegalArgumentException\n29206 | ↓\n29207 | KafkaShareGroupRegistrar.register(profile, spec)\n29208 | └── ShareRegistration implements TransportConsumerRegistration\n29209 | ├── pause(scope) → failedFuture(KAFKA_SHARE_NO_PAUSE)\n29210 | ├── resume(scope) → failedFuture(KAFKA_SHARE_NO_RESUME)\n29211 | ├── isActive() → true until close()\n29212 | └── close() → active = false\n29213 | \n29214 | KafkaShareWorkQueueCapability.capabilities() → MessagingCapabilities(12 booleans)\n29215 | ```\n29216 | \n29217 | ---\n29218 | \n29219 | #### 4. 계약·불변식·상태 모델\n29220 | \n29221 | ##### 4.1 `KafkaShareProfile`\n29222 | \n29223 | 다섯 필드. 생성자가 `shareGroup` 공백과 `maxDeliveryCount < 1`을 거절한다.\n29224 | \n29225 | `maxDeliveryCount`가 javadoc에서 \"how many times a record may be re-acquired before it is released\"라고 정의된다 — Share Group의 재획득 한계다. **이 필드를 읽는 코드가 이 leaf에 없다.** validator도 registrar도 쓰지 않는다.\n29226 | \n29227 | ##### 4.2 `KafkaShareProfileValidator` — 두 거절\n29228 | \n29229 | ```java\n29230 | if (!profile.enabled()) {\n29231 | throw new MessagingCapabilityUnavailableException(\n29232 | \"KAFKA_SHARE_DISABLED\",\n29233 | \"the Kafka Share Group adapter is experimental and disabled unless \"\n29234 | + \"backend.messaging.experimental.kafka-share=true\");\n29235 | }\n29236 | if (profile.orderingScope() != OrderingScope.NONE) {\n29237 | throw new IllegalArgumentException(\n29238 | \"a Kafka share group cannot provide ordered delivery: \" + profile.destination().value());\n29239 | }\n29240 | ```\n29241 | \n29242 | **두 거절의 예외 타입이 다르다.** 첫째는 `MessagingCapabilityUnavailableException`(카테고리 `CONFIGURATION`, 안정 코드 있음), 둘째는 `IllegalArgumentException`(코드 없음). 둘 다 설정 오류인데 하나만 플랫폼 실패 어휘를 쓴다. §17.\n29243 | \n29244 | 에러 메시지가 **프로퍼티 키를 직접 적는다** — `backend.messaging.experimental.kafka-share=true`. 그 키를 읽는 코드가 이 저장소에 없다(§12.4).\n29245 | \n29246 | ##### 4.3 `KafkaShareGroupRegistrar` — spec을 받고 쓰지 않는다\n29247 | \n29248 | ```java\n29249 | public TransportConsumerRegistration register(\n29250 | KafkaShareProfile profile, TransportConsumerSpec spec) {\n29251 | Objects.requireNonNull(spec, \"spec must not be null\");\n29252 | validator.validate(profile);\n29253 | return new ShareRegistration(profile);\n29254 | }\n29255 | ```\n29256 | \n29257 | `spec`은 **null 검사만 받는다.** `ShareRegistration`은 `profile`과 `AtomicBoolean active` 둘만 갖는다.\n29258 | \n29259 | `TransportConsumerSpec`은 `(DestinationProfile profile, Function Pause and resume are refused rather than silently ignored. A share group has no partition\n29280 | * assignment to pause, so accepting the call would let a retry policy that depends on pausing\n29281 | * appear to work while doing nothing.\n29282 | ```\n29283 | \n29284 | **예외를 던지지 않고 실패한 `CompletionStage`를 반환한다** — `TransportConsumerRegistration.pause`의 반환 타입이 `CompletionStage