본문 바로가기
개발일지/Spring

컴포넌트 스캔

by 꾸주니=^= 2025. 2. 18.

이 포스트는 인프런 사이트에 김영한 - 스프링 핵심원리 강의를 보고 정리한 포스터입니다.

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8

 

스프링 핵심 원리 - 기본편 강의 | 김영한 - 인프런

김영한 | , 스프링 핵심 원리를 이해하고, 성장하는 백엔드 개발자가 되어보세요! 📢 수강 전 확인해주세요! 본 강의는 자바 스프링 완전 정복 시리즈의 두 번째 강의입니다. 우아한형제들 최연

www.inflearn.com

 

스프링의 컴포넌트 스캔에 대해 알아보겠습니다.


🔆 컴포넌트 스캔 (Component Scan) ?

스프링에서는 @Bean을 사용하여 하나하나 빈을 등록할 수도 있지만,
빈이 많아질 경우 자동으로 스캔해서 빈을 등록하는 기능이 필요하다.
 이 기능이 바로 "컴포넌트 스캔(Component Scan)"

 @ComponentScan : @Component가 붙은 클래스를 찾아 자동으로 빈으로 등록
 @Autowired : 자동으로 의존관계를 주입

 


🔆 컴포넌트 스캔 적용하기

@Configuration
@ComponentScan(
    excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}

컴포넌트 스캔을 사용하려면 먼저 @ComponentScan을 설정 정보에 붙여주면 됩니다.

 @ComponentScan : 컴포넌트 스캔 활성화
 excludeFilters: 특정 클래스를 스캔에서 제외

 


🔆  @Component를 이용한 자동 빈 등록

각 클래스에 @Component를 추가하면 자동으로 스프링 빈으로 등록됩니다.

 

 예제: Repository, Service, Policy 클래스 자동 등록

@Component
public class MemoryMemberRepository implements MemberRepository {}
@Component
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository;

    @Autowired  // 자동 의존관계 주입
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

이전에 AppConfig에서는 `@Bean`으로 직접 설정 정보를 작성했고, 의존관계도 직접 명시했습니다.
이제는 이런 설정 정보 자체가 없기 때문에, 의존관계 주입도 클래스 안에서 해결해야 합니.

`@Autowired` 의존관계를 자동으로 주입해줍니다. 

 


🔆  @Autowired를 통한 의존관계 자동 주입

@Autowired를 생성자에 붙이면 자동으로 해당 타입의 빈을 주입
getBean(MemberRepository.class)와 동일하게 동작

 

 여러 의존관계를 한 번에 주입 가능

@Component
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

 


🔆  컴포넌트 스캔 실행 테스트

@Test
void basicScan() {
    ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
    MemberService memberService = ac.getBean(MemberService.class);
    assertThat(memberService).isInstanceOf(MemberService.class);
}

➡ 실행하면, 스프링이 자동으로 빈을 등록하고 주입해주는 것을 확인할 수 있습니다.

 

 로그 확인

Identified candidate component class:
.. RateDiscountPolicy.class
.. MemberServiceImpl.class
.. MemoryMemberRepository.class
.. OrderServiceImpl.class

로그를 보면 컴포넌트 스캔이 동작하는 것을 확인할 있습니다.

 


🔆 @ComponentScan 탐색 위치 지정

모든 클래스를 탐색하면 성능 저하가 발생할 수 있음
 특정 패키지부터 탐색하도록 설정 가능

@ComponentScan(basePackages = "hello.core")

 basePackages : 특정 패키지를 시작점으로 설정
 basePackageClasses = MyClass.class : 특정 클래스 패키지 기준

 


🔆 컴포넌트 스캔과 자동 의존관계 주입 동작 과정

 

🌀 @ComponentScan

`@ComponentScan` `@Component` 붙은 모든 클래스를 스프링 빈으로 등록합니다.

