본문 바로가기

[Spring] IoC와 Bean 개념 정리 (+ IoC 컨테이너 종류, Bean Lite Mode)

@우잉_2025. 4. 30. 01:07

IoC(Inversion of Control, 제어의 역전)

프로그램의 흐름 제어 권한을 개발자가 아닌 프레임워크가 가진다.

소프트웨어 엔지니어링에서 객체 또는 프로그램의 일부에 대한 제어권을 컨테이너나 프레임워크에 위임하는 것을 말한다.

사용자 정의 코드가 라이브러리를 호출하는 기존 방식에서, IoC 프레임워크가 프로그램 흐름을 제어하고 사용자 정의 코드를 호출할 수 있도록 한다. 이를 위해 프레임워크는 추가 동작이 내장된 추상화를 사용한다.

Spring에서 IoC 적용 사례

  • Bean 관리
  • DI
  • AOP

즉, IoC에 대해 검색하면 자주 나오는 DI는 IoC를 활용하는 방법 중 하나이다.

이번 게시글에서는 IoC 그중에서도 Bean을 생성하고 관리하는 방법에 대해 다루려고 한다.

Bean의 정의

IoC 컨테이너에 의해 관리되는 오브젝트이다.

Bean은 IoC컨테이너에 제공하는 빈 정의(메타데이터)를 통해 생성된다. 빈을 생성하면 실제 인스턴스를 생성하는 레시피가 생성되고, Bean Scope에 정의된 바에 따라 IoC 컨테이너가 해당 객체를 생성, 주입, 소멸 시킨다.

즉, IoC 컨테이너에 등록된 Bean은 개발자가 객체에 대한 생명주기를 직접 관리하지 않고 Spring에서 이를 관리한다.

Bean 속성

자주 사용되거나 중요한 개념은 Bold

속성 설명
Id Bean을 식별하기 위한 id (중복 불가)
Class 생성할 객체의 패키지 경로
Name Bean을 식별하기 위한 별칭
Scope 실제 인스턴스의 생명주기를 정의
Constructor argument 생성자 기반 Bean 의존성 주입시 전달할 값
Properties Setter 기반 Bean 의존성 주입시 전달할 값
Autowiring mode @Autowired로 주입할때 자동 연결 모드 지정
Lazy initialization mode 빈을 지연 초기화 할지 여부
Initialization method 생성자 메소드 정의
Destruction method 소멸자 메소드 정의

Bean Scope란?

Bean Scope는 빈 정의에 정의된 클래스를 실제 인스턴스로 생성할때의 생명주기를 의미한다. 즉, 어느시점까지 인스턴스를 유지하고 소멸시킬지를 말한다.

범위 설명
Singleton (Default) IoC컨테이너에 의해 단일 인스턴스만 관리되며, 동일한 ID의 빈에 대한 요청은 해당 인스턴스만 반환한다.
주로 읽기전용 인스턴스나, 상태가 없는 인스턴스에 사용
Prototype DI 요청마다 새로운 Bean을 생성한다. Bean 소멸에는 IoC컨테이너가 관여하지 않고, GC에 의해 소멸됨
Web 웹 요청의 생명 주기와 동일한 생명 주기를 가진다.
(request, session, application, websocket)

IoC 컨테이너: ApplicationContext와 BeanFactory

IoC중에서 가장 중요한 개념은 단연 IoC 컨테이너라고 할 수 있다. Spring에서는 IoC 컨테이너 역할을 담당하는 Application Context와 Bean Factory가 있다. Bean Factory는 기본적인 기능만을 제공하고, Application Context는 Bean Factory의 상위 집합으로 부가적인 기능을 제공한다.

기능 BeanFactory ApplicationContext
Bean 인스턴스화/연결 Yes Yes
통합 생명주기 관리 No Yes
BeanPostProcessor 자동 등록 No Yes
BeanFactoryPostProcessor 자동 등록 No Yes
MessageSource(국제화) 접근 지원 No Yes
ApplicationEvent(이벤트 발행) 기능 내장 No Yes

요약하면 ApplicationContext는 아래의 추가 기능을 제공한다

  • Bean 생명주기 관리 및 생성자 소멸자 자동 감지
  • 다국어 메세지 지원
  • 내부 메세지 브로커 지원

Bean 생명주기

스프링 Bean은 아래의 생명 주기를 가진다.

IoC 컨테이너 생성 -> Bean 생성 -> DI -> 초기화 콜백 -> Bean 사용 -> 소멸 전 콜백

Bean 생성

IoC 컨테이너에 Bean Definition을 저장하는 단계이다. ApplicationContext는 기본적으로 Eager Loading이므로, Bean을 등록하면 바로 생성한다. 그러나 Lazy Loading인 경우 DI요청이 발생할때 생성된다.

Bean 생성 방법

  • 설정파일(XML)에서 <bean>태그 정의
  • @Configuration 클래스에 @Bean 메소드 정의
  • 컴포넌트 스캔 (@Component, @Service, @Repository, @Contreoller...)

초기화 콜백

객체지향에서 모든 객체들은 생성자를 가진다. Bean또한 객체이므로 생성자가 존재하지만 내부적으로 의존성이 있는 경우 생성자에서 수행이 불가능한 로직이 존재할 수 있다. 이를 해결하기 위한 방법으로 초기화 콜백을 사용한다.

  • @PostConstruct - Spring 권장 방식
  • @Bean(initMethod="..."
  • InitializingBean 인터페이스

Bean Lite Mode?

Bean이긴 하나, 빈을 생성하는 메소드가 일반 팩토리 메소드화 되는것을 말한다. Bean Lite Mode가 되면, 클래스 내부끼리 Bean생성 메소드를 호출하면 싱글톤 보장이 깨지게된다. Bean Lite Mode는 @Configuration없이 @Bean메소드만을 사용한 경우 발생한다.

// 일반적인 Bean 생성
@Configuration
public class AppConfig {
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

// Bean Lite Mode
public class LiteAppConfig {
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

이런 현상이 발생하는 이유는, @Configuration 클래스는 CGLIB 라이브러리를 통해 프록시 객체가 생성된다. 이는, 클래스 내부의 빈 생성 메소드에 대한 요청을 가로채어 싱글톤을 보장하기 위함이다. 그러나, @Configuration으로 정의되지 않은 객체 내부에서는 Bean 생성 메소드가 일반 팩토리 메소드처럼 동작하여 여러개의 인스턴스로 생성될 수 있는것이다. 즉, Context.getBean()이 아닌, this.~~()처럼 동작한다는것이다.

 

이와 유사한 현상으로 @Transcational로 정의된 메소드를 클래스 내부에서 호출할때 트랜잭션 정합성이 깨지는 현상이다. 그 이유는 @Transactional은 AOP의 한 종류로 프록시 객체에 의해 관리되는데, this 호출이 우선순위가 높아 내부 메소드로 동작하기 때문이다.

의존성 주입(DI)

IoC 컨테이너의 빈 정의에 따라 빈 생성 및 조회하는 과정이다. 의존성 주입 방법에는 생성자 주입, Setter 주입, 필드 주입이 있다.

Bean 소멸

IoC 컨테이너가 종료될때 Bean 소멸 처리를 시작한다. 이때 소멸 콜백이 구현되어있는 경우 소멸 콜백을 호출한다.

  • @PreDestroy
  • @Bean(destroyMethod = "...")
  • DisposableBean 인터페이스

References

https://www.baeldung.com/spring-bean

https://docs.spring.io/spring-framework/reference/core/beans.html

https://steady-coding.tistory.com/594

https://innovation123.tistory.com/213

목차