0.❓문제상황❓

 

스프링 컨테이너에 빈 등록을 아래와 같이 한 경우를 생각해보겠습니다.

 

위 코드를 설명을 간단히 설명하면 아래와 같이 두 구성으로 이루어져 있는 상황입니다.

  1. 할인 정책
  2. 할인 정책에 의존성이 있는 오더 서비스

또한 할인 정책은 두 가지로 Fix, Rate 방식이 있는 상태이죠. 세 클래스 모두 @Component 어노테이션을 붙였기 때문에 스프링 컨테이너에 빈으로 등록됩니다.

 

이때 문제가 생깁니다. OrderServiceImpl 클래스의 생성자를 보시면 두 번째 인자로 DiscountPolicy를 받는 것을 알 수 있고 Autowired로 의존성 주입이 될 것을 예상할 수 있습니다. 

 

그런데.. DiscountPolicy로 조회한 빈이 위 같은 상황에서는 FixRate 방식 두 가지로 조회가 될 것입니다. 이 코드를 실행시키면 스프링은 어떻게 행동할까요? 바로 아래와 같은 에러를 뱉습니다.

의존성 주입을 위해서는 하나의 빈을 매칭 시켜야 하는데 후보가 두 개가 있으니 못하겠다,, 라는 말입니다. 그렇다고 DiscountPolicyFix 방식으로 하겠다!라고 하여 생성자 인자의 DiscountPolicyFixDiscountPolicy로 바꾼다면 DIP를 위배하게 되고 코드의 유연성이 저하되게 됩니다.

 

스프링은 위와 같은 문제점을 해결할 수 있도록 몇 가지 방법을 제공해주고 있습니다. 이제 알아보도록 하죠!


 

1.@Autowired의 매칭 보험 수단

 

OrderServiceImple의 생성자를 아래와 같이 슥 바꿔봅시다.

생성자에 들어갈 파라미터 이름을 슬쩍 바꿔본 것인데요. 이럴 경우 RateDiscountPolicy의 빈 이름 rateDiscountPolicy와 동일하게 되어 스프링에서 알아서 이와 매칭을 해줍니다. 

 

즉, 의존성을 주입할 빈 후보가 여러 개일 경우 파라미터 이름과 빈 이름이 매칭 되는 것이 있는지 체크하고, 있다면 이를 주입시키는 기능을 제공합니다.

 

@Autowired의 매칭에 대해 간단히 정리하자면 아래와 같습니다.

  1. 우선 타입을 매칭한다.
  2. 타입 매칭을 하려는데 결과가 2개 이상인 경우? ➜ 파라미터 혹은 필드명을 가지고 매칭한다.

 

2.@Qualifier

 

@Qualifier 어노테이션은 추가로 구분자를 붙여주는 방식입니다. 사용 예시를 살펴보죠.

 

 

위와 같이 Qualifier 어노테이션에 구분자용 문자열을 넣어주면 Qualifier끼리 매칭되어 해당 빈을 주입할 수 있게 됩니다.

 

이때 Qualifier를 좀 더 알아보기 쉽고, 오타로 인한 실수가 런타임 대신 컴파일 에러로 나타날 수 있게끔 커스텀 어노테이션을 만들어도 좋은 방식이라고 합니다. 예시로 확인해보겠습니다.

 

위 같은 방식으로 커스텀 어노테이션을 만든 뒤 아래와 같이 활용해줍니다.

 

 

위와 같이 커스텀 어노테이션을 활용해주면 의미상으로도 더욱 명확해지고, Qualifer 안에 구분자용 문자열의 오타 가능성으로 인한 런타임 에러도 없앨 수 있습니다. 

 


 

3.@Primary

 

@Primary 어노테이션을 사용하면 Autowired 하는 도중 여러 개의 후보 빈이 매칭되면 @Primary 어노테이션이 붙어있는 빈이 우선권을 가질 수 있게 해줍니다. 바로 예시를 살펴보시죠!

 

위와 같이 Rate방식에 @Primary 어노테이션을 써주게 되면 매칭 할 후보 빈이 Rate, Fix 두 가지임에도 불구하고 @Primary 어노테이션이 붙어있는 Rate방식이 주입됩니다. 


 

4.💨나가며

이번 글에서는 Autowired를 해야 하는데 조회 빈이 여러 개여서 문제가 생긴 상황과 이를 해결하는 방법에 대해 알아보았습니다.

 

정리하자면 해당 문제 해결을 위해 다음과 같은 방식을 사용해볼 수 있을 것 같습니다.

  1. @Autowired의 필드, 파라미터명 매칭 기능을 사용한다.
  2. @Qualifier 어노테이션을 사용한다.
  3. @Primary 어노테이션을 사용한다. 

 

 

 

반응형
복사했습니다!