# 열거형이 세운 규칙과 상수
/**
 * The web capabilities that are not part of Stable, each behind its own flag.
 *
 * <p>One flag per capability, not one for "advanced". They have nothing in common operationally:
 * virtual threads change how every request is scheduled, streaming changes how long a response
 * holds a connection, XML adds a parser with a decades-long history of entity-expansion attacks. A
 * single switch would make those one decision, and a deployment that wanted the first would be
 * given the third.
 *
 * <p>Every constant is off unless named.
 */
  선언된 상수 수 : 12
  24:  MVC_VIRTUAL_THREADS,
  27:  WEBFLUX_BLOCKING_BRIDGE,
  30:  JSON_MERGE_PATCH,
  33:  JSON_PATCH,
  36:  SSE,
  39:  NDJSON,
  42:  JSON_SEQUENCE,
  45:  FUNCTIONAL_WEBFLUX,
  48:  CBOR,
  51:  XML,
  54:  OPENAPI_32,
  57:  RATELIMIT_DRAFT_HEADERS;
  각 상수가 발행하는 속성 이름 :
  /** The property that turns this on. */
  public String propertyName() {
    return "backend.web.advanced." + name().toLowerCase(Locale.ROOT).replace('_', '-') + ".enabled";
  }


# main 에 그 접두사가 나오는 자리 전부. 만드는 쪽과 읽는 쪽이 섞여 있다
  VirtualThreadProfile.java:76     return "backend.web.advanced.virtual-threads.enabled";
  MvcStreamingExecutorConfiguration.java:37     prefix = "backend.web.advanced.ndjson",
  VirtualThreadSettings.java:10  * Settings bound from {@code backend.web.advanced.mvc-virtual-threads.*}.
  VirtualThreadSettings.java:17 @ConfigurationProperties(prefix = "backend.web.advanced.mvc-virtual-threads")
  VirtualThreadMvcConfiguration.java:33     prefix = "backend.web.advanced.mvc-virtual-threads",
  WebAdvancedFeature.java:61     return "backend.web.advanced." + name().toLowerCase(Locale.ROOT).replace('_', '-') + ".enabled";
  그중 게이트나 바인딩인 셋 :