이때 스프링 빈의 기본 이름은 클래스명을 사용하되 앞글자만 소문자를 사용합니다.
이름 기본 전략: MemberServiceImpl 클래스 memberServiceImpl
이름 직접 지정: 만약 스프링 빈의 이름을 직접 지정하고 싶으면
`@Component("memberService2")` 이런식으로 이름을 부여하면 됩니.

 

🌀 @Autowired 의존관계 자동 주입

생성자에 `@Autowired` 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입합니다.
이때 기본 조회 전략은 타입이 같은 빈을 찾아서 주입합니다.

`getBean(MemberRepository.class)` 동일하다고 이해하면 됩니다.

 

생성자에 파라미터가 많아도 다 찾아서 자동으로 주입합니다.

 


🔆 @ComponentScan의 기본 스캔 대상

어노테이션 역할
@Component 기본적인 컴포넌트 등록
@Controller MVC 컨트롤러 역할
@Service 비즈니스 로직 처리
@Repository 데이터 접근 계층 역할
@Configuration 스프링 설정 클래스

 @Component가 내부적으로 포함되어 있기 때문에 자동으로 스캔됨
 스프링이 추가적인 기능을 부여함 (@Repository는 예외 변환 지원 등)

 


🔆 필터 (Filter) 사용하기

includeFilters : 특정 대상만 추가
excludeFilters : 특정 대상 제외

 

 필터를 사용한 예제

@ComponentScan(
    includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
    excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)

 @MyIncludeComponent 붙은 클래스는 등록, @MyExcludeComponent 붙은 클래스는 제외

 


🔆 FilterType 옵션

FilterType은 5가지 옵션이 있습니다.

ANNOTATION 기본값, 애노테이션을 인식해서 동작 org.example.SomeAnnotation
ASSIGNABLE_TYPE 지정한 타입과 자식 타입을 인식해서 동작 org.example.SomeClass
ASPECTJ AspectJ 패턴 사용 org.example..*Service+
REGEX 정규 표현식 org\.example\.Default.*
CUSTOM TypeFilter이라는 인터페이스를 구현해서 처리 org.example.MyTypeFilter

 

예를 들어 BeanA도 빼고 싶으면 다음과 같이 추가하면 됩니다.

@ComponentScan(
    includeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes =
        MyIncludeComponent.class),
    },
    
    excludeFilters = {
        MyExcludeComponent.class),
        @Filter(type = FilterType.ANNOTATION, classes =
        @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = BeanA.class)
}

 `@Component` 충분하기 때문에, `includeFilters` 사용할 일은 거의 없습니다.
 `excludeFilters` 여러가지 이유로 간혹 사용할 때가 있지만 많지는 않습니다.

특히 최근 스프링 부트는 컴포넌트 스캔을 기본으로 제공하는데, 개인적으로는 옵션을 변경하면서 사용하기 보다는 스프링의 기본 설정에 최대한 맞추어 사용하는 것을 권장하고, 선호하는 편입니다.

 


🔆 빈 중복 등록과 충돌

 

 자동 빈 등록 vs 자동 빈 등록

➡ 같은 이름의 빈이 자동 등록되면 오류 발생
 ConflictingBeanDefinitionException 발생

 

 수동 빈 등록 vs 자동 빈 등록

 수동 빈이 자동 빈을 오버라이딩
✔ 스프링 부트에서는 충돌 시 오류 발생하도록 기본 설정 변경됨

 


정리

 @ComponentScan을 사용하면 자동으로 빈을 등록할 수 있습니다.
 @Autowired를 통해 자동으로 의존관계를 주입할 수 있습니다.
 @Component, @Service, @Repository 등 다양한 어노테이션을 활용할 수 있습니다.
 excludeFilters  includeFilters를 사용해 특정 클래스만 선택적으로 스캔 가능
 빈 이름 충돌을 조심해야 하며, 수동 빈 등록이 우선 적용됨

📢 컴포넌트 스캔을 활용하면 불필요한 설정 코드 없이, 효율적으로 스프링 빈을 등록할 수 있습니다! 🚀