INFLEARN

[스프링 핵심 원리 - 기본편] 13. 조회 Bean이 2개 이상일 경우(@Qualifier vs @Primary)

ch010104 2026. 3. 20. 23:59

1. @Autowired 필드 명 매칭

@Autowired는 먼저 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름이나 파라미터 이름으로 빈 이름을 추가 매칭합니다.

  • 기존 코드:
@Autowired
private DiscountPolicy discountPolicy
  • 필드 명을 빈 이름으로 변경:
@Autowired
private DiscountPolicy rateDiscountPolicy

정리: 타입 매칭 결과가 2개 이상일 때, 필드 명(또는 파라미터 명)과 일치하는 빈 이름을 찾아 주입합니다.


2. @Qualifier 사용

@Qualifier는 빈에 추가 구분자를 붙여주는 방법입니다. 빈 이름을 바꾸는 것이 아니라, 주입 시 식별할 수 있는 별칭을 주는 것입니다.

  • 빈 등록 시:
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
  • 주입 시 (생성자 예시):

참고: 만약 @Qualifier("mainDiscountPolicy")를 못 찾으면, 해당 이름의 스프링 빈을 추가로 찾습니다. 하지만 @Qualifier 용도로만 사용하는 것이 명확합니다.

// 같은 타입(DiscountPolicy) 2개가 @Component로 등록되어 있는데, 어떤 것을 꺼내야와야할지 몰라서 에러가 발생하는 것우
// @Qualifier 를 이용해서 Bean을 지정 가능(Bean의 이름이 바뀌는 것은 아니다)
// 이 예시에서는 RateDiscountPolicy.java에서 @Qualifier("mainDiscountPolicy")가 붙어 있어서 RateDiscountPolicy를 의존성 주입해줌
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
                        @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
}

3. @Primary 사용 (가장 편리한 방법)

@Primary는 우선순위를 정하는 방법입니다. 여러 빈이 매칭되면 @Primary가 붙은 빈이 우선권을 가집니다.

  • 우선권 부여:
@Component
@Primary // 메인으로 사용할 빈에 선언// -> Bean 타입이 겹칠 경우, 최상위 우선순위를 가지게 함
// @Qualifire vs @Primary 의 선택이지만, 둘 중에서 우선순위는 @Qualifire가 가져감(Spring은 자동보다는 수동이, 넓은 범위보다 좁은 범위의 선택권이 우선순위임)
public class RateDiscountPolicy implements DiscountPolicy {}

@Component
public class FixDiscountPolicy implements DiscountPolicy {}
  • 사용 코드:
// 주입 시 별도의 어노테이션 없이 평범하게 주입받으면 @Primary가 선택됨
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
}

4. @Primary vs @Qualifier (우선순위와 활용)

  • 활용:
    • 메인 DB 커넥션처럼 자주 쓰는 빈은 @Primary로 설정해 코드 가독성을 높이고, 서브 DB처럼 가끔 쓰는 빈은 @Qualifier를 지정해서 명시적으로 획득하는 방식이 깔끔
  • 우선순위:
    • 스프링은 항상 좁은 범위(상세한 설정)가 우선권을 가짐
    • 따라서 넓은 범위인 @Primary보다 상세하게 지정하는 @Qualifier가 우선순위가 더 높음

5. Lombok 환경에서의 해결 방법 (핵심 추가)

Lombok의 @RequiredArgsConstructor를 사용할 때는 생성자가 자동으로 만들어지기 때문에 생성자 파라미터에 @Qualifier를 직접 적을 수 없음

방법 A: lombok.config 설정 (권장)

  • 프로젝트 루트에 lombok.config 파일을 만들고 아래 설정을 추가하면, 필드에 적은 @Qualifier가 생성자 파라미터로 자동 복사
# lombok.config
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier

적용 코드:

@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    
    @Qualifier("mainDiscountPolicy") // 설정 덕분에 생성자 주입 시 정상 동작
    private final DiscountPolicy discountPolicy;
}

방법 B: @Primary 활용

  • Lombok과 함께 쓸 때 가장 속 편한 방법
  • Bean 클래스에 @Primary만 붙여두면 Lombok 코드를 수정할 필요가 없음
    @Component
    @Primary
    public class RateDiscountPolicy implements DiscountPolicy {}
    
    // 서비스 코드는 수정 불필요
    @RequiredArgsConstructor
    public class OrderServiceImpl implements OrderService {
        private final DiscountPolicy discountPolicy; 
    }
    ​