이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Bean 생성자는 언제 호출되고 초기화 callback은 언제 실행되는가?
@PostConstruct,InitializingBean, init method는 어떤 위치의 도구인가?- 소멸 callback은 어떤 Scope에서 특히 주의해야 하는가?
개요
Spring Bean의 생명주기는 단순한 생성자 호출이 아니다. 컨테이너는 Bean을 만들고, 의존성을 주입하고, aware callback과 post processor를 적용하고, 초기화 callback을 실행한다. 컨테이너 종료 시점에는 singleton Bean의 destruction callback도 처리한다.
이 흐름을 이해하면 @PostConstruct가 왜 생성자와 다른지, AOP Proxy가 왜 Bean 생성 흐름과 연결되는지, prototype Bean의 정리를 왜 조심해야 하는지 설명할 수 있다.
원리
대표적인 singleton Bean 생명주기는 다음과 같다.
Bean 인스턴스 생성
의존성 주입
Aware callback
BeanPostProcessor before initialization
초기화 callback
BeanPostProcessor after initialization
사용
컨테이너 종료
소멸 callback실제 내부 흐름은 더 복잡하지만 학습 관점에서는 이 순서가 중요하다. 특히 초기화 callback은 의존성 주입이 끝난 뒤 실행된다. 다만 초기화 callback은 대개 AOP proxy가 완전히 노출되기 전의 원본 Bean을 대상으로 실행되므로, 초기화 메서드 안에서 자기 자신의 @Transactional 같은 AOP 적용 메서드를 호출해도 기대한 Advice가 적용된다고 보면 안 된다.
초기화 callback
초기화 callback에는 여러 방식이 있다.
@Component
public class CacheWarmUp {
@PostConstruct
public void init() {
// 캐시 준비, 설정 검증
}
}@PostConstruct는 가장 흔한 방식이며 Spring 전용 인터페이스에 덜 묶인다는 장점이 있다. InitializingBean.afterPropertiesSet()이나 @Bean(initMethod = "...")도 사용할 수 있지만, InitializingBean은 코드가 Spring API에 직접 의존한다. 어떤 방식을 쓰든 비즈니스 로직을 자동 실행하는 데 남용하면 애플리케이션 시작과 업무 흐름이 뒤섞인다.
소멸 callback
소멸 callback은 컨테이너가 종료될 때 리소스를 정리하는 데 사용한다.
@Component
public class ExternalClientHolder {
@PreDestroy
public void close() {
// connection, thread pool, file handle 정리
}
}소멸 callback은 singleton Bean에서 자연스럽다. prototype Bean은 컨테이너가 생성과 주입까지만 관여하고 이후의 전체 소멸 관리는 호출자 책임에 가까우므로 주의해야 한다.
생성자와 초기화의 역할 구분
생성자는 객체의 불변 필드와 필수 의존성을 받는 자리다. 초기화 callback은 의존성 주입 이후에 필요한 준비 작업을 수행하는 자리다. 공식 문서 기준으로도 초기화 callback은 설정 검증이나 내부 자료구조 준비에 어울리며, 외부 Bean을 다시 호출해 복잡한 런타임 흐름을 시작하는 위치로 보기는 어렵다.
생성자에서 너무 많은 외부 작업을 하면 테스트가 어려워지고 객체 생성이 무거워진다. 반대로 초기화 callback에 핵심 상태 검증을 모두 미루면 객체의 필수 조건이 생성자에서 드러나지 않는다.
실전 팁
- 생성자는 필수 상태와 의존성을 받는 데 집중한다.
@PostConstruct는 설정 검증과 가벼운 준비 작업에 사용한다.- 초기화 callback 안에서 자기 자신의 AOP 적용 메서드를 호출해 트랜잭션이나 보안 Advice가 적용될 것이라고 기대하지 않는다.
- 외부 API 호출, 데이터 대량 변경, 배치성 업무는 초기화 callback보다 명시적인 runner나 별도 작업으로 분리한다.
@PreDestroy에는 반드시 idempotent한 정리 코드를 두는 편이 좋다. 종료 과정은 여러 상황에서 호출될 수 있다.- 초기화 중 예외가 나면 애플리케이션 시작이 실패한다. 실패해도 되는 준비 작업인지, 반드시 실패해야 하는 검증인지 구분한다.
위험 신호!
- 생성자에서 네트워크 호출이나 파일 I/O를 수행한다.
@PostConstruct에서 실제 주문 생성, 결제 요청 같은 비즈니스 작업을 수행한다.- prototype Bean이 리소스를 잡는데 정리 책임이 없다.
- 초기화 순서에 의존하는 Bean들이 많다.
- 초기화 callback에서 프록시 적용이 필요한 메서드를 직접 호출한다.
- 종료 callback이 실패해도 로그 없이 삼켜진다.
확인 질문
확인 질문
- 초기화 callback은 생성자보다 언제 실행되는가?
- Bean 인스턴스 생성과 의존성 주입이 끝난 뒤 실행된다.
@PostConstruct에 적합한 작업은 무엇인가?
- 설정 검증, 캐시 준비, 리소스 초기화처럼 Bean 사용 전 준비 작업이다.
- prototype Bean 소멸을 조심해야 하는 이유는 무엇인가?
- 컨테이너가 prototype Bean의 전체 생명주기와 소멸 callback을 singleton처럼 관리하지 않기 때문이다.