--- kind: CASE slug: a05-f018-sql title: 쿼리 이름을 SQL 로 옮기는 두 경로가 양쪽 끝만 있고 가운데가 없다 topic: runtime-reachability-and-composition project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:a05-f018-sql evidenceCapturedOn: 2026-09-02 assets: - key: a05-f018-sql file: ../../../final/evidence/rendered/a05-f018-sql.svg evidence: - ../../../final/evidence/raw/a05-f018-sql.txt source: - 원본 분석 절은 `final/document.md#a05` §55 의 backlog 안, 「Cross-scope P1/P2 — query SQL naming/observability composition 부재」 항목이다. 검사기의 런타임 등록이 0 이라는 판정, SQL 계층으로 잇는 다리가 확인되지 않았다는 판정, 그리고 최종 판정을 트랜잭션 관측 배선 공백과 함께 관측·설정 범위로 미룬다는 기록이 거기 있다. - 다리가 확인되지 않았다는 판정은 맞다. `QueryNameContext` 가 이름을 스레드에 묶고 검사기가 그것을 읽지만, 어떤 SessionFactory 도 그 검사기를 설치하지 않으므로 `inspect()` 는 호출되지 않는다. 코드에 있는 것은 다리가 아니라 양쪽 끝이다. 두 번째 경로인 힌트도 마찬가지다. --- # 쿼리 이름을 SQL 로 옮기는 두 경로가 양쪽 끝만 있고 가운데가 없다 쿼리 이름을 SQL 주석으로 실어 나르는 문장 검사기가 있다. 어떤 SessionFactory 도 그것을 설치하지 않아 `inspect()` 가 호출되지 않는다. 두 번째 경로인 `org.hibernate.comment` 힌트도 그것을 SQL 로 내보내는 설정이 꺼져 있다. 이름을 넣는 프로덕션 클래스 셋은 자기 단위 테스트 밖에서 쓰이지 않는다. ## 관계 - **@Bean이 있다는 것은 조립 증거가 아니다** 구현의 존재를 조립의 증거로 읽는다는 점이 같다. - **이름은 값이 아니라 registry key다** 쿼리 이름이 등록된 식별자로 쓰이는 맥락이다. - **카디널리티 경계를 타입으로 표현하기** 그 이름이 메트릭 태그가 되는 경계다. ## 문제 주석에 이름이 실려야 DBA 가 눈앞의 문장을 어느 유스케이스에서 나온 것인지 되짚는다. 검사기 javadoc 이 그 이유를 적고, 없으면 어느 엔드포인트가 이 쿼리를 내는지를 SQL 조각으로 코드베이스를 뒤져 답하게 된다고 덧붙인다. ## 결론 inspect() 는 다 쓰여 있다. 현재 스레드에 묶인 이름을 꺼내 /* 이름 */ 을 앞에 붙이고, 이름에 주석 종료자가 있으면 원본을 돌려준다. 그 분기는 도달하지 않는다. QueryName 의 형식 [a-z][a-z0-9.-]{2,95} 가 이미 * 와 / 를 막고, javadoc 도 그 검사를 additionally 라고 적는다. 단위 테스트는 없다. 검사기에도 컨텍스트에도 테스트 파일이 없다. SessionFactory 가 설치해 주지 않으면 inspect() 는 불리지 않는다. 네 경로는 저장소 루트에서 확장자 제한 없이 훑어 얻었다. 설정 키 0, HibernatePropertiesCustomizer 와 SessionFactoryBuilder 와 ServiceRegistry 와 Integrator 0, persistence.xml 0, 런타임 리소스의 FQCN 0 이다. 이름을 넣는 쪽은 배선되어 있다. QueryNameContext.with(...) 를 부르는 프로덕션 코드가 셋이다. 그 컨텍스트를 읽는 쪽은 검사기 하나뿐이다. SQL 로 이름을 옮기는 길이 그것 말고 또 있다. org.hibernate.comment 힌트를 거는 것은 리포지토리 조각 지원과 Querydsl 지원이다. 그 힌트도 스위치 하나에 달려 있는데 저장소에 그 키가 0 이다. 두 경로 다 이름을 넣는 코드와 그것을 읽는 자리는 있는데, 그 사이를 잇는 설치와 스위치가 없다. 넣는 세 클래스도 실행되지 않는다. 각 타입을 쓰는 파일은 자기 자신과 자기 단위 테스트뿐이고, 리포지토리 조각 지원을 상속하는 유일한 코드도 그 테스트 안의 중첩 클래스다. ## 검증 환경 OpenJDK : 21.0.12 확인 방식 : 설치 경로 넷을 저장소 루트에서 계수, 이름을 넣고 읽는 지점 추적, 두 번째 경로의 활성 설정 확인 소스 수정 : x ## 재현 조건 1. 검사기 구현과 javadoc, 그리고 QueryName 의 형식 정규식을 읽는다. 2. 저장소 루트에서 확장자 제한 없이 설치 경로 넷을 각각 센다. 3. 그 클래스의 단위 테스트가 있는지 찾는다. 4. 이름을 넣는 호출과 읽는 호출을 각각 센다. 5. org.hibernate.comment 힌트를 거는 곳과, use_sql_comments 를 켜는 곳을 센다. 6. 넣는 세 클래스를 쓰는 파일과 상속하는 코드를 전부 나열한다. ## 본문 쿼리 이름이 SQL 주석으로 실려야 DBA 가 `pg_stat_activity` 나 느린 쿼리 로그의 문장을 유스케이스로 되돌릴 수 있다. 검사기 javadoc 이 그 이유를 적는다. ## inspect() 가 하는 일 :::evidence key="a05-f018-sql" alt="문장 검사기의 javadoc 앞부분과 클래스 본문, 쿼리 이름의 형식 정규식, 하이버네이트가 이 구현을 설치하는 경로 넷을 저장소 루트에서 확장자 제한 없이 센 결과와 그 클래스의 단위 테스트 수, 이름을 컨텍스트에 넣는 세 곳과 읽는 곳, 이름을 SQL 로 옮기는 두 번째 경로와 그것을 활성화하는 설정의 수, 그리고 넣는 세 클래스를 쓰는 파일과 상속하는 코드를 출력한 터미널 기록." caption="검사기 javadoc 과 본문 · 이름 형식이 이미 * 와 / 를 막음 · 설치 경로 넷 전부 0 · 단위 테스트 0 · 넣는 곳 셋과 읽는 곳 하나 · 두 번째 경로의 use_sql_comments 0 · 세 클래스는 자기 테스트만 — 69줄 · exit 0" zoom="true" ::: ```java public String inspect(String sql) { if (sql == null) { return null; } Optional queryName = QueryNameContext.current(); if (queryName.isEmpty()) { return sql; } String value = queryName.get().value(); if (value.contains(COMMENT_TERMINATOR)) { return sql; } return "/* " + value + " */ " + sql; } ``` 현재 스레드에 묶인 이름을 꺼내 주석으로 붙인다. 이름에 `*/` 가 있으면 원본을 돌려주는 분기가 하나 더 있는데, `QueryName` 의 형식 `[a-z][a-z0-9.-]{2,95}` 가 이미 `*` 와 `/` 를 막으므로 도달하지 않는다. javadoc 도 그 검사를 additionally 라고 적는다. 이 클래스에는 단위 테스트가 없다. 컨텍스트 쪽도 없다. ## 검사기를 설치하는 설정이 없다 하이버네이트가 `inspect()` 를 부르려면 SessionFactory 가 이 구현을 설치해야 한다. 설치 경로 넷을 저장소 루트에서 확장자 제한 없이 셌다. ```text statement_inspector 설정 키 : 0 HibernatePropertiesCustomizer / SessionFactoryBuilder / Integrator : 0 persistence.xml : 0 런타임 리소스의 FQCN : 0 ``` ## 이름을 컨텍스트에 넣는 세 곳 `JpaStreamExecutor:63`, `JpaKeysetQuerySupport:49`, `JpaRepositoryFragmentSupport:72` 가 작업을 감싸며 이름을 컨텍스트에 넣는다. 그 컨텍스트를 읽는 코드는 검사기 하나다. 이름을 SQL 로 옮기는 경로는 하나가 더 있다. 리포지토리 조각 지원과 Querydsl 지원이 `org.hibernate.comment` 힌트를 건다. ```java return entityManager.createQuery(jpql, resultType).setHint(COMMENT_HINT, name.value()); ``` 그 힌트가 SQL 로 나오려면 `hibernate.use_sql_comments` 가 켜져야 한다. 저장소에 그 키는 0 이다. 두 경로 모두 양쪽 끝만 있고 가운데가 없다. ## 세 클래스의 프로덕션 참조 각 타입을 쓰는 파일은 자기 자신과 자기 단위 테스트뿐이다. 리포지토리 조각 지원을 상속하는 유일한 코드도 그 테스트 안의 중첩 클래스다. 스프링 데이터의 조각 규약으로 연결될 여지도 없다. 그 클래스는 인터페이스가 아니라 추상 클래스이고, `repositoryBaseClass` 도 `@NoRepositoryBean` 도 저장소에 없다. 이름은 실행 경로에 오르지 않는다. ## 확인하지 못한 것 애플리케이션을 부팅해 실제 문장에 주석이 붙지 않는 것을 관측하지 않았다. 설치 경로와 호출 지점을 센 것까지가 확인 범위다.