목차
스프링 Bean 라이프사이클 완벽 정리! 실무에서 꼭 알아야 할 핵심 개념과 예제까지
스프링 개발자라면 한 번쯤은 의문이 듭니다.
“Spring Bean은 언제 생성되고 언제 사라질까?”
“초기화 메서드랑 소멸 메서드는 어떻게 설정하지?”
“@PostConstruct, @PreDestroy는 언제 호출되는 걸까?”
이번 포스트에서는 Spring Bean의 라이프사이클에 대해 개념부터 실무 예제, 주의사항까지 완벽하게 정리해 드릴게요.
1. Spring Bean 라이프사이클이란? 주요 개념과 특징
Spring Bean이란?
Spring Bean은 스프링 컨테이너가 관리하는 객체입니다. @Component, @Service, @Repository, @Controller 같은 어노테이션이나 XML 설정으로 등록되며, 의존성 주입(DI)과 생명 주기 관리를 받습니다.
라이프사이클 개요
Spring Bean의 라이프사이클은 크게 생성 → 초기화 → 사용 → 소멸의 단계로 나뉩니다. 스프링 컨테이너가 빈을 생성하고 초기화하며, 필요에 따라 소멸시키는 과정을 관리합니다.
주요 단계와 특징
- 빈 정의(Definition): @Bean 또는 어노테이션으로 빈이 정의됨.
- 인스턴스화(Instantiation): 빈 객체가 메모리에 생성됨.
- 의존성 주입(Dependency Injection): @Autowired로 의존성이 주입됨.
- 초기화(Initialization): 빈이 사용 준비를 마침.
- 사용(Usage): 애플리케이션에서 빈이 활용됨.
- 소멸(Destruction): 컨테이너 종료 시 빈이 정리됨.
주요 인터페이스와 메서드
- InitializingBean: afterPropertiesSet()으로 초기화.
- DisposableBean: destroy()로 소멸.
- @PostConstruct: 초기화 후 호출.
- @PreDestroy: 소멸 전 호출.
쉽게 말해, Spring Bean 라이프사이클은 "빈이 태어나서 자라나고, 일 시작하며, 퇴장하는 과정"입니다.
🔄 Spring Bean의 라이프사이클 단계
Spring Bean은 다음과 같은 순서로 동작합니다:
객체 생성 → 의존성 주입 → 초기화 → 사용 → 소멸
단계 | 설명 |
객체 생성 | 스프링 컨테이너가 빈의 인스턴스를 생성 |
의존성 주입 | 생성자/필드/Setter를 통해 의존성 주입 |
초기화 | 초기화 콜백(@PostConstruct, afterPropertiesSet, custom init) |
사용 | 비즈니스 로직 수행 |
소멸 | 컨테이너 종료 시 소멸 콜백 호출(@PreDestroy, destroy) |
📌 Bean 라이프사이클 관련 어노테이션 및 인터페이스
방식 | 설명 |
@PostConstruct | Bean 생성 후 초기화 작업 수행 (자바 표준 어노테이션) |
@PreDestroy | Bean 소멸 전 작업 수행 |
InitializingBean | afterPropertiesSet() 메서드 구현 |
DisposableBean | destroy() 메서드 구현 |
XML 방식 | <bean init-method="" destroy-method=""> 설정 가능 |
@Bean(initMethod=...) | JavaConfig에서 초기화/소멸 메서드 지정 가능 |
2. 예시 및 코드 설명: Bean 초기화와 소멸 처리
예시1. 어노테이션 기반 (@PostConstruct, @PreDestroy)
@Component
public class MyComponent {
public MyComponent() {
System.out.println("1. 생성자 호출");
}
@PostConstruct
public void init() {
System.out.println("2. 초기화 작업 실행");
}
@PreDestroy
public void destroy() {
System.out.println("3. 소멸 전 정리 작업 실행");
}
}
1. 생성자 호출
2. 초기화 작업 실행
// 애플리케이션 종료 시
3. 소멸 전 정리 작업 실행
예시2. 인터페이스 기반
@Component
public class MyComponent implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() {
System.out.println("초기화 - afterPropertiesSet()");
}
@Override
public void destroy() {
System.out.println("소멸 - destroy()");
}
}
✔ 실무에서는 어노테이션 방식이 더 간단하고 유지보수에 유리해서 선호됩니다.
예시 3. 사용자 서비스 빈의 라이프사이클
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;
@Component
public class UserService {
private String serviceName;
// 생성자
public UserService() {
System.out.println("1. UserService 인스턴스화");
}
// 의존성 주입 (예시로 Setter 사용)
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
System.out.println("2. 의존성 주입: " + serviceName);
}
// 초기화
@PostConstruct
public void init() {
System.out.println("3. 초기화 완료: " + serviceName + " 서비스 준비");
}
// 비즈니스 메서드
public void doWork() {
System.out.println("4. 사용 중: " + serviceName + " 작업 수행");
}
// 소멸
@PreDestroy
public void cleanup() {
System.out.println("5. 소멸: " + serviceName + " 정리");
}
}
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
UserService userService = context.getBean(UserService.class);
userService.setServiceName("UserManager");
userService.doWork();
context.close(); // 컨테이너 종료로 소멸 트리거
}
}
- 인스턴스화: UserService 객체가 생성되며, 생성자 호출.
- 의존성 주입: setServiceName()으로 serviceName 설정.
- @PostConstruct: 초기화 단계에서 호출되어 빈이 준비됨.
- 사용: doWork()로 빈이 실제 작업 수행.
- @PreDestroy: 컨테이너 종료 시 호출되어 정리.
1. UserService 인스턴스화
2. 의존성 주입: UserManager
3. 초기화 완료: UserManager 서비스 준비
4. 사용 중: UserManager 작업 수행
5. 소멸: UserManager 정리
3. 실무에서 Spring Bean 라이프사이클 활용 사례
데이터베이스 연결 관리
실무에서 데이터베이스 연결 풀(DBCP)을 관리하는 빈은 라이프사이클을 활용합니다.
@Component
public class DatabaseConnectionPool {
private String connectionString;
public DatabaseConnectionPool() {
System.out.println("DB 풀 생성");
}
@PostConstruct
public void initializePool() {
this.connectionString = "jdbc:mysql://localhost:3306/myapp";
System.out.println("DB 풀 초기화: " + connectionString);
// 실제로는 연결 풀 설정
}
public void executeQuery(String query) {
System.out.println("쿼리 실행: " + query);
}
@PreDestroy
public void closePool() {
System.out.println("DB 풀 종료: 연결 해제");
// 실제로는 연결 풀 닫기
}
}
활용 이유:
- @PostConstruct로 애플리케이션 시작 시 연결 풀을 초기화.
- @PreDestroy로 종료 시 자원을 정리해 메모리 누수 방지.
4. 주의사항: 장점과 단점
✅ 장점
- 자동 관리: 스프링이 빈의 생명 주기를 관리해 개발자가 신경 쓸 부분 감소.
- 자원 최적화: 초기화와 소멸 단계를 활용해 자원(예: DB 연결)을 효율적으로 관리.
- 확장성: 커스터마이징 가능한 메서드로 유연성 제공.
- 리소스 초기화/정리를 Bean 생명주기와 연동 가능
- 코드 흐름을 명확히 관리할 수 있음
- Spring이 자동 호출해주므로 별도 호출 필요 없음
❌ 단점
- 복잡성 증가: 커스텀 초기화/소멸 로직이 많아지면 디버깅 어려움.
- 해결책: 간단한 로직 유지, 로그 추가.
- 의존성 문제: 초기화 시점에 의존성이 아직 주입되지 않을 수 있음.
- 해결책: @DependsOn으로 의존성 순서 명시.
- 싱글톤 기본: 기본적으로 빈은 싱글톤이므로 상태 관리 주의.
- 해결책: @Scope로 범위 조정(예: prototype).
- @PreDestroy는 서버가 강제 종료될 경우 호출되지 않을 수 있음
- Bean 초기화 실패 시 전체 앱 부팅이 실패할 수 있음
- 너무 많은 라이프사이클 로직은 Bean의 역할을 흐릴 수 있음
✅ 결론: 생명주기를 이해하면, 스프링이 보인다
Spring Bean의 라이프사이클을 이해하면 초기화와 종료 처리를 안전하게 할 수 있고, 리소스 낭비를 줄이며 안정적인 앱 운영이 가능합니다.
특히 실무에서는 DB, 외부 API 연결, 스케줄러 초기화 등에서 매우 유용하게 사용되죠.
👉 @PostConstruct, @PreDestroy는 꼭 기억하세요!
'이직&취업 > Spring Framework' 카테고리의 다른 글
@RestController와 @Controller의 차이점은 무엇인가요? (25) | 2025.04.11 |
---|---|
Spring MVC에서 요청 흐름은 어떻게 되나요? (18) | 2025.04.11 |
@Component vs @Service vs @Repository vs @Controller: 스프링 어노테이션 차이 (30) | 2025.04.10 |
Spring Framework IoC (Inversion of Control)란? (21) | 2025.03.21 |
Spring Framework DI (의존성 주입) 란? (15) | 2025.03.20 |