스프링에서 Bean은 생명주기를 가지고 있다.
이 생명주기 동안 특정 시점에 초기화와 종료 작업을 수행해야 할 경우가 있다.
김영한 강사님의 강의에선 DB 커넥션 풀을 초기화하거나 네트워크 소켓을 연결하는 작업에서 생명주기 초기화와 종료 작업이 필요하다고 한다.
3가지 방법에 대해 공부하고 간단하게 DB 커넥션 풀 초기화와 네트워크 소켓 연결의 예시 코드를 만들어서 이해를 돕도록 해보겠다.
빈의 생명주기 콜백이란?
빈 생명주기 콜백은 스프링 빈이 생성되고 소멸되는 과정에서 특정 시점에 호출되는 메서드를 의미한다.
스프링은 빈이 생성되고 의존관계 주입이 완료된 후 초기화 콜백을 호출하고, 빈이 소멸되기 직전에 소멸 콜백을 호출한다.
안전하게 빈의 초기화와 종료 작업이 가능한 것이다.
빈의 생명주기는 크게
- 스프링 컨테이너 생성
- 스프링 빈 생성
- 의존관계 주입
- 초기화 콜백 - 빈이 생성되고 의존관계 주입이 완료된 후 호출
- 사용
- 소멸 전 콜백 - 빈이 소멸되기 직전에 호출
- 스프링 컨테이너 종료
이렇게 나뉘어져 있다. 생성자 주입 같은 경우엔 빈을 생성하면서 의존관계 주입까지 하는 건 알고 있을 것이니 더 설명하지 않겠다. 보통 이런 단계로 수행된다.
빈 생명주기 콜백은 인터페이스 사용, @Bean 어노테이션의 속성 활용, 최신 방법인 어노테이션 사용만으로 관리하는 방법이 있다.
1. 인터페이스 사용
스프링은 InitializingBean과 DisposableBean 인터페이스를 제공한다.
빈의 초기화와 소멸 시점에 특정 메서드를 호출할 수 있다.
- InitializingBean 인터페이스는 afterPropertiesSet() 메서드를 통해 초기화를 지원한다.
- DisposableBean 인터페이스는 destory() 메서드를 통해 소멸을 지원한다.
public class CustomBean implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
// 초기화
}
@Override
public void destroy() throws Exception {
// 종료
}
}
그러나 스프링 전용 인터페이스에 의존을 하게 된다는 단점과 메서드 이름 변경이 불가능하다는 점, 외부 라이브러리엔 적용할 수 없다는 단점이 있다.
2. 빈 등록 시 초기화, 소멸 메서드 지정
@Bean 어노테이션을 사용하여 빈을 등록할 땐 initMethod와 destroyMethod 속성을 통해서 초기화와 소멸 메서드를 지정할 수 있다.
@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public CustomBean customBean() {
return new CustomBean();
}
}
public class CustomBean {
public void init() {
// 초기화
}
public void close() {
// 종료
}
}
앞선 방법보다 더 좋다.
- 메서드 이름을 자유롭게 지정할 수 있다.
- 스프링 코드에만 의존하지 않는다.
- 외부 라이브러리에 적용할 수 있다.
destoryMethod는 기본적으로 close나 shutdown 메서드를 자동으로 추론하여 호출한다.
3. 어노테이션 사용
가장 최신의 방법이다. @PostConstructor와 @PreDestroy 어노테이션을 사용하기만 하면 된다.
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class CustomBean {
@PostConstruct
public void init() {
// 초기화
}
@PreDestroy
public void close() {
// 종료
}
}
- 가장 간편하고 권장되는 방법이다.
- 스프링에 종속되지 않는 자바 표준이다. JSR-250이라고 한다나 뭐라나
- 컴포넌트 스캔과 잘 어울린다.
간편하지만 외부 라이브러리를 초기화하거나 종료해야 할 경우엔 @Bean의 initMethod와 destoryMethod를 사용해야 한다.
이제 DB 커넥션 풀 초기화와 네트워크 소켓 연결을 구현한 간단한 예시 코드를 보자.
DB 커넥션 풀 초기화
@PostConstruct
public void init() {
connectionPool = new ConnectionPool(); // 커넥션 풀 초기화
connectionPool.init("jdbc:mysql://localhost:3306/mydb", "user", "password");
}
초기화: 애플리케이션 시작 지점에 DB 커넥션 풀을 이렇게 미리 생성해두고 필요할 때마다 커넥션을 가져와서 사용한다.
@PreDestroy
public void close() {
connectionPool.close(); // 커넥션 풀 종료
}
종료: 애플리케이션 종료 시점에 커넥션 풀을 안전하게 종료한다.
네트워크 소켓 연결
@PostConstruct
public void init() {
socket = new Socket("hskhsmm.com", 8080); // 소켓 연결
System.out.println("소켓 연결 완료");
}
초기화: 애플리케이션 시작 지점에 네트워크 소켓을 연결한다.
@PreDestroy
public void close() {
socket.close(); // 소켓 연결 종료
System.out.println("소켓 연결 종료");
}
종료: 애플리케이션 종료 시점에 소켓 연결을 종료한다.
이렇게 리소스 낭비 방지와 애플리케이션의 안정성을 높일 수 있는 것이다.
정리
1. 초기화와 종료 작업은 반드시 필요할 때만!
- 무거운 작업은 생성자에서 처리하지 말고 초기화 메서드에서 처리하는 것이 좋다.
2. @PostConstructor와 @PreDestroy 어노테이션을 사용하는 것이 가장 편리하고 권장되는 방법이다.
3. 외부 라이브러리를 초기화하거나 종료할 경우 @Bean의 initMethod와 destoryMethod를 사용한다.
'Backend > Spring Boot' 카테고리의 다른 글
스프링 부트 계층형 아키텍처 (0) | 2025.03.27 |
---|---|
빈이 존재하는 범위를 이해하자 (0) | 2025.02.19 |
@Autowired 필드 명, @Qualifier, @Primary 정리 (0) | 2025.02.11 |
@Configuration 없이 @Bean만 사용하면? (0) | 2025.02.09 |
@Controller와 @RestController 차이 (1) | 2025.02.04 |