Sub-scope 06 (inbound-web) pagination + cursor + conditional + cache + versioning
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
generatedAt=2026-08-30T07:31:01+00:00

=== OWNED FILES ===
b71cd1d422e6254261d678475e75b5185214c6d5 main/java/dev/caskeleton/adapter/inbound/web/cache/WebCacheHeaders.java 28
31b21401e6d221ea09f728a0cbea41e566b7b573 main/java/dev/caskeleton/adapter/inbound/web/cache/WebCachePolicy.java 72
b09aadbdbd52f95dd541cb14296bb3f7e42f9466 main/java/dev/caskeleton/adapter/inbound/web/cache/WebCachePolicyCatalog.java 120
19c1f5e8e3cf501cc8c8a631768daac6c7935671 main/java/dev/caskeleton/adapter/inbound/web/cache/WebVaryPolicy.java 90
a5bfb5ad0c192fa3e10e9f9086a5ab8a7b9277f0 main/java/dev/caskeleton/adapter/inbound/web/conditional/ConditionalReadDecision.java 17
de3d849582523c68911a6519b6ff856153d51b78 main/java/dev/caskeleton/adapter/inbound/web/conditional/ConditionalReadEvaluator.java 69
34312d5aad3943a3a93f1caec13056133a4e1183 main/java/dev/caskeleton/adapter/inbound/web/conditional/ETags.java 91
0b35c91c6612a73885efa9626939aa4a809dd24e main/java/dev/caskeleton/adapter/inbound/web/conditional/EntityTag.java 83
331e63451d5f308f8fdaaae7c5c17d258bc7cc02 main/java/dev/caskeleton/adapter/inbound/web/conditional/EntityTagCodec.java 72
519b207998c1af0eb4d0b8d75d1edefd2c86e3bf main/java/dev/caskeleton/adapter/inbound/web/conditional/HttpPrecondition.java 42
dfd6b5dad1fea00ad3cadcba94d7b400ea2b8638 main/java/dev/caskeleton/adapter/inbound/web/conditional/MutationPreconditionEvaluator.java 73
7fdf339fedad25745e191825922986e31af92812 main/java/dev/caskeleton/adapter/inbound/web/conditional/PreconditionDecision.java 21
5b3648d1744a9c736f2583482ce1d2a53829ee4a main/java/dev/caskeleton/adapter/inbound/web/conditional/PreconditionFailedException.java 15
4b41fd8f9c23a1f1a11f76148b7aa6a0f14dce59 main/java/dev/caskeleton/adapter/inbound/web/conditional/WebPreconditionFailedException.java 37
c18becee86c2e798c072811562b9c61b977fa647 main/java/dev/caskeleton/adapter/inbound/web/cursor/CursorCodec.java 100
2d5ccd57029f41382d5bdbdb2ebafd071b9f65bd main/java/dev/caskeleton/adapter/inbound/web/cursor/CursorException.java 15
8563a0c09368b898f8b445e83b6f1cbd22eea7c5 main/java/dev/caskeleton/adapter/inbound/web/pagination/FilterFieldCatalog.java 79
1c81d2396816f186b6076edac156ac2699e1a9d7 main/java/dev/caskeleton/adapter/inbound/web/pagination/FilterFingerprint.java 70
6db733f43e6d5c4d60ff2c817cadc450a7a71766 main/java/dev/caskeleton/adapter/inbound/web/pagination/FilterOperatorCatalog.java 87
27eae3065306329009718da083bd540d523b758f main/java/dev/caskeleton/adapter/inbound/web/pagination/HmacWebCursorCodec.java 135
697cf50ac78f44a30e24df14cb0be66659a10971 main/java/dev/caskeleton/adapter/inbound/web/pagination/InMemoryCollectionQueryCatalog.java 93
95e0fbace4444ef787309c8cbdc16e74c22d5638 main/java/dev/caskeleton/adapter/inbound/web/pagination/PageParams.java 46
a0dad859e1433f17004f0bce2767410742c14ca3 main/java/dev/caskeleton/adapter/inbound/web/pagination/PageValidationException.java 29
cd71b9d3acb102ce9278bd74b59dd23088a5f70f main/java/dev/caskeleton/adapter/inbound/web/pagination/ProjectionProfileCatalog.java 55
3e56c25c0fa268cf2e59f157220af5628d58d22b main/java/dev/caskeleton/adapter/inbound/web/pagination/SortField.java 29
bb559644e11d9a0a7917725e51e0079d835bc0d7 main/java/dev/caskeleton/adapter/inbound/web/pagination/SortFieldCatalog.java 66
90149f3df64b5e6b812f39fd21b7e8ec875fa2a9 main/java/dev/caskeleton/adapter/inbound/web/pagination/SortParam.java 56
34d281c8750d7be823b9f0254335622c1cef1cd5 main/java/dev/caskeleton/adapter/inbound/web/pagination/UnsupportedQueryVocabularyException.java 30
b220a509ac2e5e2bf612e71af057502b85f0c680 main/java/dev/caskeleton/adapter/inbound/web/pagination/WebCollectionRequest.java 55
2d1bf71b8405769cf0d74440fea11c04b4f920ae main/java/dev/caskeleton/adapter/inbound/web/pagination/WebCollectionRequestParser.java 135
8bfea2f9fba90a1c33eee3d03a1cb5333371860c main/java/dev/caskeleton/adapter/inbound/web/pagination/WebCursorCodec.java 29
43e9006f24cdebdf033b00394c3458e2973cbd4f main/java/dev/caskeleton/adapter/inbound/web/pagination/WebCursorException.java 19
afb1197b753691f132465d1844a72cfe71b7af6b main/java/dev/caskeleton/adapter/inbound/web/pagination/WebCursorKeyRing.java 77
0344224965ee1f264bb7a2429bb9a8a0c6833fb8 main/java/dev/caskeleton/adapter/inbound/web/pagination/WebCursorPayload.java 79
46c0f5b97088991aa79a12c2607615b42aeefbbd main/java/dev/caskeleton/adapter/inbound/web/pagination/WebPageSizePolicy.java 61
4a54c62d1f0acd30cebb1f33cfaa14471cb744e2 main/java/dev/caskeleton/adapter/inbound/web/versioning/ApiDeprecationPolicy.java 60
db1397377b3cf9dd6699739f64bd9f490e43f6f1 main/java/dev/caskeleton/adapter/inbound/web/versioning/ApiVersionCatalog.java 66
161119cf579a5986eca24b7719c6f77ed0e24ac4 main/java/dev/caskeleton/adapter/inbound/web/versioning/DeprecatedRoute.java 51
2a99176fdffcb03047da758c875e910e8860b5fb main/java/dev/caskeleton/adapter/inbound/web/versioning/DeprecationHeaderWriter.java 70
b442226238e93850b2b2b4e02e2b2c9079fc389d main/java/dev/caskeleton/adapter/inbound/web/versioning/PathApiVersionResolver.java 60
582bc80f24caeeb01699e95ce3859844ff8e2009 main/java/dev/caskeleton/adapter/inbound/web/versioning/SunsetViolationException.java 18
9dd5f14e03bc8c14c946bc66ee55a8ab8bc0b78d main/java/dev/caskeleton/adapter/inbound/web/versioning/UnsupportedApiVersionException.java 39
26adae975785892947b14a2b090828c183bc45ba test/java/dev/caskeleton/adapter/inbound/web/cache/WebCachePolicyTest.java 143
3063ff9bf667b0596c85a97c8fedbf2dfc68ae41 test/java/dev/caskeleton/adapter/inbound/web/conditional/ConditionalReadEvaluatorTest.java 88
ca4ee2a080a1c9b4ba13ca8c47ec82b887a11548 test/java/dev/caskeleton/adapter/inbound/web/conditional/ETagsTest.java 54
3449f0cf36ff50de2b6ed038f0867ac57aa173fb test/java/dev/caskeleton/adapter/inbound/web/conditional/MutationPreconditionEvaluatorTest.java 99
17818d69f9261bc514adeaa3d27c66ba40a1a81a test/java/dev/caskeleton/adapter/inbound/web/cursor/CursorCodecTest.java 57
af94a22156b65312fea18626299972f384d76f35 test/java/dev/caskeleton/adapter/inbound/web/pagination/CollectionQueryCatalogTest.java 137
22fc35f5e84c63304d849452911fd4a3c999c3e4 test/java/dev/caskeleton/adapter/inbound/web/pagination/HmacWebCursorCodecTest.java 170
e35baf04168287731a6e3a22a6865d9cc5260941 test/java/dev/caskeleton/adapter/inbound/web/pagination/PageParamsTest.java 67
355be4b68c4037d8bae2977bbac44613c6d537d4 test/java/dev/caskeleton/adapter/inbound/web/pagination/SortParamTest.java 51
194055a24da33f232cb574de64e3d5b9fda50763 test/java/dev/caskeleton/adapter/inbound/web/pagination/WebCollectionRequestParserTest.java 156
c9b51117a9f8e308717cc9155d942e2513d663d2 test/java/dev/caskeleton/adapter/inbound/web/versioning/DeprecationHeaderWriterTest.java 107
9fe7484c631e85db319564b63cd1eb0b3d991ea4 test/java/dev/caskeleton/adapter/inbound/web/versioning/PathApiVersionResolverTest.java 82
file_count=54

