[LG CNS AM Inspire Camp 1기] Spring (4) - @Component & @ComponentScan

정성엽·2025년 1월 22일
2

LG CNS AM Inspire 1기

목록 보기
33/53

INTRO

이번 포스팅에서는 스프링의 자동 빈 등록 기능인 @Component와 @ComponentScan에 대해 알아보려고 한다.

기존에는 @Bean 어노테이션을 통해 수동으로 빈을 등록했지만, 이번에는 스프링이 자동으로 빈을 등록해주는 방법을 살펴보도록 하자 👀


1. @Component란?

@Component는 스프링이 클래스를 검색해서 빈으로 등록해주는 어노테이션이다.

즉, 클래스를 생성하고 @Component 어노테이션만 붙여주면 자동으로 빈으로 등록되기 때문에 설정 관련 코드를 크게 줄일 수 있다.

💡 @Component 사용하기

간단한 예제 코드를 통해 살펴보자

Sample Code

@Component
public class MemberDAO {
    private static long nextId = 0;
    private Map<String, Member> map = new HashMap<>();
    ...
}

이렇게 클래스에 @Component를 붙이면 스프링이 구동될 때 자동으로 빈으로 등록된다.

빈의 이름은 별도로 지정하지 않으면 클래스 이름의 첫 글자를 소문자로 바꾼 이름을 사용한다는걸 기억하자!

💡 빈 이름 지정하기

물론 다음과 같이 빈의 이름을 직접 지정할 수도 있다.

Sample Code

@Component("infoPrinter")
public class MemberInfoPrinter {
    @Autowired
    private MemberDAO memberDAO;
    ...
}

필자는 포스팅을 하면서 문득 @Bean과 @Component의 차이점이 궁금해졌다.

이 차이점을 한번 정리해보자

💡 @Bean VS @Component

구분@Bean@Component
사용 위치메서드 레벨에서 사용, @Configuration 클래스 내부에서 사용클래스 레벨에서 사용
사용 목적개발자가 제어할 수 없는 외부 라이브러리 등을 빈으로 등록할 때 사용개발자가 직접 작성한 클래스를 빈으로 등록할 때 사용
등록 방식수동으로 빈을 등록, 메서드에서 리턴하는 객체를 빈으로 등록자동으로 빈을 등록, 클래스 자체가 빈으로 등록

@Bean과 @Component를 비교하는 표를 작성해보니, 필자가 이전 포스팅에서 작성했던 방식과는 차이가 있었다.

이전에는 필자가 직접 작성한 클래스도 모두 @Bean으로 등록했지만, 실제로는 용도에 따라 구분해서 사용하는 것이 더 좋다는 것을 정리할 수 있었다.
(이번에 정리하면서 알게 되었다..)

이처럼 외부 라이브러리는 @Bean으로, 직접 작성한 클래스는 @Component로 등록하는 것이 스프링의 일반적인 사용 방식이라고 한다.

간단한 코드를 통해 한번 살펴보자

Sample Code

// @Bean 사용 예시
@Configuration
public class AppConfig {
    @Bean
    public RestTemplate restTemplate() { 
        return new RestTemplate();
    }
}

// @Component 사용 예시
@Component
public class UserService {
    private final UserRepository userRepository;
   
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

외부 라이브러리를 스프링 빈으로 등록해야 할 때는 해당 라이브러리의 코드를 직접 수정할 수 없기 때문에, @Bean 어노테이션을 사용하여 설정 클래스에서 수동으로 등록한다.

반면에 우리가 직접 작성한 클래스들은 @Component 어노테이션을 사용하여 자동으로 스프링 빈으로 등록할 수 있다 🙌


2. @ComponentScan이란?

@ComponentScan은 @Component 어노테이션이 붙은 클래스를 찾아 자동으로 빈으로 등록해주는 기능이다.

패키지 내의 클래스들을 스캔하여 빈으로 등록할 대상을 찾아준다.

💡 @ComponentScan 사용하기

예제 코드를 통해 살펴보자

Sample Code

@Configuration
@ComponentScan(basePackages = {"main"})
public class AppCtx {
    @Bean
    @Qualifier("printer")
    public MemberPrinter memberPrinter() {
        return new MemberPrinter();
    }

