INFLEARN

[스프링 핵심 원리 - 기본편] 10. ComponentScan

ch010104 2026. 3. 15. 22:32

1. 컴포넌트 스캔과 자동 주입이란?

  • 컴포넌트 스캔: @Bean으로 일일이 등록하지 않아도, @Component 애노테이션이 붙은 클래스를 스캔하여 자동으로 스프링 빈에 등록하는 기능입니다.
  • @Autowired: 생성자에 붙이면 스프링 컨테이너가 해당 타입에 맞는 빈을 찾아 자동으로 주입해줍니다.


2. 코드 적용 예시

① 설정 정보 등록 (AutoAppConfig.java)

@ComponentScan을 붙여줍니다. 기존 AppConfig와의 충돌을 막기 위해 @Configuration이 붙은 클래스는 제외하는 필터를 추가했습니다.

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

② 빈 등록 대상에 @Component 추가

스캔 대상이 될 클래스들에 애노테이션을 붙입니다.

  • MemoryMemberRepository, RateDiscountPolicy 등에 @Component 추가

③ 의존관계 주입이 필요한 곳에 @Autowired 추가

@Component
public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository;

    @Autowired // 컨테이너가 MemberRepository 타입의 빈을 찾아 자동으로 주입함
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

3. 탐색 위치와 기본 스캔 대상

탐색 위치 지정

  • basePackages: 탐색할 패키지의 시작 위치를 지정합니다.
  • 권장 방법: 프로젝트 최상단 패키지에 설정 정보 클래스를 두고 basePackages 지정을 생략하는 것입니다. 스프링 부트의 @SpringBootApplication도 이 방식을 따릅니다.
package com.example.spring_study; // AutoAppConfig의 pacakge

import com.example.spring_study.member.MemoryMemberRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(
        // 현재 프로젝트의 AppConfig 파일은 수동으로 Bean을 등록하는 예제임
        // 이를 제외해주지 않으면, 충돌이 남(실무에선 거의 x)
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)

        // packages를 지정해서 ComponentScan 대상이 되는 영역을 지정할 수 있음
        // basePackages = "com.example.spring_study.member",

        // AutoAppConfig 클래스가 있는 파일의 package를 대상으로 ComponentScan을 진행(여기에선 com.example.spring_study임)
        // basePackageClasses = AutoAppConfig.class

        // 위의 것들을 지정 안하면, @ComponentScan이 붙은 설정 정보 package가 시작 위치가 됨(해당 package 및 하위 폴더 전체에서 Scan)
)
// @Component 라고 붙은 것을 모두 찾아서 자동으로 Spring Bean으로 등록해줌
public class AutoAppConfig {
}

컴포넌트 스캔 기본 대상

다음 애노테이션들은 @Component를 포함하고 있어 자동으로 스캔 대상이 됩니다.

  • @Controller: 스프링 MVC 컨트롤러로 인식
  • @Service: 특별한 처리는 없으나 비즈니스 로직 계층임을 명시
  • @Repository: 데이터 접근 계층으로 인식, 데이터 계층 예외를 스프링 예외로 변환
  • @Configuration: 스프링 설정 정보로 인식, 싱글톤 유지 처리

4. 필터 (Filter)

  • includeFilters: 스캔 대상을 추가
  • excludeFilters: 스캔 대상에서 제외
    @Configuration
    @ComponentScan(
            // type = FilterType.ANNOTATION 은 default 값이므로 생략 가능
            includeFilters = {@Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class)},
            excludeFilters = {@Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)}
    )
     static class ComponentFilterAppConfig{

    }

참고: 최근 스프링 부트는 기본 스캔 기능을 잘 제공하므로 필터를 커스텀하게 사용하는 일은 많지 않습니다.


5. 중복 등록과 충돌 상황

① 자동 빈 vs 자동 빈

  • 이름이 같은 빈이 두 개 이상 자동 등록되면 ConflictingBeanDefinitionException 예외가 발생합니다.

② 수동 빈 vs 자동 빈

  • 과거 스프링: 수동 빈이 우선권을 가지며 자동 빈을 오버라이딩합니다. (로그 기록 남음)
  • 최근 스프링 부트: 설정이 꼬이는 것을 방지하기 위해 기본적으로 오류가 발생하도록 변경되었습니다.
    • 오류 메시지: Consider renaming one of the beans or enabling overriding...
package com.example.spring_study; // AutoAppConfig의 pacakge

import com.example.spring_study.member.MemoryMemberRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
// @Component 라고 붙은 것을 모두 찾아서 자동으로 Spring Bean으로 등록해줌
public class AutoAppConfig {

    // MemoryMemberRepository에서 ComponentScan으로 자동으로 이미 Bean에 등록되었는데, 수동으로 등록하면 충돌이 날까??
    // 자동 vs 자동, 즉 componentScan끼리의 충돌(Bean의 이름이 같음)은 에러가 남
    // 수동 vs 자동의 경우, 수동이 우선권을 가짐(수동 Bean이 자동 Bean을 오버라이딩함) -> 에러가 안남
    // 최근 Spring Boot에서는 오류가 나도록 바뀜(properties에서 설정으로 오버라이딩되게 변경 가능)
    @Bean(name = "memoryMemberRepository")
    MemoryMemberRepository memoryMemberRepository() {
        return new MemoryMemberRepository();
    }

}

spring.application.name=spring-study

# 수동 vs 자동 ComponentScan 일 경우, 수동 Bean이 자동 Bean을 오버라이딩하는 설정
spring.main.allow-bean-definition-overriding=true