본문 바로가기
IT 개인 공부/Spring

[Spring] 조회 빈(Bean)이 2개 이상일때 처리하는 방법

by Libi 2021. 8. 18.
반응형

스프링 컨테이너에 빈을 등록하면 스프링 컨테이너가 알아서 의존관계를 맺어준다. 그런데 의존관계를 맺어줄 때 해당하는 타입의 빈이 2개 이상이라면 어떤 문제가 발생할까?

간단한 예시로 알아보도록 하자.

현재 MyRepository 인터페이스가 하나 있으며, 이를 구현하는 2개의 Repository가 있다.

public interface MyRepository {
}

@Repository
public class MemoryRepository implements MyRepository{
}

@Repository
public class JpaRepository implements MyRepository{
}

 

스프링 컨테이너에서 관리되는 컴포넌트에서 빈으로 등록된 MyRepository를 사용한다고 하자. @Autowired 어노테이션으로 인해 스프링 컨테이너에서 알맞은 빈을 자동으로 주입해줄 것이다.

@Component
public class MyService {

    @Autowired
    private MyRepository myRepository;
}

 

위 코드는 아무런 문제가 없을까?

MyRepository는 인터페이스이기 때문에 사용하기 위해선 실제로 구현한 MemoryRepository나 JpaRepository 중 하나를 주입해야 할 것이다.

스프링 컨테이너 입장에서는 둘 중 어떤 빈을 주입해야 할지 판단할 수 없기 때문에 에러가 발생한다. 실제로 동작해보면 다음과 같이 에러가 발생한다.

 

에러를 살펴보면 하나의 빈이 필요한데 2개의 빈이 발견되었다는 것을 알 수 있다.

물론 인터페이스를 주입받지 말고 구현된 하위 클래스를 지정하면 해결할 수 있지만 이는 다형성을 깨뜨리기 때문에 Repository를 교체할 경우 DIP를 위배하고 유연성이 떨어지는 단점이 있다.

@Autowired
private MemoryRepository memoryRepository;
    
@Autowired
private JpaRepository jpaRepository;

 

그렇다면 어떻게 해결해야 할까? 에러 사진을 자세히 봤다면 어느 정도 눈치를 챘을 것이다.

조회 빈이 2개 이상일 경우에는 3가지 방법으로 해결할 수 있다. 하나씩 살펴보도록 하자.

  • @Autowired 필드 명 매칭
  • @Qualifier 사용
  • @Primary 사용

 

먼저 @Autowired 필드 명 매칭이다. @Autowired는 기본적으로 타입(Type)으로 조회를 시도하고, 동일한 타입의 빈이 2개 이상 존재하면 필드 이름, 파라미터 이름으로 빈을 조회한다.

따라서 다음과 같이 원하는 빈 이름을 필드 이름으로 지정해주면 된다.

@Autowired
private MyRepository memoryRepository;
//private MyRepository jpaRepository;

 

두 번째로 @Qualifier를 사용하는 방법이다. @Qualifier은 구분할 수 있는 정보를 추가해주는 방법이다. 의존관계 주입 시 추가적인 방법을 제공하는 것이지 빈 자체의 이름을 변경하는 것은 아니다.

빈 등록 시 @Qualifier를 붙여주고, 실제로 사용할 때 해당하는 빈을 주입받도록 해준다.

@Repository
@Qualifier("memoryRepository")
public class MemoryRepository implements MyRepository{
}

@Repository
@Qualifier("jpaRepository")
public class JpaRepository implements MyRepository{
}

------------------------------------------------------
@Autowired
@Qualifier("jpaRepository")
private MyRepository myRepository;

 

만약 @Qualifier의 "jpaRepository"를 못 찾는다면 실제 이름이 "jpaRepository"인 빈을 스프링 컨테이너가 추가적으로 찾는다.

 

마지막으로 @Primary를 사용하는 방법이다. 단어 의미 그대로 우선순위를 정하는 방법이다. 원하는 빈에 @Primary를 붙여주면 된다.

@Primary
@Repository
public class JpaRepository implements MyRepository{
}

 

그렇다면 3가지 방법 중 어떤 방법을 사용하는 게 좋을까?

우선 @Autowired 필드 명 매칭 방식은 어노테이션을 적용한 게 아니라 단순히 필드 명을 바꾼 것이기 때문에 오타를 낼 수 도 있고, 다른 사람이 봤을 때 알아채기가 힘들다.

 

또한, @Qualifier은 모든 코드에 작성해야 하는 반면, @Primary 방법은 하나만 작성하면 되기 때문에 굉장히 간단해 보인다.

따라서 기본적으로 애플리케이션 생명주기 동안 변하지 않는다면 @Primary를 사용하면 된다.

 

만약 애플리케이션 생명주기동안 default값으로 MemoryRepository를 사용하다가 특별한 경우에 JpaRepository를 사용해야 하는 경우가 발생한다면 MemoryRepository에 @Primary를 적용해주고 JpaRepository에 @Qualifier를 적용해주면 된다.

왜냐하면 둘 중 @Qualifier가 우선순위가 더 높기 때문이다.

스프링에서는 자동보단 수동, 넓은 범위의 선택권보다는 좁은 범위의 선택권,  좀 더 상세하게 동작하는 친구들이 우선순위가 높다.

 

 

 

[ Reference ]

· 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/dashboard

 

반응형

댓글