본문 바로가기
백엔드

[Spring Boot] @Bean, @Configuration, @Component

by 육빔 2024. 8. 30.
728x90
반응형

위 3가지는 Spring Bean을 등록할때 사용하는 어노테이션이다. 각 차이점을 정확히 알고자 정리한다. 

우선 Spring Bean은 무엇일까?

 

Spring Bean이란??

 

Spring IoC (Inversion of Control) 컨테이너에 의해 관리되는 객체를 의미합니다. Spring 애플리케이션의 구성 요소들로서, 애플리케이션의 주요 기능들을 담당하는 객체들이 빈(Bean)으로 등록된다. 이러한 빈들은 Spring 컨테이너가 생성하고, 그 생명 주기를 관리하며, 애플리케이션이 실행될 때 자동으로 주입된다. 덕분에 우리가 귀찮게 의존성과 생성자를 관리할 필요가 없다.

 

Spring Bean의 주요 특징

  1. 관리되는 객체: Spring Bean은 Spring IoC 컨테이너가 관리. 즉, 개발자가 직접 인스턴스를 생성하지 않고, Spring 컨테이너가 빈의 생명 주기를 책임지며, 필요한 경우 자동으로 빈을 주입.
  2. 스코프(범위): Spring Bean은 여러 스코프(scope)를 가진다. 기본적으로 빈은 싱글톤(Singleton) 스코프로 생성되지만, 필요에 따라 프로토타입(Prototype), 요청(Request), 세션(Session) 등의 스코프를 설정 가능.
    • Singleton: 컨테이너 당 하나의 인스턴스만 존재하는 스코프.
    • Prototype: 빈 요청 시마다 새로운 인스턴스를 생성하는 스코프.
    • Request: HTTP 요청당 하나의 인스턴스를 생성하는 스코프 (웹 애플리케이션에서 사용).
    • Session: HTTP 세션당 하나의 인스턴스를 생성하는 스코프 (웹 애플리케이션에서 사용).

 

또한 기존에는 xml파일에서 빈을 등록하고 수정하였으나 귀찮고 번거로워 어노테이션으로 관리하는 방향으로 진화했다.

이러한 빈을 등록하기 위해 보통 2가지 방법이 존재한다.

 

첫번째는 @Component를 사용하는 방법이다.

@Component
public class MyService {
    // Some code here
}

 

@Component는 빈을 등록할때 쓰이는 어노테이션으로 Spring의 Component Scan에 의해 자동으로 감지되고 빈으로 등록될 수 있도록 한다. 또한 우리가 자주쓰는 @Service, @Controller, @Repository와 같은 어노테이션 안에 존재한다. 아래 확인해보자

 

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

 

?? 그런데 생각해보니 Repository는 @Repository가 없어도 실행된다는 것이었다.

 

정상적으로 동작하는 코드

public interface BoardRepository extends JpaRepository<Board, Long> {
}

 

궁금해서 찾아보니 @EnableJpaRepositories 라는 에너테이션을 통해 다음 아래와 같은 단계가 이루어지는 것을 찾을 수 있었다.

 

  1. @EnableJpaRepositories 애너테이션: Spring 설정 클래스에 이 애너테이션이 추가되면, Spring은 이 애너테이션이 위치한 클래스에서 JpaRepositoriesRegistrar를 사용하여 리포지토리를 등록할 것을 지시합니다.
  2. JpaRepositoriesRegistrar 실행: 이 클래스는 ImportBeanDefinitionRegistrar 인터페이스를 구현하여, registerBeanDefinitions 메서드를 통해 지정된 패키지를 스캔합니다. 여기서 스캔 대상 패키지는 @EnableJpaRepositories의 속성으로 지정할 수 있습니다.
  3. 리포지토리 인터페이스 감지: 스캔 과정에서 JpaRepository를 확장한 모든 인터페이스를 감지합니다.
  4. 빈 등록: 감지된 리포지토리 인터페이스들을 Spring 컨테이너에 빈으로 등록합니다. 이때 리포지토리 인터페이스는 SimpleJpaRepository라는 기본 구현체를 통해 실제 데이터 액세스 로직을 제공합니다.

스프링에서는 Configuration 클래스에 @EnableJpaRepositories를 사용하지만 스프링부트에서는 자동으로 등록이 된다.

 

쨋든 다시 주제로 넘어오자.

 

그다음 두번째 방법은 @Configuration과 @Bean을 이용하여 등록하는 방법이다.

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyService();
    }
}

 

보통 global.config에 추가하고 싶은 설정들을 적을텐데 이때 사용되는 빈 등록 방법이다. 추가 설명을 보자.

@Configuration

  • 역할: @Configuration은 하나 이상의 @Bean 메서드를 포함하는 클래스를 나타내며, 이 클래스는 Spring 컨테이너에 의해 빈 정의로 사용됩니다. 이 클래스 내의 메서드들이 생성하는 객체들이 빈으로 등록됩니다.

@Bean

  • 역할: @Bean은 메서드 수준에서 사용되며, 해당 메서드가 반환하는 객체를 Spring 컨테이너에 빈으로 등록합니다. @Configuration 클래스 내부나, @Component가 붙은 클래스 내부에서 사용될 수 있습니다.
  • 용도: 주로 복잡한 초기화 로직이 필요한 객체를 빈으로 등록할 때 사용됩니다.

여기서 또 의문점이 생긴다.

 

@Configuration과 @Bean은 왜 같이쓰고 차이는 뭐냐?

 

일단 결론으로는 

@Configuration은 내부적으로는 CGLIB 프록시를 사용하여 메서드 간 의존성을 처리한다. 사실 나도 아직 이 부분을 잘 몰라서 추후 정리하려고 한다. 일단 정의만 간단히 보면,

 

CGLIB이란?

Code Generator Library 의 약자로, 클래스의 바이트 코드를 조작하여 프록시 객체를 생성해 주는 라이브러리이다.

 

그래서 하는 일이 뭐냐?

CGLIB는 @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환해준다.

 

한마디로 싱글톤을 유지하려면 @Configulation을 항상 같이 써줘야된다는 것이다. 물론 같이 안써도 되지만 싱글톤을 보장할 수 없다..

 

728x90
반응형