스프링 프레임워크는 객체를 직접 생성하고 관리하는 대신, 필요한 객체를 스프링 컨테이너에게 위임하는 구조를 가집니다. 이때 스프링이 관리하는 객체를 Bean이라고 부르며, 이러한 Bean들은 단순한 자바 객체와 달리 생성과 초기화, 사용, 소멸에 이르기까지 전체 생명주기(Life Cycle)를 갖습니다.
즉 객체들을 관리한다는 것은 객체의 생성부터 소멸까지의 생명주기(LifeCycle) 관리를 개발자가 아닌 컨테이너가 대신 해준다는 말이며 객체 관리의 주체가 프레임워크가 되기 때문에 개발자는 로직에 집중할 수 있게 됩니다.
Spring Container와 Bean의 관계

먼저 애플리케이션이 구동되면, 스프링 IoC 컨테이너는 설정 파일이나 애노테이션 기반 설정을 바탕으로 컴포넌트를 탐색하고, 해당 클래스를 빈으로 등록하여 관리합니다.
이 과정은 단순한 객체 생성을 넘어서, 의존성 주입, 초기화 설정, 후처리 등을 포함한 일련의 생명주기 관리가 함께 이루어집니다.
스프링 Bean Life Cycle의 전체 흐름
스프링 애플리케이션이 실행되면, 컨테이너가 생성되고 내부적으로 다음과 같은 순서로 빈을 처리하게 됩니다. 먼저 빈이 생성됩니다. 이 과정은 보통 생성자를 통해 이루어지며, 이 시점에는 아직 의존성이 완전히 주입되지 않은 상태입니다.
이어서 빈이 필요로 하는 다른 객체들이 자동으로 주입됩니다. 이 단계에서 의존성 주입 방식은 생성자 주입, 필드 주입, 세터 주입 등 다양하게 선택할 수 있습니다.
생성자 주입
생성자 주입은 객체 생성과 동시에 의존성 주입이 완료되는 구조입니다. 즉, Bean 생성과 의존성 주입이 한 번에 이뤄지기 때문에, 객체는 항상 완전한 상태로 생성됩니다.
@Component
public class MyController {
private final MyService myService;
public MyController(MyService myService) {
this.myService = myService;
}
}
이 방식은 NullPointerException을 방지하고, 컴파일 타임에 의존성을 강제할 수 있다는 점에서 권장되는 방식입니다.
세터 주입
세터 주입은 빈이 먼저 생성된 후, 별도로 의존성이 주입되는 구조입니다. 즉, 객체 생성과 의존성 주입이 분리되어 진행됩니다.
@Component
public class MyController {
private MyService myService;
@Autowired
public void setMyService(MyService myService) {
this.myService = myService;
}
}
이 방식은 유연하게 설계할 수 있지만, 초기 상태에서 의존성이 없는 객체가 존재할 수 있기 때문에 주의가 필요합니다.
빈 생성 이후 라이프사이클 흐름
의존성 주입이 완료된 후, 초기화 콜백이 실행됩니다. 초기화는 단순히 객체를 생성한 것을 넘어서, 사용 준비가 완료된 상태로 만드는 과정입니다.
외부 API와의 연결, 파일 로딩, 캐시 초기화와 같은 작업이 이 단계에서 이루어질 수 있습니다. 초기화가 끝나면 빈은 실제 서비스 로직에서 사용되는 안정적인 상태에 들어갑니다.
스프링에서는 기본적으로 싱글톤 스코프를 사용하기 때문에, 한 번 생성된 빈은 애플리케이션이 종료될 때까지 동일한 인스턴스로 재사용됩니다.
애플리케이션이 종료되거나 빈이 더 이상 필요 없어지면, 스프링은 빈의 소멸 전 콜백을 호출하여 필요한 정리 작업을 수행할 수 있도록 해줍니다. 이때 데이터베이스 연결 해제, 쓰레드 종료, 외부 자원 반환 등의 처리를 안전하게 할 수 있습니다.
빈 생명주기에 개입하는 3가지 방법
InitializingBean & DisposableBean
스프링에서 제공하는 인터페이스를 구현하는 방식입니다. 각각 afterPropertiesSet()과 destroy()를 오버라이드하면 초기화와 소멸 시점에 개입할 수 있습니다.
@Component
public class ExampleBean implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
// 초기화 로직
System.out.println("Bean 초기화 완료");
}
@Override
public void destroy() throws Exception {
// 소멸 전 로직
System.out.println("Bean 소멸 직전");
}
}
단점은 스프링에 강하게 의존한다는 점이며, 외부 라이브러리나 수정 불가능한 클래스에는 적용할 수 없습니다.
@Bean의 initMethod와 destroyMethod
초기화와 소멸 메서드를 지정하고 싶은 경우, @Bean 애노테이션의 속성으로 메서드명을 직접 지정할 수 있습니다.
public class ExampleBean {
public void init() {
System.out.println("init() 실행");
}
public void cleanup() {
System.out.println("cleanup() 실행");
}
}
@Configuration
public class BeanConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup")
public ExampleBean exampleBean() {
return new ExampleBean();
}
}
이 방식은 스프링 코드에 의존하지 않으며, 외부 라이브러리에서도 활용이 가능하다는 장점이 있습니다. 단점은 메서드명을 일일이 지정해야 하고, 빈 등록 시 설정이 필요하다는 번거로움이 있습니다.
@PostConstruct & @PreDestroy
가장 간결하고 권장되는 방법은 javax.annotation 패키지의 애노테이션을 사용하는 것입니다. 이 방식은 JSR-250 표준이기 때문에 스프링 외부 환경에서도 호환됩니다.
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class ExampleBean {
@PostConstruct
public void init() {
System.out.println("@PostConstruct 초기화");
}
@PreDestroy
public void cleanup() {
System.out.println("@PreDestroy 소멸 전 처리");
}
}
이 방식은 컴포넌트 스캔과도 잘 어울리며, 단 하나의 애노테이션만으로 동작하기 때문에 가독성과 유지보수 측면에서도 뛰어납니다. 단, 외부 라이브러리에는 적용할 수 없다는 제한이 있습니다.
지금까지 설명한 흐름은 대부분 싱글톤 스코프를 기준으로 한 것입니다. 그러나 스프링은 요청(Request), 세션(Session), 프로토타입(Prototype) 등 다양한 스코프를 지원합니다.
예를 들어 프로토타입 스코프의 빈은 의존성 주입 이후 초기화까지는 스프링이 관리하지만, 그 이후의 생명주기 즉 소멸 처리는 스프링이 관여하지 않습니다. 이처럼 스코프에 따라 라이프사이클의 관리 범위가 달라지므로, 빈을 설계할 때 그 특성에 맞는 생명주기 개입 방법을 선택하는 것이 중요합니다.
마무리
최종적인 빈 라이프사이클은 다음과 같습니다.
스프링 IoC 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 메소드 호출 →
사용 → 소멸 전 콜백 메소드 호출 → 스프링 종료
스프링 빈은 단순한 자바 객체가 아니라, 스프링 컨테이너에 의해 관리되는 생명력을 가진 객체입니다. 이 빈이 언제 생성되고, 어떻게 초기화되며, 언제 소멸되는지를 이해하는 것은 스프링 기반 애플리케이션을 안정적으로 운영하는 데에 있어서 매우 중요한 요소입니다.
개발자는 이 생명주기에 적절히 개입함으로써 외부 리소스 초기화, 정리 작업을 명확하게 처리할 수 있고, 객체가 불완전한 상태에서 사용되는 것을 방지할 수 있습니다. 따라서 빈의 생명주기를 정확히 이해하고, 상황에 맞는 방식을 선택하여 사용하는 것이 좋습니다.
'CS' 카테고리의 다른 글
| 트랜잭션 고립수준 (1) | 2025.05.07 |
|---|---|
| 프록시와 리버스 프록시, 그리고 자바 스프링에서의 프록시 활용 정리 (0) | 2025.04.17 |
| 데이터베이스 인덱스에 대해서 설명해주세요 (0) | 2025.04.12 |
| 일급 컬렉션이 무엇인가요? (0) | 2025.04.09 |
| Spring Data JPA에서 새로운 Entity인지 판단하는 방법은 무엇일까요? (0) | 2025.04.08 |