--- title: "HikariCP knob 간 제약을 Spring Boot 시작 guard 로 강제하는 패턴" source_type: blog-topic status: raw related_branches: [feature-database-connection-pool-contract] related_projects: [ca-tmpl] tags: [blog-topic, ca-tmpl, hikaricp, spring-boot, startup-validation, connection-pool, clean-architecture] created: 2026-06-09 status_label: ready-for-canonical target_audience: backend-engineer inspiration_url: archive_url: --- # HikariCP inter-knob constraints as a Spring Boot startup guard ## Parent - [[raw/branch-notes/feature-database-connection-pool-contract]] ## 트리거 / Trigger - 트리거 유형: `branch-work` - 트리거 날짜: 2026-06-09 - 트리거 연결 노트: [[raw/branch-notes/feature-database-connection-pool-contract]] ## 글감 / Topic seed - 한 문장 요지: HikariCP knob 간 제약을 runtime 경고에 맡기지 않고 Spring Boot startup guard로 수집해 fail-fast시키는 패턴이다. - 예상 제목 후보: - HikariCP 설정 오류를 startup에서 잡기 - Connection pool knob 제약을 Spring Boot guard로 고정하기 ## 핵심 주장 후보 / Claim candidates - 사실 후보: - `validationTimeout < connectionTimeout`, `keepaliveTime < maxLifetime` 같은 inter-knob 제약이 있다. - `SmartInitializingSingleton`과 `ApplicationContextRunner`로 startup guard를 검증할 수 있다. - 의견/해석 후보: - pool 설정 오류는 traffic을 받기 전 startup phase에서 실패시키는 편이 운영적으로 더 명확하다. ## Outline seed 1. HikariCP knob 간 제약과 runtime 경고/reset의 한계를 정리한다. 2. String 기반 defensive parse로 Duration drift를 안전하게 처리한다. 3. `ApplicationContextRunner`로 guard failure를 작은 테스트로 고정한다. ## 핵심 아이디어 HikariCP 에는 knob 간 순서 제약이 있다: - `validationTimeout < connectionTimeout` - `keepaliveTime < maxLifetime` - `connectionTimeout >= 250 ms` - `leakDetectionThreshold >= 2000 ms` (0 = disabled 허용) 이 제약들은 HikariCP 내부에서 경고 또는 reset 으로만 처리되고, 설정 오류가 runtime 에서만 드러나는 경우가 많다. `SmartInitializingSingleton` + `Environment.getProperty(key)` (String, not typed) 패턴으로 context refresh 완료 직전에 모든 위반을 한꺼번에 수집해 `IllegalStateException` 으로 boot fail 시키면, 잘못된 pool 설정이 prod 에 배포되는 것을 막을 수 있다. ## 흥미로운 구현 포인트 ### Defensive parseMillis (CONNECTION_TIMEOUT_FORMAT_DRIFT) `environment.getProperty("spring.datasource.hikari.connection-timeout", Long.class)` 는 env-keys.yaml default 가 `"5s"` (Duration string) 일 때 `ConversionFailedException` 을 던진다. 대신 `getProperty(key)` 로 String 을 받아 `Long.parseLong(raw.trim())` + `NumberFormatException catch → return null` 패턴으로 방어 파싱하면: 1. 숫자 ms 값은 정상 검증 2. Duration string 은 null (absent 취급) — 크래시 없이 skip 3. 명세에서 두 포맷이 공존하는 drift 환경에서 안전 ### ApplicationContextRunner 기반 단위 테스트 `@SpringBootTest` 없이 `ApplicationContextRunner.withUserConfiguration(ValidatorConfig.class)` 만으로 `SmartInitializingSingleton` 의 `afterSingletonsInstantiated()` 가 호출된다. `context.hasFailed()` + `context.getStartupFailure().hasStackTraceContaining(...)` 으로 각 위반 케이스를 격리 검증. ## 글감 방향 - Spring Boot startup contract 패턴 시리즈 (`SmartInitializingSingleton` vs `ApplicationListener` vs `@PostConstruct`) - HikariCP 운영에서 놓치기 쉬운 knob 간 제약 총정리 - "설정 오류를 runtime 이 아닌 startup 에서 잡는다" 원칙의 구현 패턴들 ## Canonical 전환 후보 / Canonical extraction candidates - `wiki/projects/ca-tmpl/data-layer-persistence-cache-outbound.md` 후보: - HikariCP inter-knob constraint startup guard를 data-layer/pool configuration 글감으로 연결. - 필요한 추가 검증: - 실제 validator class, `ApplicationContextRunner` 테스트, env duration drift 처리 범위. ## Sources / 근거 후보 - [[raw/branch-notes/feature-database-connection-pool-contract]] ## 미해결 / Unknown - 아직 확인해야 할 사실: 현재 ca-tmpl 코드에 startup guard와 관련 테스트가 존재하는지. - 과장하면 안 되는 부분: HikariCP 자체가 모든 오류를 방치한다고 쓰지 않고, ca-tmpl에서 선택한 fail-fast 보강으로 제한한다. ## Related / 관련 - 관련 branch: [[raw/branch-notes/feature-database-connection-pool-contract]] ## Decision / 처리 결정 - 액션: `promote-to-canonical` - 이유: `wiki/projects/ca-tmpl/data-layer-persistence-cache-outbound.md` 에 HikariCP startup guard 글감으로 반영했다. - 다음 단계: target canonical이 아직 `draft` 이므로 `blogify` 전 pool guard 구현·검증 여부를 branch-note/code 기준으로 확인한다.