    @Bean
    @Qualifier("summaryPrinter")
    public MemberSummaryPrinter memberPrinter2() {
        return new MemberSummaryPrinter();
    }
}

여기서 basePackages는 스캔할 패키지의 시작 위치를 지정한다.

이렇게 설정하면 "main" 패키지와 그 하위 패키지에 있는 모든 @Component 어노테이션이 붙은 클래스를 스캔할 수 있다.

즉, @Component 어노테이션이 붙은 모든 클래스를 스프링 컨테이너에 빈으로 자동으로 등록된다!

💡 컴포넌트 스캔 대상

스프링은 다음과 같은 어노테이션이 붙은 클래스를 스캔 대상으로 포함한다

기본 스캔 대상

  • @Component: 일반적인 스프링 빈
  • @Controller: 스프링 MVC 컨트롤러
  • @Service: 비즈니스 로직 처리
  • @Repository: 데이터 접근 계층
  • @Aspect: AOP 관련 클래스 (공통 관심사를 분리한 프로그래밍 기법)
  • @Configuration: 스프링 설정 정보

💡 빈 이름 충돌 처리

컴포넌트 스캔 과정에서 같은 이름의 빈이 존재할 경우 충돌이 발생할 수 있다.

우선 충돌이 발생할 수 있는 상황을 코드로 작성해보자

Sample Code - 충돌 발생

@Component
public class MemberRegisterService {
    // memberRegisterService 이름으로 빈이 등록
    // 이미 같은 이름의 빈이 존재하면 충돌 발생!
}

Result View

이전에 살펴본 컴포넌트 스캔의 basePackages를 패키지 명으로 설정할 수 있다.

패키지 하위에는 또 다른 여러 개의 패키지가 존재할 수 있으며, 당연히 패키지 내부에서는 동일한 이름의 클래스가 존재할 수 있다.

따라서, 만약 동일한 이름으로 Main 하위의 패키지에서 사용하고 있다면, 컴포넌트 스캔 시 충돌이 발생할 수 있다.

필자는 사진과 같이 ConflictingBeanDefinitionException 예외가 발생하는 모습을 볼 수 있었다.

이러한 경우, 우리는 빈 이름을 명시적으로 지정하여 해결할 수 있다

Sample Code - 충돌 해결

@Component("ex02MemberRegisterService")
public class MemberRegisterService {
    // 다른 이름으로 등록되어 충돌 방지
}

이전에 살펴본 빈의 이름을 지정하는 방식을 사용하면 된다 🙌

💡 수동 등록 vs 자동 등록

만약 개발자가 같은 빈 이름으로 수동 등록(@Bean)과 자동 등록(@Component)을 했다면, 수동 등록된 빈이 우선권을 갖는다는 것을 기억하자!

간단한 코드를 통해 예시를 살펴보자

Sample Code

// MemberDAO.java
@Component
public class MemberDAO {
	...
}

// AppCtx.java
@Configuration
@ComponentScan(basePackages = {"main"})
public class AppCtx {
    @Bean
    public MemberDAO memberDAO() {
        return new MemberDAO();
    }
    ...
}

위 코드에서는 MemberDAO가 두 가지 방식으로 등록되고 있다.

  • @Component 어노테이션을 통한 자동 등록
  • 설정 파일에서 @Bean을 통한 수동 등록

이런 경우 수동 등록이 우선권을 갖는다. 왜냐하면 수동 등록이 더 구체적인 설정이기 때문이다.

하지만 이처럼 자동 등록과 수동 등록이 섞여 있으면 빈 등록이 중복되거나 덮어쓰여질 수 있어 잠재적인 버그의 원인이 될 수 있다.

따라서 빈 등록 방식은 한 가지로 일관되게 유지하는 것이 좋다!


OUTRO

이번 포스팅에서는 스프링의 자동 빈 등록 기능인 @Component와 @ComponentScan에 대해 알아보았다.

이 기능들을 사용하면 반복적인 빈 등록 코드를 줄일 수 있고, 더욱 효율적으로 스프링 빈을 관리할 수 있다.

다만, 빈 이름 충돌이나 수동/자동 등록의 혼용과 같은 주의사항들을 잘 고려해서 사용하자 👊

profile
코린이

0개의 댓글

관련 채용 정보