=== 8.1 도달성: 라이브러리 타입의 소비자 ===
HmacWebCursorCodec                    5 test 
CursorCodec                           2 test 
WebCollectionRequestParser            1 test 
ConditionalReadEvaluator              1 test 
MutationPreconditionEvaluator         1 test 
ETags                                 3 sample      12 test 
WebCachePolicyCatalog                10 test 
WebVaryPolicy                         4 main       8 test 
ApiVersionCatalog                     4 test 
DeprecationHeaderWriter               1 test 
PathApiVersionResolver                2 test 
WebPageSizePolicy                     1 test 

=== 8.2 조건 형제 비교: 두 개의 캐시 정책 메커니즘 ===
$ cat adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/filter/CacheControlFilter.java | grep -vE '^import|^package'


/**
 * Applies the skeleton's default HTTP cache policy: every response gets {@code Cache-Control:
 * no-store} and a {@code Vary: Accept, Accept-Encoding, Authorization} header. Defaults are set
 * before the chain so a cacheable endpoint can opt in by returning a {@code ResponseEntity} whose
 * {@code Cache-Control} header overwrites the default. See README for the design rationale.
 */
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 20)
public class CacheControlFilter extends OncePerRequestFilter {

  static final String DEFAULT_CACHE_CONTROL = "no-store";
  static final String DEFAULT_VARY = "Accept, Accept-Encoding, Authorization";