17:@ConfigurationProperties(prefix = "backend.web.advanced.mvc-virtual-threads")
33:    prefix = "backend.web.advanced.mvc-virtual-threads",
34-    name = "enabled",
35-    havingValue = "true")
36-public class VirtualThreadMvcConfiguration {
37:    prefix = "backend.web.advanced.ndjson",
38-    name = "enabled",
39-    havingValue = "true")
40-public class MvcStreamingExecutorConfiguration {
  이름을 만들기만 하는 둘 :
  public String propertyName() {
    return "backend.web.advanced." + name().toLowerCase(Locale.ROOT).replace('_', '-') + ".enabled";
  /** The property that turns this on. */
  public static String propertyName() {
    return "backend.web.advanced.virtual-threads.enabled";
  }

# 두 스위치를 켜면 각각 무엇이 생기는가
   * <p>Named {@code applicationTaskExecutor} because that is the bean Spring Boot's MVC async
   * support looks for. A differently named bean is created, is never used, and leaves the container
   * default in place — which is the failure mode where the whole capability is switched on and
   * nothing changes.
   */
  @Bean("applicationTaskExecutor")
  @ConditionalOnMissingBean(name = "applicationTaskExecutor")
  그런데 그 이름을 출하 조립 경로가 이미 쥐고 있다 :
16: * with a bounded pool; naming the bean {@value #EXECUTOR_BEAN_NAME} backs off Boot's auto-config
19:@Configuration
25:  public static final String EXECUTOR_BEAN_NAME = "applicationTaskExecutor";
40:  @Bean(name = EXECUTOR_BEAN_NAME)
41:  @Primary
42:  ThreadPoolTaskExecutor applicationTaskExecutor(
52:    executor.setRejectedExecutionHandler(new LoggingAbortPolicy(EXECUTOR_BEAN_NAME, metrics));
57:        EXECUTOR_BEAN_NAME, () -> executor.getThreadPoolExecutor().getQueue().size());
  컴포넌트 스캔 제외 정규식에 bootstrap.async 가 있는가 : 0
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnProperty(
    prefix = "backend.web.advanced.ndjson",
    name = "enabled",
    havingValue = "true")
public class MvcStreamingExecutorConfiguration {
72:    return new MvcStreamWriter(encoder, StreamFraming.NDJSON);
84:    return new MvcStreamWriter(encoder, StreamFraming.JSON_SEQUENCE);
  그 두 미디어 타입을 내는 main 라우트 : 0
  MvcStreamWriter 를 advanced 밖에서 부르는 main 코드 : 0

# 레인이 무엇을 고르고 그 안에서 무엇을 단언하는가
tasks.register('webAdvancedTest', Test) {
    group = 'verification'
    description = 'Runs every web Advanced capability contract.'
    testClassesDirs = sourceSets.test.output.classesDirs
    classpath = sourceSets.test.runtimeClasspath
    useJUnitPlatform {
        includeTags 'web-advanced'
    }
    failOnNoDiscoveredTests = true
  그 태그를 단 시험 클래스 수 : 22
  그 파일에서 속성을 참으로 두는 자리. 셋은 만들어지는 빈을, 둘은 기동 실패를 단언한다 :
43:            "backend.web.advanced.mvc-virtual-threads.enabled=true",
72:        .withPropertyValues("backend.web.advanced.ndjson.enabled=true")
92:            "backend.web.advanced.mvc-virtual-threads.enabled=true",
117:            "backend.web.advanced.mvc-virtual-threads.enabled=true",
136:        .withPropertyValues("backend.web.advanced.mvc-virtual-threads.enabled=true")
  그중 켠 뒤 빈을 확인하는 자리 :
        .withPropertyValues("backend.web.advanced.ndjson.enabled=true")
        .withUserConfiguration(MvcStreamingExecutorConfiguration.class)
        .run(
            context ->
                assertThat(context)
                    .hasNotFailed()
                    .hasSingleBean(WebStreamPolicy.class)
                    .hasSingleBean(WebStreamRegistry.class)
                    .hasSingleBean(StreamRecordEncoder.class)
                    .hasBean("mvcNdjsonWriter")
                    .hasBean("mvcJsonSequenceWriter"));

# 롤백 시험 일곱이 무엇을 단언하는가
49:  void nothingIsCreatedWithNoFlags() {
67:  void explicitFalseCreatesNothing() {
86:  void flagsReportStablePreservation() {
103:  void codecsOffLeaveOnlyJson() {
119:  void draftHeadersOffWriteNothing() {
134:  void functionalRoutingOffServesNothing() {
146:  void everyGateDemandsRollbackEvidence() {
  속성 상태를 다루는 둘 — 하나는 값을 두지 않고, 하나는 거짓으로 둔다 :
  void nothingIsCreatedWithNoFlags() {
    // No property values at all — the shape a Stable deployment actually has.
    runner
        .withUserConfiguration(
        .withPropertyValues(
            "backend.web.advanced.ndjson.enabled=false",
            "backend.web.advanced.mvc-virtual-threads.enabled=false")
        .withUserConfiguration(
  플래그 값을 쓰는 유일한 시험 전문 :
  void flagsReportStablePreservation() {
    assertThat(
            dev.caskeleton.adapter.inbound.web.advanced.WebAdvancedFeatureFlags.none()
                .stableBehaviourPreserved())
        .isTrue();
    for (WebAdvancedFeature feature : WebAdvancedFeature.values()) {
      boolean preserved =
          dev.caskeleton.adapter.inbound.web.advanced.WebAdvancedFeatureFlags.of(feature)
              .stableBehaviourPreserved();
      assertThat(preserved)
          .as("%s claims to leave unrelated requests alone", feature)
          .isEqualTo(!feature.affectsUnrelatedRequests());
    }
  }

44:  public boolean stableBehaviourPreserved() {
45-    return enabled.stream().noneMatch(WebAdvancedFeature::affectsUnrelatedRequests);
46-  }

# 롤백 검증이 대상으로 삼는 값
22:  public static WebAdvancedFeatureFlags none() {
27:  public static WebAdvancedFeatureFlags of(WebAdvancedFeature... features) {
  그 타입을 부르는 자리 전부 :
  test · WebAdvancedReleaseTest.java:21    WebAdvancedFeatureFlags flags = WebAdvancedFeatureFlags.none();
  test · WebAdvancedReleaseTest.java:37    assertThat(WebAdvancedFeatureFlags.of(WebAdvancedFeature.CBOR).stableBehaviourPreserved())
  test · WebAdvancedReleaseTest.java:40            WebAdvancedFeatureFlags.of(WebAdvancedFeature.MVC_VIRTUAL_THREADS)
  test · WebAdvancedRollbackIT.java:88            dev.caskeleton.adapter.inbound.web.advanced.WebAdvancedFeatureFlags.none()
  test · WebAdvancedRollbackIT.java:93          dev.caskeleton.adapter.inbound.web.advanced.WebAdvancedFeatureFlags.of(feature)

# 그 값을 돌리는 레인과, 속성 이름을 단언하는 시험
  .github/workflows/web-advanced-nightly.yml:46:          :adapter:inbound:web:webAdvancedTest
  .github/workflows/web-advanced-release.yml:53:          :adapter:inbound:web:webAdvancedTest
  test · ResumeTokenTest.java:187     assertThat(WebSocketAdvancedCapability.RESUME.propertyName())
  main · WebSocketAdvancedCapability.java:75   public String propertyName() {
  test · WebAdvancedReleaseTest.java:48     assertThat(WebAdvancedFeature.JSON_MERGE_PATCH.propertyName())
  main · VirtualThreadProfile.java:75   public static String propertyName() {
  main · WebAdvancedFeature.java:60   public String propertyName() {
