[Spring 입문] Bean이 두 개면 누굴 선택해? (@Primary vs @Qualifier)

시에나·2026년 1월 11일

스프링 기초

목록 보기
7/13
post-thumbnail

지난 시간에 우리는 @Component를 써서 스프링이 알아서 객체(Bean)를 만들게 했습니다. 그런데 여기서 호기심이 생깁니다.

"만약 게임이 팩맨(Pacman) 하나가 아니라, 마리오(Mario)도 있다면 어떻게 될까?"

스프링에게 "게임기 가져와!"라고 시켰는데, 게임기가 두 대라면 스프링은 과연 알아서 골라올까요? 오늘은 이 상황에서 발생하는 에러와 두 가지 해결책을 알아보겠습니다.


1. 사건 발생: "도대체 누구랑 연결하라는 거야?!"

우리는 PacmanGame에 @Component를 붙여뒀습니다. 이번엔 MarioGame에도 똑같이 @Component를 붙여보겠습니다.

@Component
public class MarioGame implements GamingConsole { ... }

@Component
public class PacmanGame implements GamingConsole { ... }

그리고 실행(Run)을 하면... 펑! 💥 에러가 발생합니다.

에러 메시지: NoUniqueBeanDefinitionException

해석:
스프링이 GameRunner를 만들려고 보니 GamingConsole이 필요한데, 후보자가 마리오와 팩맨 둘 다 있어서 누구를 골라야 할지 모르겠다는 뜻입니다. 스프링은 결정장애가 오면 그냥 멈춰버립니다.


2. 해결책 1: "일단 얘가 대장이야!" - @Primary

첫 번째 해결책은 '우선순위'를 정해주는 것입니다. 여러 후보가 있을 때, "별말 없으면 그냥 얘를 써!"라고 정해두는 것이죠. 이것이 바로 @Primary 어노테이션입니다.

MarioGame에 왕관(@Primary)을 씌워줍시다.

@Component
@Primary  // <-- "여러 개면 나를 먼저 선택해!"
public class MarioGame implements GamingConsole { ... }

결과:
이제 스프링은 팩맨과 마리오를 둘 다 발견하지만, @Primary가 붙은 마리오 게임을 선택해서 실행합니다. 팩맨은 아쉽지만 선택받지 못했네요.


3. 해결책 2: "콕 집어서 얘를 데려와!" - @Qualifier

자, 이제 상황을 더 복잡하게 만들어 볼까요?

  1. PacmanGame (@Component)
  2. MarioGame (@Component, @Primary) -> 현재 1순위
  3. SuperContraGame (새로 추가, @Component)

여기서 제가 "마리오가 1순위인 건 알겠는데, 이번엔 특별히 '슈퍼 콘트라'를 하고 싶어!"라고 한다면 어떻게 해야 할까요?

이때 사용하는 것이 @Qualifier (한정자)입니다. 별명을 붙여서 콕 집어 부르는 방식이죠.

1단계: 슈퍼 콘트라에 별명 붙이기

@Component
@Qualifier("SuperContraGameQualifier") // <-- "내 별명은 이거야"
public class SuperContraGame implements GamingConsole { ... }

2단계: GameRunner에서 콕 집어 부르기

게임을 실행하는 GameRunner에게 가서 "그냥 게임기 말고, 꼭 저 별명을 가진 애를 가져와"라고 말해줍니다.

@Component
public class GameRunner {
    private GamingConsole game;

    // 생성자 주입
    public GameRunner(@Qualifier("SuperContraGameQualifier") GamingConsole game) {
        this.game = game;
    }
    // ...
}

결과:
MarioGame이 @Primary라는 강력한 우선권을 가지고 있음에도 불구하고, @Qualifier로 아주 구체적으로 지목당한 슈퍼 콘트라 게임이 실행됩니다.


스프링 컨테이너 안에 똑같은 타입의 객체(Bean)가 여러 개 있을 때, 스프링은 혼란에 빠집니다(NoUniqueBeanDefinitionException). 이를 해결하는 방법은 두 가지입니다.

  1. @Primary: "여러 후보가 있으면 우선적으로 나를 써라." (범용적인 기본값 설정)
  2. @Qualifier: "다른 거 말고 특정 이름표(한정자)가 붙은 걔를 써라." (구체적인 지목)

승자 판정: @Primary보다 @Qualifier가 더 강력합니다. (넓은 범위의 기본값보다 구체적인 지시가 이깁니다.)


팁

코드를 작성할 때, 평소에 가장 많이 쓰는 것에는 @Primary를 붙여두고, 가끔 특별한 상황에 써야 하는 것에는 @Qualifier를 사용하면 아주 깔끔하게 코드를 짤 수 있습니다!

다음 시간에는 스프링의 더 깊은 개념들(의존성 주입의 종류 등)을 알아보겠습니다.

0개의 댓글