  @Override
  protected void doFilterInternal(
      HttpServletRequest request, HttpServletResponse response, FilterChain chain)
      throws ServletException, IOException {
    response.setHeader(ApiHeaders.CACHE_CONTROL, DEFAULT_CACHE_CONTROL);
    response.setHeader(ApiHeaders.VARY, DEFAULT_VARY);
    chain.doFilter(request, response);
  }
}
exit=0

$ grep -rn 'web\.cache\.' --include=*.java adapter/inbound/web/src app-bootstrap/src sample-portfolio/src | grep -v '/cache/' | wc -l
0
exit=0

=== 8.3 no-store 와 조건부 읽기의 충돌 ===
$ grep -n 'IF_NONE_MATCH\|NOT_MODIFIED\|eTag(' sample-portfolio/src/main/java/dev/caskeleton/sample/portfolio/adapter/inbound/web/controller/WorkLogController.java
141:      @RequestHeader(value = ApiHeaders.IF_NONE_MATCH, required = false) String ifNoneMatch) {
145:      return ResponseEntity.status(HttpStatus.NOT_MODIFIED).eTag(etag).build();
147:    return ResponseEntity.ok().eTag(etag).body(WorkLogWebMapper.toResponse(workLog));
159:    return ResponseEntity.ok().eTag(etagOf(created)).body(WorkLogWebMapper.toResponse(created));
174:    return ResponseEntity.ok().eTag(etagOf(updated)).body(WorkLogWebMapper.toResponse(updated));
exit=0

$ grep -rn 'CacheControl\|CACHE_CONTROL' --include=*.java sample-portfolio/src/main | wc -l
0
exit=0

=== 8.4 예외 타입의 throw 지점과 handler ===
PageValidationException              main throw sites=10
CursorException                      main throw sites=7
UnsupportedQueryVocabularyException  main throw sites=6
UnsupportedApiVersionException       main throw sites=0
SunsetViolationException             main throw sites=3
WebCursorException                   main throw sites=10
$ grep -n 'ExceptionHandler(PageValidationException\|ExceptionHandler(CursorException\|ExceptionHandler(PreconditionFailedException' adapter/inbound/web/src/main/java/dev/caskeleton/adapter/inbound/web/error/GlobalExceptionHandler.java
157:  @ExceptionHandler(PreconditionFailedException.class)
169:  @ExceptionHandler(PageValidationException.class)
180:  @ExceptionHandler(CursorException.class)
exit=0

