Spring/Spring

Spring에서 동일한 타입의 Bean 충돌 문제 해결하기

ghyeong 2024. 11. 19. 15:45

Spring 애플리케이션을 개발하다 보면, 의존성 주입 중 동일한 타입의 Bean 충돌 문제를 경험할 수 있습니다. 이번 포스팅에서는 이 문제의 원인과 이를 해결하기 위한 다양한 방법을 소개합니다.


에러 상황: 동일한 타입의 Bean 주입 충돌

Spring 컨테이너가 특정 Bean을 주입하려 할 때, 동일한 타입의 Bean이 여러 개 정의되어 있다면 다음과 같은 에러가 발생할 수 있습니다.

에러 메시지 예시

Parameter 3 of constructor in com.example.controller.MyController required a single bean, but 2 were found:
    - userServiceImpl1: defined in [com.example.service.UserServiceImpl1]
    - userServiceImpl2: defined in [com.example.service.UserServiceImpl2]

이 메시지는 Spring이 특정 타입의 Bean을 주입하려 했지만, 두 개 이상의 후보가 발견되어 어떤 것을 선택해야 할지 결정하지 못했다는 것을 의미합니다.


원인: Spring의 Bean 주입 방식

Spring은 의존성을 주입할 때 다음과 같은 단계를 따릅니다:

  1. 주입해야 하는 Bean의 타입을 기준으로 검색.
  2. 해당 타입의 Bean이 하나라면 성공적으로 주입.
  3. 여러 Bean이 발견되면 충돌 발생(NoUniqueBeanDefinitionException).

위 상황에서 UserService 타입의 Bean이 두 개 (userServiceImpl1, userServiceImpl2) 등록되어 있어 문제가 발생한 것입니다.


해결 방법

이 문제를 해결하려면 Spring이 주입할 Bean을 명확히 선택하도록 해야 합니다. 다음은 이 문제를 해결하는 몇 가지 방법입니다.


@Qualifier로 주입할 Bean 명시하기

Spring에서 특정 Bean 이름을 지정하여 주입할 수 있습니다.

@RestController
public class MyController {

    private final UserService userService;

    public MyController(@Qualifier("userServiceImpl1") UserService userService) {
        this.userService = userService;
    }
}

@Qualifier의 값은 Bean의 이름(userServiceImpl1, userServiceImpl2)과 일치해야 합니다. 이렇게 하면 Spring이 어떤 Bean을 사용할지 명확히 알 수 있습니다.

반응형

@Primary로 기본 Bean 지정

만약 특정 Bean을 기본으로 사용하고 싶다면, 해당 Bean에 @Primary를 추가할 수 있습니다.

@Service
@Primary
public class UserServiceImpl1 implements UserService {
    // 구현 내용
}

이렇게 하면 별도의 @Qualifier를 지정하지 않아도 UserService 타입의 Bean을 주입할 때 userServiceImpl1이 기본적으로 선택됩니다.


구성 클래스를 사용해 Bean 등록하기

@Configuration 클래스에서 조건에 따라 Bean을 동적으로 등록하는 방법입니다.

@Configuration
public class UserServiceConfig {

    @Bean
    public UserService userService() {
        if (isSpecialCondition()) {
            return new UserServiceImpl1();
        } else {
            return new UserServiceImpl2();
        }
    }

    private boolean isSpecialCondition() {
        // 조건 로직
        return true;
    }
}

이 방식은 특정 조건에 따라 동적으로 Bean을 선택해야 할 때 유용합니다.


인터페이스 세분화

서비스 구현체가 각각 고유한 역할을 담당한다면, 인터페이스를 세분화하여 충돌을 방지할 수 있습니다.

public interface UserService {
    void execute();
}

public interface AdminService extends UserService {
    void manage();
}

@Service
public class AdminServiceImpl implements AdminService {
    public void execute() { /* 구현 */ }
    public void manage() { /* 구현 */ }
}

이렇게 하면 각 인터페이스의 역할에 맞게 주입할 Bean을 더 명확히 관리할 수 있습니다.


결론

Spring에서 동일한 타입의 Bean이 여러 개 존재할 때, 적절한 방법으로 주입할 Bean을 명확히 지정하면 문제가 해결됩니다.

요약: 문제 해결을 위한 선택지

  1. @Qualifier로 특정 Bean을 명시적으로 지정.
  2. @Primary로 기본 Bean 설정.
  3. 구성 클래스를 활용하여 동적으로 Bean 등록.
  4. 인터페이스를 세분화하여 역할별로 Bean 관리.