LanguageCache에서 @PostConstruct를 사용하여 언어 데이터를 캐싱하려 했으나,
languageRepository.findAll()이 빈 결과를 반환하는 문제 발생.
@PostConstruct
public void init() {
languageRepository.findAll().forEach(language -> {
languageCodeToIdMap.put(language.getLanguageCode(), language.getId());
});
log.info("Loaded {} languages into cache", languageCodeToIdMap.size()); // 0개 출력
}
┌─────────────────────────────────────────────────────────────────┐
│ 1. Spring Container 시작 │
├─────────────────────────────────────────────────────────────────┤
│ 2. Bean 생성 및 의존성 주입 (DI) │
├─────────────────────────────────────────────────────────────────┤
│ 3. @PostConstruct 실행 ◀── 이 시점에 DB 데이터 없음! │
├─────────────────────────────────────────────────────────────────┤
│ 4. Hibernate DDL 실행 (테이블 생성) │
├─────────────────────────────────────────────────────────────────┤
│ 5. SQL 스크립트 실행 (데이터 INSERT) │
│ └── defer-datasource-initialization: true 설정 필요 │
├─────────────────────────────────────────────────────────────────┤
│ 6. ApplicationReadyEvent 발생 ◀── 이 시점에 DB 데이터 있음! │
└─────────────────────────────────────────────────────────────────┘
jpa:
hibernate:
ddl-auto: create-drop
defer-datasource-initialization: true # DDL 이후에 SQL 스크립트 실행
sql:
init:
mode: always
data-locations:
- classpath:local-init/seed/01_languages.sql
- classpath:local-init/seed/02_currencies.sql
# ...
defer-datasource-initialization: true의 역할| 설정 | 실행 순서 |
|---|---|
false (기본값) | SQL 스크립트 → Hibernate DDL (실패: 테이블 없음) |
true | Hibernate DDL → SQL 스크립트 (성공: 테이블 있음) |
주의: 이 설정과 관계없이 @PostConstruct는 DDL/SQL 스크립트보다 먼저 실행됨.
ApplicationStartingEvent
↓
ApplicationEnvironmentPreparedEvent
↓
ApplicationContextInitializedEvent
↓
ApplicationPreparedEvent
↓
ContextRefreshedEvent ← @PostConstruct 실행 시점
↓
ApplicationStartedEvent
↓
ApplicationReadyEvent ← @EventListener(ApplicationReadyEvent.class) 실행 시점
↓
(애플리케이션 실행 중...)
import jakarta.annotation.PostConstruct;
@PostConstruct
public void init() {
// DB 데이터 없음 - 실패
}
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
@EventListener(ApplicationReadyEvent.class)
public void init() {
// 모든 초기화 완료 후 실행 - 성공
}
| 방법 | 실행 시점 | SQL 스크립트 이후 실행? | 사용 사례 |
|---|---|---|---|
@PostConstruct | Bean 초기화 직후 | ❌ | 외부 의존성 없는 초기화 |
CommandLineRunner | 애플리케이션 시작 후 | ✅ | CLI 인자 처리 |
ApplicationRunner | 애플리케이션 시작 후 | ✅ | ApplicationArguments 필요 시 |
@EventListener(ApplicationReadyEvent.class) | 완전히 준비된 후 | ✅ | DB 데이터 의존 초기화 |
@PostConstruct는 Bean 생성 직후 실행되므로 DB 초기 데이터에 의존하는 로직에는 부적합defer-datasource-initialization: true 설정은 DDL과 SQL 스크립트 순서만 조정할 뿐, @PostConstruct 실행 시점에는 영향 없음@EventListener(ApplicationReadyEvent.class) 사용 권장
감동적인 글이네요