[Spring] 스프링 기본3 - 컴포넌트 스캔

easyone·2026년 9월 17일

Spring

목록 보기
27/29

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

@Bean으로 하나하나 설정 정보 등록하면 번거롭기 때문에, 반복을 줄이는 것이 필요하다. 그래서 스프링은 이러한 설정 정보 없이도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.

의존관계를 자동으로 주입해주는 @Authowired 기능도 제공한다.
타입에 맞는걸 주입해주는? ac.getBean(MemberRepository.class) 이런식으로 자동으로 코드가 들어가는 것이다.
생성자에서 여러 의존관계도 주입이 가능하다.

컴포넌트 스캔 사용

@ComponentScan 을 설정 정보에 붙여주면 된다. 별도로 @Bean 으로 클래스를 등록하지 않아도 된다.

붙여준 어노테이션은, @Component가 붙은 모든 클래스를 스프링 빈으로 등록한다. 기본 클래스명을 사용하고 맨 앞글자만 소문자를 사용한다.

@Component("빈 이름") 이렇게 지정할 수도 있다. 기본적으로는 MemberServiceImpl -> memberServiceImpl 이렇게 주입된다.

@Autowired 의존관계 자동 주입

  • 생성자에 지정하면 스프링 컨테이너가 자동으로 해당 빈을 찾아서 주입한다.
  • 타입이 같은 빈을 찾아서 주입한다.
  • 생성자에 파라미터가 많아도, 다 찾아서 자동 주입한다.

탐색 위치와 기본 스캔 대상

탐색할 패키지의 시작 위치 지정하기

모든 자바 클래스를 다 컴포넌트 스캔하면 시간이 오래 걸리기 때문에, 필요한 위치부터 탐색하도록 시작 위치 지정이 가능하다. 라이브러리까지 다 탐색하고 그런다면 오래걸린다.

@ComponentScan으로 지정을 안하면 디폴트가 되는데, 그냥 어노테이션만 붙이면, 설정 정보 클래스의 패키지가 시작 위치가 된다.

요즘에는 설정 정보 클래스의 위치를 프로젝트 최상단에 두는 방식으로 한다. 즉 상위 프로젝트가 com.hello라면 AppConfig같은 메인 설정 정보를 두고, basePackages 지정은 생략하는 것이다.

스프링 부트를 사용하면 @SpringBootApplication 이라는 대표 시작 정보인 어노테이션을 시작 루트에 둔다.
이 설정 안에 @ComponentScan이 들어있다. 불필요한것만 exclude로 제외하면 된다.

컴포넌트 스캔 기본 대상

@Component 뿐만 아니라 다음 내용도 대상에 포함된다.

@Component : 컴포넌트 스캔에서 사용
@Controller : 스프링 MVC 컨트롤러에서 사용, 컨트롤러로 인식
@Service : 스프링 비즈니스 로직에서 사용, 특별한 처리는 안하고 비즈니스 계층 인식에 도움이 된다.
@Repository : 스프링 데이터 접근 계층에서 사용, 데이터 계층의 예외를 스프링 예외로 변환해준다. 특정 예외가 터지면 서비스 계층까지 올라온다. DB마다 던지는 예외가 다르기 때문에, 서비스코드가 특정 DB 접근 기술에 의존하는걸 방지하기 위해서, 스프링 자체에서 추상화해서 변환해주는 것이다.

JDBC의 SQLException
JPA의 PersistenceException
MyBatis의 예외

-> DataAccessException (스프링 추상화 예외) 이렇게 하나로 통일해준다. 서비스계층은 DataAccessException 이 예외만 처리하면 된다.
@Configuration : 스프링 설정 정보에서 사용, 스프링 빈이 싱글톤을 유지하도록 추가 처리를 한다.

이것들도 내부적으로 @Component를 포함하고 있는 것이다.

그런데 애노테이션은 상속 관계라는 것은 없다. 이렇게 애노테이션이 특정 애노테이션을 들고있는것을 인식할 수 있는 건 자바가 아니라 스프링이 지원하는 기능이다.

애노테이션 자체가 메타정보이기 때문에 특별한 처리를 안하는 경우도 있다.
useDefaultFilters 옵션이라는게 기본으로 켜져있는데 이 옵션을 끄면 기본 스캔 대상들이 제외된다.

필터

includeFilters : 컴포넌트 스캔 대상을 추가로 지정한다.
excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정한다.

근데 실무에서는 잘 안쓴다고 한다.

@Target(ElementType.TYPE) // 클래스에만 붙일 수 있음 
@Retention(RetentionPolicy.RUNTIME) // 리플렉션으로 읽을 수 있게 런타임까지 정보 유지
@Documented
public @interface MyIncludeComponent { // 인터페이스로 선언하면 애노테이션이 생성됨
}
@MyIncludeComponent
public class BeanA {
}

이렇게하면 BeanA에 MyIncludeComponent라는 설정이 붙는다.

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

이렇게 BeanB에는 MyExcludeComponent라는 어노테이션을 붙여주고, excludeFilters로 추가하면 스프링 빈에 등록되지 않는다.

FilterType 옵션

  • ANNOTATION: 기본값, 애노테이션을 인식해서 동작한다.즉 어노테이션이 붙어있는지로 판단해서 , 이게 붙어있는 클래스를 찾아라 이런것이다.
    ex) org.example.SomeAnnotation
  • ASSIGNABLE_TYPE: 지정한 타입과 자식 타입을 인식해서 동작한다. 특정 클래스나 인터페이스를 상속,구현했는지로 판단하고 애노테이션이 아니라 타입 자체로 거른다.
    ex) org.example.SomeClass
  • ASPECTJ: AspectJ 패턴 사용해서, 조금 더 복잡한 조건을 표현식으로 쓸 수 있는데 거의 안쓴다.
    ex) org.example..*Service+
  • REGEX: 정규 표현식, 클래스 풀네임을 매칭한다.
    ex) org\.example\.Default.*
  • CUSTOM: TypeFilter 이라는 인터페이스를 구현해서 처리한다. 이 클래스를 포함할지 말지를 boolean으로 직접 판단하는 match 메서드를 작성한다.
    ex) org.example.MyTypeFilter

중복 등록과 충돌

컴포넌트 스캔에서 같은 빈 이름이 등록되면, 오류를 발생시킨다.

수동 빈 vs 자동 빈 충돌

수동 vs 자동 이름 겹침 → 원래는 수동이 이겨서(오버라이딩) 우선권을 가진다.
근데 스프링 부트는 기본적으로 에러나게 바꿔놓는다.

Overriding bean definition for bean 'memoryMemberRepository' with a different
definition: replacing

로그를 친절하게 이렇게 띄워준다.
그런데 스프링에서 이런 오류가 나는게 그냥 설정이 꼬여서 순수 실수에 의한 것이기 때문에 스프링 부트에서는 이를 무조건 잡을 수 있게, 충돌이 발생한다. 다음과 같이 에러가 발생한다.

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

자동 vs 자동

자동 vs 자동 이름 겹침 → 무조건 에러를 발생시킨다. (ConflictingBeanDefinitionException)

profile
백엔드 개발자 지망 대학생

1개의 댓글

comment-user-thumbnail
2026년 10월 5일

껌뽀넌뜨 뚜깬

답글 달기