빈이 생성된 직후에 실행해야 하는 초기화 로직이 있다면, @PostConstruct, InitializingBean, @Bean(initMethod) 중 무엇을 써야 할까요?

개념 정의

스프링은 빈이 생성되고 의존성이 주입된 직후 에 초기화 로직을 실행하는 3가지 방법을 제공합니다. 커넥션 풀 워밍, 캐시 로딩, 설정 검증 등에 사용합니다.

3가지 방법 비교

방법코드 위치스프링 의존성실행 순서
@PostConstruct빈 클래스 메서드에 어노테이션없음 (Jakarta EE 표준)1번째
InitializingBean빈 클래스가 인터페이스 구현** 있음** (스프링 인터페이스)2번째
@Bean(initMethod)설정 클래스에서 지정** 없음** (메서드 이름만 지정)3번째

1. @PostConstruct — 가장 많이 쓰는 방법

JAVA
@Component
public class CacheWarmer {
    private final RedisTemplate<String, String> redis;

    @PostConstruct
    public void warmUp() {
        // 애플리케이션 시작 시 캐시를 미리 채운다
        redis.opsForValue().set("config:version", loadVersion());
    }
}

Jakarta EE 표준 어노테이션이라 스프링에 종속되지 않습니다. 대부분의 경우 이걸 쓰면 됩니다.

2. InitializingBean — 스프링 프레임워크 내부에서 주로 사용

JAVA
@Component
public class DataSourceValidator implements InitializingBean {
    private final DataSource dataSource;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 모든 프로퍼티 주입 후 DB 연결 검증
        try (Connection conn = dataSource.getConnection()) {
            if (!conn.isValid(3)) {
                throw new IllegalStateException("DB 연결 실패");
            }
        }
    }
}

스프링 인터페이스에 직접 의존하므로 일반 애플리케이션 코드에서는 권장하지 않습니다. 스프링 프레임워크 내부 코드에서 많이 사용됩니다.

3. @Bean(initMethod) — 외부 라이브러리 빈 초기화

JAVA
@Configuration
public class AppConfig {
    @Bean(initMethod = "init")
    public ExternalService externalService() {
        return new ExternalService();  // 소스 수정 불가
    }
}

소스 코드를 수정할 수 없는 외부 라이브러리의 빈에 초기화 로직을 연결할 때 사용합니다.

실행 순서

3가지를 모두 설정하면 다음 순서로 실행됩니다:

JAVA
@Component
public class OrderService implements InitializingBean {
    @PostConstruct
    public void postConstruct() {
        System.out.println("1. @PostConstruct");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("2. InitializingBean");
    }

    // @Bean(initMethod = "customInit")으로 지정된 경우
    public void customInit() {
        System.out.println("3. initMethod");
    }
}
// 출력: 1 → 2 → 3

선택 기준

상황선택이유
일반적인 초기화 로직@PostConstruct간단, 표준, 스프링 비종속
외부 라이브러리 빈@Bean(initMethod)소스 수정 불가
스프링 프레임워크 확장InitializingBean프레임워크 내부 관례
초기화 순서가 중요3가지 조합1→2→3 순서 보장

주의할 점

1. @PostConstruct에서 다른 빈에 의존하면 순서 문제

@PostConstruct는 해당 빈의 의존성이 주입된 직후에 실행되지만, ** 다른 빈의 @PostConstruct가 먼저 실행됐는지는 보장되지 않습니다.** 빈 A의 @PostConstruct에서 빈 B의 초기화된 데이터에 접근하면, 빈 B가 아직 초기화되지 않았을 수 있습니다.

JAVA
@PostConstruct
public void init() {
    // configService의 @PostConstruct가 먼저 실행됐는지 보장 안 됨!
    String value = configService.getCachedValue();
}

해결: @DependsOn으로 순서를 명시하거나, ApplicationRunner를 사용합니다.

2. 생성자에서 초기화하면 안 되나?

의존성 주입이 생성자 주입이면 생성자 시점에 모든 의존성이 있으므로, 간단한 초기화는 생성자에서 해도 됩니다. 하지만 ** 프록시 기반 기능(@Transactional, @Cacheable 등)은 생성자 시점에 동작하지 않습니다.** 프록시는 빈이 완전히 생성된 후에 감싸지기 때문입니다.

정리

방법용도스프링 의존외부 라이브러리
@PostConstruct일반 초기화없음불가
InitializingBean프레임워크 확장있음불가
@Bean(initMethod)외부 라이브러리없음** 가능**

대부분의 경우 @PostConstruct를 쓰면 됩니다. 외부 라이브러리 빈만 @Bean(initMethod)로 처리합니다.

댓글 로딩 중...