같은 타입의 Bean이 여러 개라면?

StrayCat·2026년 2월 16일

같은 타입의 Bean이 여러 개일 때 해결 방법


문제 상황

Spring에서 같은 타입의 Bean이 여러 개 등록되어 있을 때, @Autowired로 주입하려고 하면 어떤 Bean을 주입해야 할지 알 수 없어 오류가 발생합니다.


예제 코드

// Food 인터페이스
public interface Food {
    void eat();
}

// Chicken 클래스
@Component
public class Chicken implements Food {
    @Override
    public void eat() {
        System.out.println("치킨을 먹습니다.");
    }
}

// Pizza 클래스
@Component
public class Pizza implements Food {
    @Override
    public void eat() {
        System.out.println("피자를 먹습니다.");
    }
}
@SpringBootTest
public class BeanTest {
    @Autowired
    Food food; // 오류 발생! Chicken과 Pizza 중 어떤 것을 주입해야 할지 모름
}

오류 메시지: "Food 타입의 Bean 객체가 하나 이상 있습니다."


해결 방법

1. Bean 이름으로 명시하기

필드명을 Bean의 이름과 동일하게 지정하면 해당 Bean이 주입됩니다.

@SpringBootTest
public class BeanTest {
    @Autowired
    Food pizza;    // Pizza Bean 주입
    
    @Autowired
    Food chicken;  // Chicken Bean 주입
}

원리: @Autowired는 먼저 타입(Type)으로 Bean을 찾고, 타입이 여러 개면 이름(Name)으로 찾습니다.


2. @Primary 사용하기

여러 Bean 중 우선적으로 주입될 Bean을 지정할 수 있습니다.

@Component
@Primary  // 같은 타입의 Bean이 여러 개일 때 우선 주입
public class Chicken implements Food {
    @Override
    public void eat() {
        System.out.println("치킨을 먹습니다.");
    }
}
@SpringBootTest
public class BeanTest {
    @Autowired
    Food food;  // Chicken Bean이 주입됨
}

특징: 별도의 설정 없이 기본적으로 주입될 Bean을 지정할 때 유용합니다.


3. @Qualifier 사용하기

특정 Bean을 명시적으로 지정하여 주입받을 수 있습니다.

@Component
@Qualifier("pizza")  // Bean의 별칭 지정
public class Pizza implements Food {
    @Override
    public void eat() {
        System.out.println("피자를 먹습니다.");
    }
}
@SpringBootTest
public class BeanTest {
    @Autowired
    @Qualifier("pizza")  // pizza라는 이름의 Bean을 주입
    Food food;
}

특징: 주입받는 쪽에서도 @Qualifier를 명시해야 하므로, 특정 상황에서만 사용하는 Bean에 적합합니다.


@Primary vs @Qualifier 우선순위

@Component
@Primary
public class Chicken implements Food {
    @Override
    public void eat() {
        System.out.println("치킨을 먹습니다.");
    }
}

@Component
@Qualifier("pizza")
public class Pizza implements Food {
    @Override
    public void eat() {
        System.out.println("피자를 먹습니다.");
    }
}
@SpringBootTest
public class BeanTest {
    @Autowired
    @Qualifier("pizza")
    Food food;
    
    @Test
    @DisplayName("Primary와 Qualifier 우선순위 확인")
    void test1() {
        food.eat();  // "피자를 먹습니다." 출력
    }
}

결론: @Qualifier@Primary보다 우선순위가 높습니다.


개발시 사용 권장 사항

  • @Primary: 범용적으로 사용되는 Bean에 적용
    • 예) 기본 데이터베이스 연결, 기본 메시지 발송 서비스
  • @Qualifier: 특수한 상황에서만 사용되는 Bean에 적용
    • 예) 테스트용 Mock 객체, 특정 조건에서만 사용하는 서비스

핵심 정리

  1. @Autowired타입 → 이름 순서로 Bean을 찾습니다.
  2. 같은 타입의 Bean이 여러 개일 때 해결 방법:
    • 필드명으로 Bean 이름 명시
    • @Primary로 기본 Bean 지정
    • @Qualifier로 특정 Bean 명시
  3. @Qualifier > @Primary 우선순위
  4. 개발에서는 @Primary(범용) + @Qualifier(특수목적) 조합 추천

추가 팁

생성자 주입에서의 사용

필드 주입보다 생성자 주입이 권장되는데, 생성자 주입에서도 동일하게 적용됩니다.

@Service
public class FoodService {
    private final Food food;
    
    // 생성자 주입에서 @Qualifier 사용
    public FoodService(@Qualifier("pizza") Food food) {
        this.food = food;
    }
}

List로 모든 Bean 받기

같은 타입의 Bean을 모두 받아서 사용하고 싶다면 List로 주입받을 수 있습니다.

@Service
public class FoodService {
    private final List foods;
    
    public FoodService(List foods) {
        this.foods = foods;  // Chicken, Pizza 모두 주입됨
    }
    
    public void eatAll() {
        foods.forEach(Food::eat);
    }
}

이 방법은 전략 패턴이나 여러 구현체를 동시에 사용해야 할 때 유용합니다.

profile
알면 좋은 것보단 잊어버리기 싫은 것들을 기록합니다.

0개의 댓글