본문 바로가기
개발일지/Spring

의존관계 자동 주입 2편

by 꾸주니=^= 2025. 2. 19.

이 포스트는 인프런 사이트에 김영한 - 스프링 핵심원리 강의를 보고 정리한 포스터입니다.

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8

 

스프링 핵심 원리 - 기본편 강의 | 김영한 - 인프런

김영한 | , 스프링 핵심 원리를 이해하고, 성장하는 백엔드 개발자가 되어보세요! 📢 수강 전 확인해주세요! 본 강의는 자바 스프링 완전 정복 시리즈의 두 번째 강의입니다. 우아한형제들 최연

www.inflearn.com

 

스프링에서는 같은 타입의 빈이 여러 개 존재할 때, 이를 어떻게 해결할 것인지에 대한 다양한 방법을 제공합니다.

이번 포스트에서는  빈이 2개 이상일 때 발생하는 문제와 해결 방법(@Autowired 필드 명, @Qualifier, @Primary), 애노테이션을 직접 만들어 해결하는 방법, 조회한 빈이 모두 필요할 때 List와 Map을 사용하는 방법, 그리고 자동과 수동 등록의 올바른 실무 운영 기준에 대해 자세히 정리하겠습니다.

 

이전 포스트 의존관계 자동 주입 1편을 보고 오면 도움됩니다.

https://uplifted.tistory.com/entry/%EC%9D%98%EC%A1%B4%EA%B4%80%EA%B3%84-%EC%9E%90%EB%8F%99-%EC%A3%BC%EC%9E%85-1%ED%8E%B8

 

의존관계 자동 주입 1편

이 포스트는 인프런 사이트에 김영한 - 스프링 핵심원리 강의를 보고 정리한 포스터입니다.https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8 스프

uplifted.tistory.com


🔆 조회 대상 빈이 2개 이상일 때 발생하는 문제

@Autowired는 기본적으로 타입을 기준으로 빈을 주입합니다.
그런데 같은 타입의 빈이 2개 이상 존재할 경우 NoUniqueBeanDefinitionException 오류가 발생합니다.

 

✅ 문제 상황

@Autowired
private DiscountPolicy discountPolicy;

위 코드에서 DiscountPolicy 타입을 가진 빈이 2개 이상 존재하면 다음과 같은 예외가 발생합니다.

 

NoUniqueBeanDefinitionException: No qualifying bean of type 'DiscountPolicy' available: expected single matching bean but found 2: fixDiscountPolicy, rateDiscountPolicy

즉, DiscountPolicy 타입의 빈이 fixDiscountPolicy, rateDiscountPolicy 두 개가 존재하기 때문에 어떤 빈을 주입해야 할지 알 수 없어서 오류가 발생합니다.

 

 


🔆 조회 대상 빈이 2개 이상일 때 해결 방법

 

🌀 (1) @Autowired 필드명 매칭

@Autowired는 기본적으로 타입으로 빈을 찾지만, 필드명과 동일한 빈 이름이 있으면 해당 빈을 주입합니다.

 

✅ 해결 방법 예제

@Autowired
private DiscountPolicy rateDiscountPolicy; // 빈 이름과 필드명을 일치시킴

위처럼 필드명을 rateDiscountPolicy로 변경하면 스프링이 자동으로 rateDiscountPolicy 빈을 주입합니다.

 우선 타입으로 매칭하고, 타입이 여러 개이면 필드명과 빈 이름을 매칭하는 방식으로 해결됩니다.

 

🎈 @Autowired 매칭 정리

1. 타입 매칭
2. 타입 매칭의 결과가 2 이상일 필드 , 파라미터 명으로 이름 매칭

 


🌀 (2) @Qualifier 사용 (구체적인 빈 이름 지정)

@Qualifier를 사용하면 특정 빈을 직접 선택하여 주입할 수 있습니다.
빈을 등록할 때 @Qualifier를 붙여주고, 주입할 때도 같은 @Qualifier를 사용해야 합니다.

 

✅ 빈 등록 시

@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}

@Component
@Qualifier("subDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}

 

✅ 주입 시

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

 @Qualifier("mainDiscountPolicy")를 통해 RateDiscountPolicy가 주입됩니다.

 

🎈 @Qualifier 매칭 정리

1. @Qualifier 매칭
2.  이름 매칭
3. NoSuchBeanDefinitionException 예외 발생

 

 


🌀 (3) @Primary 사용 (우선순위 지정)

@Primary는 같은 타입의 여러 빈이 있을 때 우선적으로 주입할 빈을 지정하는 방법입니다.

 

✅ @Primary를 사용한 빈 등록

@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}

@Component
public class FixDiscountPolicy implements DiscountPolicy {}

