# 문서가 무엇을 막겠다고 하는가 — 두 가지다 7:/** 8: * The rules a dynamic {@link Specification} query must satisfy before it runs (design §23.2). 9: * 10: *

A specification with no predicate is a full table scan wearing a builder's clothing. It is 11: * usually the result of every optional filter being absent — a search screen submitted empty — and 12: * it looks harmless in code review because no single line is wrong. Requiring either a predicate or 13: * an explicit "yes, scan everything" token makes that case a decision instead of an accident. 14: * 15: *

A page bound is required for the same reason: an unbounded collection query hydrates whatever 16: * the table happens to hold today. 17: */ # 실제 검사 31: public static void requireBounded( 32: Specification specification, Pageable pageable, String allowUnboundedToken) { 33: Objects.requireNonNull(pageable, "pageable"); 34: if (pageable.isUnpaged()) { 35: throw new IllegalArgumentException( 36: "a specification query requires a bounded page; unpaged reads whatever the table holds"); 37: } 38: if (specification == null && !ALLOW_UNBOUNDED_TOKEN.equals(allowUnboundedToken)) { 39: throw new IllegalArgumentException( 40: "a specification query requires a bounded predicate, or the explicit '" 41: + ALLOW_UNBOUNDED_TOKEN 42: + "' token"); 43: } 44: } 45: 46: /** Fails when the specification has no predicate at all. */ 47: public static void requirePredicate(Specification specification) { 48: if (specification == null) { 49: throw new IllegalArgumentException("a specification query requires a bounded predicate"); 50: } 51: } # 이 타입을 언급하는 곳 (레포 전체, 언어 무관) docs/superpowers/plans/2026-08-11-jpa-persistence-platform-implementation-plan.md:1848:- Create: `modules/jpa/jpa-spring-data/src/main/java/io/backend docs/superpowers/plans/2026-08-11-jpa-persistence-platform-implementation-plan.md:1922:git add 'modules/jpa/jpa-querydsl/src/main/java/io/backend/skel docs/architecture/jpa-api-surface.txt:321:dev.caskeleton.adapter.outbound.persistence.springdata.SpecificationPolicy docs/study/postgresql-jpa-walkthrough.ko.md:874:`SpecificationPolicy`: docs/study/postgresql-jpa-walkthrough.ko.md:3249:SpecificationPolicy docs/study/postgresql-jpa-walkthrough.ko.md:3257:grep -rn "UpsertExecutor\|WorkClaimExecutor\|RangeQuerySupport\|SpecificationPolicy\|PgRangeJdbcType" src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/SpecificationPolicy.java:18:public final cla src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/SpecificationPolicy.java:23: private Specif # 두 검사 메서드를 부르는 코드 (정적 임포트 포함, 레포 전체): 0 # 같은 리프의 형제 정책. 같은 토큰을 쓰고 같은 널 비교를 한다. adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/springdata/SpecificationPolicy.java:21: public static final String ALLOW_UNBOUNDED_TOKEN = "allow-unbounded-scan"; adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/querydsl/PredicatePolicy.java:20: public static final String ALLOW_UNBOUNDED_TOKEN = "allow-unbounded-scan"; 29: public static void requireBounded(Predicate predicate, QueryPage page) { 30: Objects.requireNonNull(page, "page"); 31: if (predicate == null && !page.allowsUnboundedScan()) { 32: throw new IllegalArgumentException( 33: "a collection query requires a bounded predicate, or the explicit '" 34: + ALLOW_UNBOUNDED_TOKEN 35: + "' token"); 36: } # 형제 쪽 호출처와 그것을 부르는 테스트 adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/querydsl/QuerydslJpaSupportTest.java:17: assertThatThrownBy(() -> PredicatePolicy.requireBounded(null, QueryPage.of(0, 100))) adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/querydsl/QuerydslJpaSupportTest.java:25: assertThatCode(() -> PredicatePolicy.requireBounded(null, QueryPage.unboundedScan(0, 100))) adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/querydsl/QuerydslJpaSupport.java:42: PredicatePolicy.requireBounded(predicate, page);