배경
테스트 규약은 TestConventionTest(src/test 스캔, import 텍스트 매칭)가 기계 강제해 위반 0건이다. 반면 src/main 규약(== null 금지·logger 패턴·FK 금지·JPA 연관 금지·class-level @Transactional 금지·도메인 예외 네이밍)은 대부분 산문(CLAUDE.md)뿐이라, 실측상 드리프트가 존재했다.
== null/!= null 은 이미 PostToolUse hook 으로 막지만, Claude 편집 시점만 걸리고 사람 직접 편집·CI 에선 안 걸린다.
목표
src/main 규약 중 오탐 없이 기계 판정 가능한 것을 CI 게이트(./gradlew test)로 승격한다 (CLAUDE.local.md "층" 원칙: 결정론 층으로 내린다).
경로 A — MainConventionTest (텍스트 스캔, 의존성 0)
TestConventionTest 와 같은 방식(File("src/main/kotlin").walkTopDown() + 정규식). 대상:
- JPA 연관 어노테이션 금지 (
@ManyToOne·@OneToMany·@OneToOne·@JoinColumn)
- logger 패턴 (
getLogger(javaClass) 외 금지)
- 마이그레이션 FK 금지 + 파일명
V{14자리}__snake.sql
경로 B — Konsist (AST, 의존성 추가)
텍스트로 오탐 나는 것:
== null/!= null 금지 (문자열·주석 오탐 위험 — 이번에 hook 이 주석을 오탐한 사례 있음)
- class-level
@Transactional 금지
- 도메인 예외 private constructor
선행 — 코드 드리프트 3건 수정
게이트 켜기 전 먼저 고쳐야 CI 가 안 터진다:
MetricsService.kt 복합 null 분기 → guard clause 분해
JwtProvider.kt·RedisRefreshTokenStore.kt top-level logger(::class.java) → class 내부 getLogger(javaClass)
순서
드리프트 3건 수정 → 경로 A 게이트 → (필요 시) 경로 B
타이밍 주의
dev 가 빠르게 변하는 중이라 게이트 도입이 in-flight PR 을 막을 수 있다. 검사 4종은 이미 팀이 지키는 관례(현 위반 0)라 리스크는 낮으나, dev 안정 시점을 고려한다.
참고
이번 세션에 경로 A + 드리프트 3건을 구현·검증(전체 test BUILD SUCCESSFUL)까지 했으나, dev 급변 타이밍상 코드는 폐기하고 이 이슈로 재구현하기로 함.
배경
테스트 규약은
TestConventionTest(src/test 스캔, import 텍스트 매칭)가 기계 강제해 위반 0건이다. 반면 src/main 규약(== null금지·logger 패턴·FK 금지·JPA 연관 금지·class-level@Transactional금지·도메인 예외 네이밍)은 대부분 산문(CLAUDE.md)뿐이라, 실측상 드리프트가 존재했다.== null/!= null은 이미 PostToolUse hook 으로 막지만, Claude 편집 시점만 걸리고 사람 직접 편집·CI 에선 안 걸린다.목표
src/main 규약 중 오탐 없이 기계 판정 가능한 것을 CI 게이트(
./gradlew test)로 승격한다 (CLAUDE.local.md "층" 원칙: 결정론 층으로 내린다).경로 A — MainConventionTest (텍스트 스캔, 의존성 0)
TestConventionTest와 같은 방식(File("src/main/kotlin").walkTopDown()+ 정규식). 대상:@ManyToOne·@OneToMany·@OneToOne·@JoinColumn)getLogger(javaClass)외 금지)V{14자리}__snake.sql경로 B — Konsist (AST, 의존성 추가)
텍스트로 오탐 나는 것:
== null/!= null금지 (문자열·주석 오탐 위험 — 이번에 hook 이 주석을 오탐한 사례 있음)@Transactional금지선행 — 코드 드리프트 3건 수정
게이트 켜기 전 먼저 고쳐야 CI 가 안 터진다:
MetricsService.kt복합 null 분기 → guard clause 분해JwtProvider.kt·RedisRefreshTokenStore.kttop-levellogger(::class.java) → class 내부getLogger(javaClass)순서
드리프트 3건 수정 → 경로 A 게이트 → (필요 시) 경로 B
타이밍 주의
dev 가 빠르게 변하는 중이라 게이트 도입이 in-flight PR 을 막을 수 있다. 검사 4종은 이미 팀이 지키는 관례(현 위반 0)라 리스크는 낮으나, dev 안정 시점을 고려한다.
참고
이번 세션에 경로 A + 드리프트 3건을 구현·검증(전체 test BUILD SUCCESSFUL)까지 했으나, dev 급변 타이밍상 코드는 폐기하고 이 이슈로 재구현하기로 함.