@Primary가 적용된 RateDiscountPolicy가 우선적으로 주입됩니다.
@Qualifier와 @Primary가 함께 사용될 경우 @Qualifier가 더 높은 우선순위를 가집니다.

 자주 사용하는 빈은 @Primary를 적용하고, 특정 빈을 명시적으로 선택해야 할 경우 @Qualifier를 사용하면 좋습니다.

 

 


🔆 애노테이션을 직접 만들어서 해결하기

@Qualifier("mainDiscountPolicy")와 같이 문자열을 사용하는 방식은 컴파일 시점에 오류를 잡아내기 어렵다는 단점이 있습니다.
이를 해결하기 위해 애노테이션을 직접 만들어 사용할 수 있습니다.

 

✅ 커스텀 애노테이션 생성

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

 

✅ 적용 예제

@Component
@MainDiscountPolicy
public class RateDiscountPolicy implements DiscountPolicy {}

@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy DiscountPolicy discountPolicy) {
    this.memberRepository = memberRepository;
    this.discountPolicy = discountPolicy;
}

 @MainDiscountPolicy를 사용하면 가독성이 높아지고, 컴파일 타임 체크가 가능해집니다.

 

 


🔆 조회한 빈이 모두 필요할 때 (List, Map 사용하기)

의도적으로 특정 타입의 모든 빈을 조회해야 하는 경우가 있습니다.

대표적인 예 : 전략 패턴(Strategy Pattern)을 사용할 때

 

✅ 예제: 모든 DiscountPolicy 빈 조회

@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;
        System.out.println("policyMap = " + policyMap);
        System.out.println("policies = " + policies);
    }

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

`Map<String, DiscountPolicy>` : map 키에 스프링 빈의 이름을 넣어주고, 값으로 `DiscountPolicy` 타입으로 조회한 모든 스프링 빈을 담아준다.
`List<DiscountPolicy>` : `DiscountPolicy` 타입으로 조회한 모든 스프링 빈을 담아준다.
만약 해당하는 타입의 스프링 빈이 없으면, 컬렉션이나 Map 주입한다.

 

✅ 실행 결과

policyMap = {fixDiscountPolicy=FixDiscountPolicy@1a2b3c, rateDiscountPolicy=RateDiscountPolicy@4d5e6f}
policies = [FixDiscountPolicy@1a2b3c, RateDiscountPolicy@4d5e6f]

 policyMap을 사용하면 빈 이름으로 원하는 빈을 선택해서 사용할 수 있습니다.
 List<DiscountPolicy>를 사용하면 모든 빈을 순서대로 조회할 수 있습니다.

 

 


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

스프링에서는 대부분의 빈을 자동 등록(@ComponentScan)하는 것이 일반적입니다.
하지만, 일부 상황에서는 수동 등록(@Bean)을 고려해야 합니다.

 

🔸 자동 등록이 적합한 경우

 업무 로직 관련 빈 (예: @Service, @Repository, @Controller)
 패턴이 일정한 빈 (예: @Component로 등록하는 일반 빈)

 

🔸수동 등록이 적합한 경우

 애플리케이션 전반에 영향을 미치는 기술 지원 빈 (예: 데이터베이스 연결, 공통 로깅, 트랜잭션 관리)
 외부 라이브러리를 활용하는 경우
 의존관계가 명확해야 하는 경우 (예: 전략 패턴에서 특정 구현체를 명확하게 지정해야 할 때)

 

✅ 정리

등록 방식 사용 사례
자동 등록 (@ComponentScan) 일반적인 서비스, 레포지토리, 컨트롤러
수동 등록 (@Bean) 기술 지원 빈, 외부 라이브러리, 특정 구현체가 필요한 경우

 


📌 결론

 같은 타입의 빈이 여러 개 있을 때는 @Qualifier, @Primary, @Autowired 필드 명을 활용해 해결할 수 있습니다.
 애노테이션을 직접 만들어 사용하면 가독성과 유지보수성이 향상됩니다.
 빈이 여러 개 필요할 때는 List와 Map을 활용하면 편리합니다.
 실무에서는 자동 등록을 기본으로 하되, 기술 지원 빈은 수동 등록하는 것이 좋습니다.

📌 스프링의 의존관계 자동 주입을 올바르게 활용하면 코드의 유지보수성과 확장성을 크게 향상시킬 수 있습니다. 🚀

'개발일지 > Spring' 카테고리의 다른 글

빈 스코프  (0) 2025.02.24
빈 생명주기 콜백  (0) 2025.02.20
의존관계 자동 주입 1편  (0) 2025.02.19
컴포넌트 스캔  (0) 2025.02.18
싱글톤 패턴, 싱글톤 컨테이너  (0) 2025.02.18