Spring Framework/Spring Core

[Spring Framework] 빈 생명주기 콜백

소재훈 2023. 5. 6. 05:24

본 포스트는 김영한 강사님의 스프링 핵심 원리 - 기본 편을 보고 정리한 내용입니다.

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

데이터베이스 연결이나 네트워크 연결의 소켓을 미리 열어놓아야 빨리 응답을 줄 수 있다. 네트워크 소켓처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고, 애플리케이션 종료 시점에 연결을 모두 종료하는 작업을 진행하려면 객체의 초기화와 종료작업이 필요하다. 이러한 역할을 스프링이 제공해 준다. 스프링을 통해 이런 초기화와 종료작업이 어떻게 진행되는지 알아보자.

 

일단 용어부터 정리해봅시다, 객체를 생성하는 단계와 초기화하는 단계를 구분해야 합니다. 객체를 생성한다라는 것의 의미는 말 그대로 인스턴스를 생성했다는 의미이고, 초기화를 했다는 것은 작업을 시작하기 위해 준비하는 과정을 완료했다는 것을 의미합니다. 생성자는 필수정보를 받고 메모리를 할당해서 객체를 생성하는 책임을 가진다면 초기화는 생성된 값들을 활용해서 외부 커넥션 연결 등 무거운 작업을 수행합니다. 따라서 생성자 안에서 무거운 초기화작업을 한꺼번에 하는 것보다는 객체를 생성하는 부분과 초기화하는 부분을 명확히 하는 것이 유지보수의 관점에서 좋습니다. 물론 작업이 단순한 경우에는 생성자에서 처리하는 것도 좋습니다.

 

스프링 빈은 간단하게 객체를 생성하고, 그 후에 의존관계를 주입하는 간단한 라이프 사이클을 가집니다. 따라서 스프링 빈은 객체를 생성하고, 의존관계를 주입한 다음에야 데이터를 사용할 수 있는 준비가 완료됩니다. 따라서 데이터베이스를 연결하거나 네트워크의 연결을 하는 등, 이러한 초기화 작업은 의존관계 주입이 모두 완료되고 난 다음에 호출해야합니다.

 

그러면 개발자가 어떻게 의존관계 주입이 완료된 시점을 알 수 있을까요? 바로 스프링이 의존관계 주입이 완료되고 나면 콜백 메서드를 통해서 초기화시점을 알려준다고 합니다.  그리고 스프링은 스프링 컨테이너가 종료되기 직전에 소멸 콜백을 줍니다. 따라서 안전하게 종료작업을 완료할 수 있습니다.

 

콜백 메서드를 포함해서 스프링 빈의 이벤트 라이프 사이클을 살펴보면 다음과 같습니다.

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

싱글톤처럼 스프링 컨테이너의 시작과 종료까지 함께 생존하는 빈은 위와같이 스프링이 종료되기 직전에 소멸 전 콜백이 발생하지만 생명주기가 짧은 빈들은 컨테이너와 무관하게 해당 빈이 종료되기 직전에 소멸 전 콜백이 일어납니다. 자세한 내용을 스코프를 공부할 때 알아보겠습니다.

 

스프링은 크게 3가지 방법으로 생명주기 콜백을 지원합니다.

  1. 인터페이스(InitializingBean, DisposableBean)
  2. 설정정보 초기화 메서드, 종료 메서드 지정
  3. @PostConstruct, @PreDestroy

결론부터 말하면 3번 방식을 주로 사용하면 됩니다. 그래도 하나하나 알아보겠습니다.

1. 인터페이스 InitializingBean, DisposableBean

InitializingBean 인터페이스

InitializingBean 인터페이스에는 afterPropertiesSet 메서드가 정의되어 있습니다. 이 인터페이스를 채택해서 해당메서드를 구현하고, 메서드 내부에 초기화 작업을 해두면 됩니다.

DisposableBean 인터페이스에는 destroy 메서드가 정의되어 있고, 이 인터페이스를 채택해서 해당 메서드를 구현하고, 메서드 내부에 소멸전에 할 작업을 수행하면 됩니다. 예를 들어서 네트워크 작업 이후에 연결을 끊는 작업 등이 있습니다.

 

하지만 이 인터페이스를 사용하는 방법은 다음과 같은 문제점이 있습니다.

  • 인터페이스가 스프링 전용 인터페이스 이기 때문에 스프링 전용 인터페이스에 의존하게 됩니다.
  • 초기화, 소멸 메서드의 이름을 변경할 수 없습니다.
  • 내가 코드를 고칠 수 없는 외부 라이브러리에 적용할 수 없습니다.

빈 등록 초기화, 소멸 메서드 지정

@Bean 애노테이션에 initMethod와 destroyMethod 값을 설정해서 초기화, 소멸 메서드의 이름을 직접지정할 수 있습니다.

@Bean(initMethod = "init", destroyMethod = "close")

위와 같이 지정하면 초기화메서드의 이름은 init이 되고, 소멸 메서드의 이름은 close가 됩니다.

인터페이스를 사용하던 방식과는 다르게 메서드 이름을 자유롭게 줄 수 있고, 스프링 빈이 스프링 코드에 의존하지 않습니다. 그리고 가장 큰 장점은 코드가 아니라 설정정보를 사용하기 때문에 코드를 고칠 수 없는 외부 라이브러리에도 초기화와 종료메서드를 적용할 수 있다는 점 입니다.

 

destroyMethod에는 추론(inferred)라는 특별한 기능이 있습니다. 소멸 메서드를 지정하지 않으면 close, shutdown라는 이름의 메서드를자동으로 호출해줍니다. 추론기능을 사용하고 싶지 않다면 destroyMethod=""처럼 빈 공백을 지정하면 됩니다.

@PostConstruct, @PreDestroy

초기화로 사용할 메서드에는 @PostConstruct, 소멸 메서드로 사용할 메서드에는 @PreDestroy 애노테이션을 붙여주는 방법입니다. 이전 방법들에 비해서 사용하기 아주 편리합니다.

@PostConstruct
public void init() {
    System.out.println("초기화 메서드");
}

@PreDestroy
public void close() {
    System.out.println("소멸 메서드");
}

최신 스프링에서 가장 권장하는 방법이고, 애노테이션하나만 붙이면 되기 때문에 매우 편리합니다. 스프링이 아닌 javax 패키지에 있는 자바 표준의 기술이기 때문에 스프링이 아닌 다른 컨테이너에서도 동작합니다. 컴포넌트 스캔과도 잘어울리지만, 외부라이브러리에는 적용하지 못한다는 단점이 있습니다. 

 

결론은 @PostConstruct, @PreDestroy 애노테이션을 사용하고, 코드를 수정할 수 없는 외부 라이브러리를 초기화, 종료해야한다면 @Bean의 initMethod, destroyMethod를 사용합시다. 인터페이스는 이제 잘 사용하지 않는다고 합니다.