Spring

@Bean, @Configuration, @ComponentScan, @Import 정리

천방지축 개발노트 2025. 3. 19. 23:11

우리가 애플리케이션을 만들기 위해서는

"어떤 클래스의 오브젝트를 만들고 그들을 어떻게 연결할 것인가?"에 대한 정보가 필요하다.

이 구성 정보를 스프링에게 전달하는 여러 가지 방법이 있는데,

컨테이너 초기화 과정에서 빈을 동적으로 생성하도록 약속된 애노테이션들에 대해 알아보자.

 


Bean을 등록시키는 중요 애노테이션

1. @Bean

@Configuration
public class PaymentConfig {

    @Bean
    public PaymentService paymentService() {
        return new PaymentService();
    }
}

@Bean 애노테이션이 붙어있는 메소드는 'Bean을 만드는 메소드'를 의미한다. 쉽게 return 되는 객체가 빈으로 등록된다고 이해하면 된다. 이 등록 방식은 애플리케이션의 구성을 위해 직접적으로 객체를 추가하기 위한 용도이기 때문에, 일반적으로 @Configuration클래스 내에 존재한다.

참고로 컨테이너 내에서 Bean으로 등록되는 오브젝트들은 아이디(Bean Nama)가 하나씩 부여되는데, 팩토리 메소드로 만드는 경우 디폴트 값으로는 메소드 이름을 따라간다. 별도의 이름을 지정하려면 메소드명을 변경하거나 아니면 @Bean("이름")으로 변경이 가능하다.

 

 

2. @Configuration

'Config 정보를 가지고 있는 클래스'를 의미한다.

스프링 컨테이너는 전체 애플리케이션의 '구성 정보'를 필요로 한다고 얘기했었다. 여기서 구성이라는 단어는 '어떻게 형성되는가?'라는 건데, 이게 곧 @Configuration 애노테이션의 의미(풀 명칭으로는 Configuration Metadata)이기도 하다. @Configuration이 붙은 클래스는 전체 애플리케이션 및 Container를 구성하는데 필요한 정보(클래스들 간의 어떤 의존 정보 등)를 담고 있다고 약속되어 있기 때문에 애플리케이션 컨텍스트(Spring Container)에 가장 처음 등록된다는 특징이 있다. 그래서 이와 같은 이유로 필요한 오브젝트들을 전체적으로 등록시키는 @ComponentScan이 메타 애노테이션으로 함께 붙어있는 모습을 자주 볼 수 있다.

 

스프링에서 제공하는 구성 정보 클래스들 외에도 해당 애노테이션을 사용하여 우리가 직접 구성 정보를 작성할 수 있지만, 생각해 볼 만한 부분이 있다. 사실 @Configuration는 ProxyBeanMethods라는 Attribute를 가지고 있다. 그리고 이 속성의 디폴트 값은 true이다. true의 의미는 오브젝트를 생성하는 코드가 여러 번 실행된다고 하더라도 딱 한 개의 오브젝트를 재사용하도록 한다는 특징이 있다. 다시 말해, @Configuration라는 특별한 애노테이션을 달아놓은 클래스에는 별도로 false 옵션을 주지 않는 한 스프링은 생성자가 여러 번 호출이 되어도 딱 하나의 오브젝트만 생성/관리된다는 것이다. 다시 말해, 여러 번 생성하거나 또는 사용하는 오브젝트가 여러 개가 된다고 할지라도 동일한 오브젝트가 사용됨을 스프링이 보장한다. @Bean을 이용해 여러 번 생성해도 동일한 인스턴스를 반환하지만, 반대로 속성값이 false라면 새로운 객체를 반환한다. 추가적으로 '@Configuration이 붙어있는 클래스는 구성 정보 클래스다'라는 것을 클래스 명으로도 표현하기 위해 보통 postfix로 "... Config" 식으로 이름이 붙어있는 것이 일반적인 특징이다.

 

 

3. @Component

@Component는 스프링에서 빈을 등록하는 가장 기본적인 애노테이션으로 이 애노테이션이 붙은 클래스는 'Bean Object가 될 대상'이라는 것을 의미한다. Spring Container의 "컴포넌트 스캐너"는 해당 애노테이션이 붙은 클래스를 모두 찾아서 빈으로 등록하기 때문에 개발자가 new 키워드로 객체를 생성하지 않아도 Spring이 알아서 생성하고 관리해 준다.

우리가 많이 사용하는 @Controller, @Service, @Repository 또한 @Component의 확장형으로 가독성 및 역할을 명확히 하기 위해 추가된 애노테이션이다. 그리고 @Component가 붙은 클래스들은 자동으로 빈으로 등록하는 용도이긴 하지만, 이를 스캔하는 역할은 @ComponentScan이 담당한다.

 

 

4. @ComponentScan

@ComponentScan은 @Component가 붙은 클래스를 찾아서 오브젝트를 생성하고 Bean으로 등록하는 역할을 담당한다. 알고 있어야 할 점은 @ComponentScan도 basePackages라는 속성이 존재하며, 이 옵션을 별도로 지정하지 않는 한 선언된 클래스가 포함돼있는 패키지를 기준으로 내부에 존재하는 클래스들을 스캔한다는 것이다. Spring Boot 프로젝트에서는 @SpringBootApplication 내부에 메타 애노테이션으로 @ComponentScan이 포함되어 있기 때문에, 별도로 우리가 선언하지 않아도 자동으로 동작하는 것이다.

하지만 위 사진과 같이 @ComponentScan(여기선 @SpringBootApplication)이 붙은 클래스가 특정 패키지 내에 있다면, 기본적으로 해당 패키지와 그 하위 패키지 내의 컴포넌트들만 스캔 대상이 된다. 즉, main이라는 패키지 밖에 존재하는 HelloController 등의 클래스는 스캔 대상에서 제외된다.

 

 

5. @Import

어떤 클래스에 @Import 애노테이션이 붙어있다면, 해당 클래스가 빈으로 등록이 될 때! @Import의 attribute에 명시된 클래스들까지 빈으로 함께 등록함을 의미한다. 즉, 스캔 대상은 아니었지만 attribute로 클래스 정보를 넘겨줌으로써 Bean을 명시적으로 추가하는데 사용한다. 해당 애노테이션은 일반적으로 @Configuration 클래스에서 작성된다.

 

이 @Import를 이용하면 @Configuration 애노테이션이 붙어 설정 전용 빈이라고 명시된 클래스 외에 일반 빈 클래스를 빈으로 등록할 수 있긴 하다. 하지만 해당 클래스가 나중에라도 스캔 대상으로 사용될 수도 있고 클래스가 빈으로 등록된다는 의미로 @Component 애노테이션을 붙여놓는 것이 바람직한 것 같다.

추가적으로 @Import는 어떤 목적으로 import를 했을까? 라는 의문을 갖게 만든다. 그래서 Spring Boot에서는 @EnableXXX 형식의 애노테이션을 정의하고 @Import를 내부적으로 사용하는 방식을 활용하고 있다. 한 가지 예로는 @SpringBootApplication 내에 존재하는 @EnableAutoConfiguration가 있다. 찾아보면 그냥 @Import(AutoConfigurationImportSelector.class)자체를 SpringBootApplication의 메타 애노테이션으로 붙여 넣을 수도 있지만, 이를 한번 감싸서 어떠한 기능을 위해 명시적으로 등록하고 있는지를 표현하고 있음을 알 수 있다.