Spring

[Spring 완전 정복 시리즈] 16편 - 컴포넌트 스캔 심화

dev-nadan 2025. 7. 29. 12:49
@SpringBootApplication // 내부에 @ComponentScan 포함
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

이전 편에서는 @ComponentScan을 통해 빈을 자동 등록하는 기본 기능과 @Autowired를 통한 생성자 주입까지 살펴봤다. 이번 편에서는 컴포넌트 스캔의 탐색 위치, 스테레오타입 어노테이션, 필터 사용법, 빈 충돌 처리 방식까지 더 깊게 들어가본다.

 


1. 컴포넌트 스캔의 탐색 위치

 

@ComponentScan은 어디서부터 클래스를 스캔할지를 정할 수 있다.

@ComponentScan(basePackages = "hello.core.member")

 

  • basePackages를 지정하면 해당 패키지부터 하위 패키지까지 스캔
  • 여러 개 지정도 가능: basePackages = {"hello.core", "hello.service"}

기본값은?

basePackages를 지정하지 않으면, @ComponentScan이 붙은 클래스의 패키지부터 시작된다.

 

그래서 일반적으로 설정 클래스(AutoAppConfig 등)는 최상단 루트 패키지에 두는 게 관례다.

@SpringBootApplication // 내부에 @ComponentScan 포함
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

 


2. 스테레오타입 어노테이션

@Component 외에도 스프링은 특정 용도에 맞춘 어노테이션들을 제공한다.

어노테이션 설명
@Controller 웹 MVC 컨트롤러 역할로 인식
@Service 비즈니스 로직 계층으로 인식 (실제로는 기능 없음)
@Repository 데이터 계층으로 인식, 예외 변환 처리
@Configuration 설정 정보로 인식, CGLIB을 이용한 싱글톤 보장 처리

이들은 모두 내부에 @Component가 포함된 메타 어노테이션이기 때문에 컴포넌트 스캔의 대상이 된다.

@Service
public class MyService {
    ...
}

※ 어노테이션은 상속이 안 되지만, 메타 어노테이션 덕분에 스프링이 인식할 수 있는 것이다.

 


3. 필터 기능

스캔 대상 중 일부만 포함하거나 제외하고 싶을 때는 필터를 사용할 수 있다.

@Configuration
@ComponentScan(
    includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
    excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
public class ComponentFilterAppConfig {
}

 

  • includeFilters: 특정 조건을 만족하는 클래스만 포함
  • excludeFilters: 특정 조건을 만족하는 클래스는 제외

테스트 예시

@Test
void filterScan() {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);

    BeanA beanA = ac.getBean("beanA", BeanA.class);
    assertThat(beanA).isNotNull();

    assertThrows(NoSuchBeanDefinitionException.class, () ->
        ac.getBean("beanB", BeanB.class));
}

 


4. 자동 등록과 수동 등록 충돌

 

컴포넌트 스캔으로 자동 등록한 빈과 개발자가 직접 등록한 수동 빈이 이름이 겹칠 경우 충돌이 발생한다.

자동 빈 vs 자동 빈

  • 동일한 이름의 자동 등록 빈이 두 개 이상이면 오류 발생

수동 빈 vs 자동 빈

  • 과거에는 수동 빈이 자동 빈을 무시하고 덮어씀
  • 현재는 오류를 발생시켜 애플리케이션이 실행되지 않도록 기본값이 바뀌었다
Consider renaming one of the beans or marking one of them as @Primary.

 


설계 원칙 정리

  • 컴포넌트 스캔은 @Component 계열 어노테이션이 붙은 클래스를 자동으로 등록
  • @Autowired로 의존성 자동 주입
  • @Controller, @Service, @Repository, @Configuration 등도 컴포넌트 대상
  • basePackages 지정 시 탐색 위치를 명시적으로 관리할 수 있음
  • 필터 기능을 통해 포함/제외 대상 설정 가능
  • 자동/수동 빈 충돌 시, 스프링 부트는 오류를 발생시킴