Spring

[Spring 완전 정복 시리즈] 19편 - 자동 주입 고급 기능과 전략 패턴 적용법

dev-nadan 2025. 7. 30. 13:06

이전 편에서는 스프링이 제공하는 다양한 의존관계 자동 주입 방식에 대해 살펴봤다. 이번 편에서는 그보다 더 나아가, 실무에서 자주 쓰이는 고급 자동 주입 기능들을 정리해본다.


빈이 여러 개일 때 발생하는 문제

@Autowired
private DiscountPolicy discountPolicy;

위 코드처럼 타입으로 자동 주입할 때, 해당 타입의 빈이 2개 이상 등록되어 있으면 예외가 발생한다.

 

스프링은 내부적으로 ac.getBean(DiscountPolicy.class)와 같은 방식으로 동작하기 때문에, 동일 타입의 빈이 여러 개면 어떤 빈을 선택해야 할지 알 수 없다.


해결 방법 1: @Primary 우선순위 지정

@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {
    // ...
}

 

  • 같은 타입의 빈이 여러 개일 때, @Primary가 붙은 빈이 자동 주입의 우선순위가 됨
  • 단, @Qualifier가 명시적으로 붙어 있으면 무시됨

해결 방법 2: @Qualifier 이름 지정 주입

@Component
@Qualifier("mainDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {
    // ...
}

@Autowired
public OrderServiceImpl(@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
    this.discountPolicy = discountPolicy;
}

 

  • @Qualifier해당 이름과 정확히 일치하는 빈을 찾는다.
  • 주입 대상이 없으면 NoSuchBeanDefinitionException 발생
  • @Qualifier빈 이름이 아닌 qualifier 값으로 주입을 유도하는 것이므로, 이름 일치를 강제하는 것과는 다름

실무에서 애노테이션 직접 만들어 사용하기

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}

 

  • 커스텀 애노테이션으로 의미를 명확하게 만들 수 있음
  • 컴파일 타임에 체크할 수 있어 오류 방지에 도움
  • 단, 웬만하면 스프링에서 제공하는 기능으로 충분하므로 남용 금지

조회한 모든 빈이 필요한 경우: Map, List 주입

@Component
public class DiscountService {
    private final Map<String, DiscountPolicy> policyMap;
    private final List<DiscountPolicy> policies;

    @Autowired
    public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
        this.policyMap = policyMap;
        this.policies = policies;
    }

    public int discount(Member member, int price, String discountCode) {
        DiscountPolicy discountPolicy = policyMap.get(discountCode);
        return discountPolicy.discount(member, price);
    }
}

 

  • 모든 빈을 한 번에 주입받고 싶을 때 유용
  • Map의 key는 빈 이름, value는 빈 인스턴스
  • 전략 패턴(Strategy Pattern)을 스프링에서 자연스럽게 구현할 수 있음

실무 운영 기준 - 자동 vs 수동 빈 등록

결론: 자동 등록을 기본으로 하자.

 

  • 자동 빈 등록은 컴포넌트 스캔만으로 대부분의 기능을 구성할 수 있어서 생산성이 뛰어남
  • 수동 등록은 설정 정보가 커지고 관리 포인트가 많아지므로 필요할 때만 사용

 

자동 등록이 더 나은 이유

 

  • 유지보수성: 변경 시 한 군데만 고치면 됨
  • 유연성: OCP, DIP 같은 설계 원칙을 지키면서 유연한 구조 가능

마무리: 실무에서 어떤 방식이 가장 좋은가?

  • 무조건 생성자 주입 + @RequiredArgsConstructor + @Primary 또는 @Qualifier 조합
  • 테스트 필요 시에는 setter 주입 허용
  • 필드 주입은 절대 사용하지 말 것
  • 전략 패턴을 쓰는 구조에서는 Map<String, Bean> 주입 방식 적극 활용