데이터베이스 커넥션 풀이나 네트워크 소켓 연결처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고, 종료 시점에 이를 안전하게 닫는 작업은 매우 중요하다. 스프링이 제공하는 빈 생명주기 콜백과 다양한 빈 스코프를 통해 객체를 효율적으로 관리하는 방법을 알아본다.
1. 빈 생명주기 콜백 (Bean Lifecycle Callbacks)
스프링 빈은 객체 생성 → 의존관계 주입이라는 라이프사이클을 가진다. 빈은 의존관계 주입이 모두 완료된 후에야 필요한 데이터를 사용할 수 있는 준비가 완료된다. 따라서 초기화 작업은 반드시 의존관계 주입이 끝난 후 호출되어야 한다.
1.1. 스프링 빈의 이벤트 라이프사이클
스프링 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 → 사용 → 소멸 전 콜백 → 스프링 종료
스프링은 세 가지 방법으로 빈 생명주기 콜백을 지원한다.
- 인터페이스(InitializingBean, DisposableBean): 초기화와 소멸 메서드를 오버라이딩한다. 스프링 전용 인터페이스에 의존하며 메서드 이름을 바꿀 수 없다는 단점이 있어 현재는 거의 사용하지 않는다.
- 설정 정보에 초기화/소멸 메서드 지정: @Bean(initMethod = "init", destroyMethod = "close")처럼 메서드 이름을 직접 지정한다. 코드가 스프링에 의존하지 않으며 외부 라이브러리에도 적용 가능하다. 특히 destroyMethod는 기본값이 (inferred)로 설정되어 있어 close, shutdown 등의 메서드를 자동으로 추론해 호출한다.
- 애노테이션 (@PostConstruct, @PreDestroy): 최신 스프링에서 가장 권장하는 방법이다. 자바 표준 기술(JSR-250)이므로 스프링이 아닌 다른 컨테이너에서도 동작하며, 컴포넌트 스캔과도 잘 어울린다.
2. 빈 스코프 (Bean Scope)
빈 스코프는 빈이 존재할 수 있는 범위를 뜻한다. 스프링은 기본적으로 싱글톤(Singleton) 스코프를 사용하지만, 필요에 따라 다양한 스코프를 지원한다.
- 싱글톤: 기본 스코프. 스프링 컨테이너의 시작부터 종료까지 유지되는 가장 넓은 범위이다.
- 프로토타입: 스프링 컨테이너는 빈의 생성과 의존관계 주입, 초기화까지만 관여하고 더는 관리하지 않는다. 따라서 @PreDestroy 같은 소멸 콜백이 호출되지 않으며, 관리 책임은 빈을 조회한 클라이언트에 있다.
- 웹 스코프: 웹 환경에서만 동작하며, request(HTTP 요청 당 생성), session(세션 당 생성) 등이 대표적이다.
3. 프로토타입 빈과 싱글톤 빈의 함께 사용 시 문제점
싱글톤 빈이 의존관계 주입을 통해 프로토타입 빈을 주입받아 사용할 경우 문제가 발생한다. 싱글톤 빈은 생성 시점에 한 번만 주입을 받기 때문에, 내부에 가지고 있는 프로토타입 빈이 사용할 때마다 새로 생성되지 않고 과거에 주입된 것이 그대로 유지되는 것이다.
3.1. 해결책: Provider
이를 해결하기 위해 직접 의존관계를 찾는 DL(Dependency Lookup) 기능을 제공하는 ObjectProvider나 자바 표준인 JSR-330 Provider를 사용한다. provider.get()을 호출하면 그때마다 컨테이너에서 새로운 프로토타입 빈을 찾아 반환하므로 의도한 대로 동작하게 된다.
4. 웹 스코프와 프록시 (Proxy)
request 스코프 빈은 실제 HTTP 요청이 들어와야 생성된다. 따라서 애플리케이션 실행 시점에 싱글톤 빈이 이를 주입받으려 하면 에러가 발생한다. 이때 프록시(Proxy) 방식을 사용하면 문제를 해결할 수 있다.
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger { ... }
- 동작 원리: 스프링은 CGLIB라는 바이트코드 조작 라이브러리를 사용하여 해당 클래스를 상속받은 '가짜 프록시 객체'를 생성해 주입한다.
- 지연 처리: 클라이언트가 메서드를 호출하는 시점에 이 가짜 객체가 내부에서 실제 빈을 요청하여 실행한다. 이를 통해 클라이언트는 마치 싱글톤 빈을 사용하듯이 편리하게 웹 스코프 빈을 다룰 수 있다.
5. 결론: 핵심 원리 정리를 마치며
지금까지 스프링의 핵심 원리인 객체 지향 설계, IoC/DI, 싱글톤, 컴포넌트 스캔, 생명주기 및 스코프에 대해 깊이 있게 살펴보았다.
스프링은 결국 자바 언어가 가진 다형성을 극대화하여 유연하고 확장이 용이한 설계를 가능하게 돕는 도구이다. 이러한 핵심 원리를 정확히 이해하고 있다면, 향후 스프링 MVC, 데이터 접근 기술, 스프링 부트 등 더 높은 수준의 기술을 학습할 때 단순한 기능 사용을 넘어선 통찰을 얻을 수 있을 것이다.
'Spring > Core' 카테고리의 다른 글
| [Advanced-2] 스레드 로컬 - ThreadLocal (0) | 2025.12.31 |
|---|---|
| [Advanced-1] 예제 만들기 (0) | 2025.12.30 |
| [Basic-7] 의존관계 자동 주입의 전략과 빈 충돌 해결 (0) | 2025.12.30 |
| [Basic-6] 컴포넌트 스캔과 자동 의존관계 주입 (0) | 2025.12.30 |
| [Basic-5] 싱글톤 컨테이너와 CGLIB의 동작 원리 (0) | 2025.12.30 |
