yja

9. 빈 생명주기 콜백 본문

[Web-Back] Spring/스프링 핵심 원리 - 기본편 (강의)

9. 빈 생명주기 콜백

유진진 2026. 4. 29. 14:58

1. 빈 생명주기 콜백 시작 

NetworkClient.java 

여기서 생성자가 호출되면 `connect()` 를 통해 연결된 url을 확인할 수 있다. 

public class NetworkClient {

    private String url;

    public NetworkClient() {
        System.out.println("생성자 호출, url = " + url);
        connect();
        call("초기화 연결 메시지");
    }

    public void setUrl(String url) {
        this.url = url;
    }

    // 서비스 시작 시 호출
    public void connect() {
        System.out.println("connect: " + url);
    }

    public void call(String message) {
        System.out.println("call = " + url + "message = " + message);
    }

    // 서비스 종료 시 호출
    public void disconnect() {
        System.out.println("close: " + url);
    }
}

 

lifeCycleTest.java

networkClient 객체를 만들고 나서, 값을 "http://hello-spring.dev"로 초기화한다. 

@Test
public void lifeCycleTest() {

    ConfigurableApplicationContext ac = new AnnotationConfigApplicationContext(LifeCycleConfig.class);
    NetworkClient client = ac.getBean(NetworkClient.class);
    ac.close();
}

@Configuration
static class LifeCycleConfig {

    @Bean
    public NetworkClient networkClient() {
        NetworkClient networkClient = new NetworkClient();
        networkClient.setUrl("http://hello-spring.dev");
        return networkClient;
    }
}

 

테스트 결과에는 connect가 null이 나온다. 왜 url이 설정되지 않았을까? 

 

생성자 호출 시점에 url이 설정되지 않아서 null 이고, 그 상태에서 바로 connect()가 호출되기 때문이다. 

객체 생성 후 setUrl() 을 통해 url 값이 세팅된다. 

 

 

스프링 빈의 이벤트 라이프사이클

스프링 컨테이너 생성 ➡️ 스프링 생성 ➡️ 의존관계 주입 ➡️ 초기화 콜백 ➡️ 사용 ➡️ 소멸전 콜백 ➡️ 스프링 종료

  • 초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출
  • 소멸전 콜백: 빈이 소멸되기 직전에 호출

이런 콜백을 통해 안전하게 초기화와 종료가 될 수 있다. 

객체 생성 시점과 초기화는 분리하는 게 좋다. 

 

 

생명주기 콜백을 지원하는 3가지 방법 

1. 인터페이스(InitializingBean, DisposableBean)

2. 설정 정보에 초기화 메서드, 종료 메서드 지정

3. @PostConstruct, @PreDestroy 애노테이션 지원

 

 

2. 인터페이스 InitializingBean, DisposableBean

  • InitializingBean`의`afterPropertiesSet()`: 초기화를 지원한다. 
  • DisposableBean`의`destroy()`: 메서드 소멸을 지원한다. 
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class NetworkClient implements InitializingBean, DisposableBean {

    // 의존관계 끝나고 호출됨
    @Override
    public void afterPropertiesSet() throws  Exception {
        System.out.println("NetworkClient.afterPropertiesSet");
        connect();
        call("초기화 연결 메시지");
    }

    // 빈이 종료될 때 호출됨
    @Override
    public void destroy() throws Exception{
        System.out.println("NetworkClient.destroy");
        disconnect();
    }
}

하지만 이 방식은 스프링 전용 인터페이스이므로 수정하기 쉽지 않아서 좋지 않다. 

 

 

3. 빈 등록 초기화, 소멸 메서드 

위 방식 대신에 내가 직접 만든 메서드로 같은 기능을 구현할 수 있다. 

이렇게 메서드를 직접 만들고, 

public class NetworkClient {

    // 의존관계 끝나고 호출되도록 
    public void init() throws  Exception {
        System.out.println("NetworkClient.init");
        connect();
        call("초기화 연결 메시지");
    }

    // 빈이 종료될 때 호출되도록 
    public void close() throws Exception{
        System.out.println("NetworkClient.close");
        disconnect();
    }
}

 

@Bean 의 `initMethod`와 `destroyMethod`에 내가 만든 메서드 이름을 지정해준다. 

@Bean(initMethod = "init", destroyMethod = "close")
public NetworkClient networkClient() {
    NetworkClient networkClient = new NetworkClient();
    networkClient.setUrl("http://hello-spring.dev");
    return networkClient;
}

 

여기서 destroyMethod는 기본값이 "inferred"여서 `close`와 `shutdown` 라는 이름을 자동으로 추론하게 되어 정해주지 않아도 소멸 기능이 작동한다. 

 

 

 

4. 애노테이션 @PostConstruct, @PreDestroy

  • `@PostConstruct`: 빈의 의존관계 주입이 완료된 후에 호출된다. 
  • `@PreDestroy`: 스프링 컨테이너가 빈을 종료하기 직전에 호출된다. 

자바 표준 기술이어서 스프링이 아닌 다른 컨테이너에서도 작동한다. -> 가장 권장되는 방식!

하지만 코드 수정은 하지 못한다. 

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;

public class NetworkClient {    
    // 의존관계 끝나고 호출됨
    @PostConstruct
    public void init() {
        System.out.println("NetworkClient.init");
        connect();
        call("초기화 연결 메시지");
    }

    // 빈이 종료될 때 호출됨
    @PreDestroy
    public void close() {
        System.out.println("NetworkClient.close");
        disconnect();
    }
}

 

사용하는 쪽에서는 @Bean으로 등록만 해주면 된다. 

@Bean
public NetworkClient networkClient() {
    NetworkClient networkClient = new NetworkClient();
    networkClient.setUrl("http://hello-spring.dev");
    return networkClient;
}