RabbitMQ를 안정적으로 운영하기 위해 기본 메시징과 별도의 목적(예: update 전용)으로 host를 분리하려고 ConnectionFactory를 2개로 늘렸다.
그런데… 예상치 못한 Bean 등록 충돌이 터졌다.
ConnectionFactory를 두 개로 분리한 직후, 애플리케이션 시작 단계에서 아래 오류가 발생했다.

Parameter 1 of method simpleRabbitListenerContainerFactory in org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration required a single bean, but 2 were found:
- defaultConnectionFactory: defined by method 'defaultConnectionFactory' in class path resource [com/example/hairbackend/config/RabbitmqConfig.class]
- updateConnectionFactory: defined by method 'updateConnectionFactory' in class path resource [com/example/hairbackend/config/RabbitmqConfig.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
핵심은 이 문장이다.
simpleRabbitListenerContainerFactory가 ConnectionFactory를 주입받아야 하는데그리고 Spring이 제안하는 해결책은 보통 아래 3개다.
@Primary로 우선순위 지정@Qualifier로 특정 bean 지정여기서 혼란이 시작됐다.
내 코드는 이미 ListenerContainerFactory를 두 개 만들어서, 각각 어떤 ConnectionFactory를 쓸지 @Qualifier로 명확히 지정해둔 상태였다.
@Bean
public SimpleRabbitListenerContainerFactory defaultRabbitListenerContainerFactory(
@Qualifier("defaultConnectionFactory") ConnectionFactory connectionFactory,
MessageConverter messageConverter) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(messageConverter);
return factory;
}
@Bean
public SimpleRabbitListenerContainerFactory updateRabbitListenerContainerFactory(
@Qualifier("updateConnectionFactory") ConnectionFactory connectionFactory,
MessageConverter messageConverter) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(messageConverter);
return factory;
}
그래서 내 기대는 이거였다.
그런데 오류는 계속 났고, 더 이상했던 건 @Primary를 붙이면 해결되었다는 점이다.
왜 @Qualifier로 분리해둔 구조에서 @Primary가 필요하지?
여기서부터 RabbitMQ의 내부 동작을 뜯어봐야겠다고 결론냈다.
문제를 해결하기 위해 작성한 코드만 볼 게 아니라, Spring AMQP가 @RabbitListener를 어떻게 초기화하는지(그리고 Spring Boot가 어떤 Bean을 자동으로 만드는지)까지 확인해봐야겠다는 결론에 도달했다.

RabbitMQ 동작 과정을 따라가던 중, @RabbitListener가 기본적으로 rabbitListenerContainerFactory라는 이름의 RabbitListenerContainerFactory Bean을 참조한다는 점을 발견했다.
그런데 필자가 등록한 Bean 이름은 defaultRabbitListenerContainerFactory, updateRabbitListenerContainerFactory였기 때문에, 기본 이름(rabbitListenerContainerFactory)으로는 Bean을 따로 등록하지 않은 상태였다.
여기서 한 가지 의문이 들었다.
기본 이름의 Bean을 내가 만들지 않았다면, "rabbitListenerContainerFactory를 찾을 수 없다"는 에러가 먼저 떠야 하지 않을까?
그런데 실제로는 Bean Not Found가 아니라, ConnectionFactory가 2개라서 하나를 고르지 못한다는 오류가 발생했다.
즉, rabbitListenerContainerFactory가 애초에 "없는 상태"가 아니라, 어딘가에서 자동으로 생성되고 있는 상태였다는 뜻이다.
결론부터 말하면, 이 Bean은 RabbitMQ가 만들어주는 게 아니라 Spring Boot의 Auto-Configuration(Spring Boot 자동 구성) 이 기본 이름의 rabbitListenerContainerFactory를 자동으로 등록한다.
따라서 ConnectionFactory를 2개 등록한 상황에서는, 자동 구성 쪽에서 ConnectionFactory를 단일로 주입받으려다 충돌이 발생할 수 있다(그래서 에러 메시지에서 @Primary를 제안하는 것도 자연스럽다).
이 의심을 확인하기 위해 Bean 리스트를 확인해 보니, 실제로 rabbitListenerContainerFactory라는 이름의 Bean이 존재하고 있었다.
즉, 필자가 의도하지 않았던 기본 ListenerContainerFactory가 자동으로 생성되면서, 그 내부에서 ConnectionFactory 후보가 2개로 잡혀 충돌이 난 것이다.
확인해보니 정말로 컨테이너에 rabbitListenerContainerFactory라는 빈이 존재했다.
즉, 내 코드에서 등록한 두 개의 컨테이너 팩토리와 별개로,
Spring Boot Auto-Configuration이 기본 리스너 팩토리를 자동 생성하고 있었던 것이다.
그리고 이 자동 생성된 rabbitListenerContainerFactory는 당연히 이런 상태다.
그래서 에러 메시지가 정확히 이 상황을 말하고 있었던 거다.
“simpleRabbitListenerContainerFactory가 ConnectionFactory 하나를 주입받아야 하는데 2개라서 못 고르겠어요.”
여기까지 오면 @Primary로 해결되는 이유도 이해가 된다.
하지만 내가 원한 해결은 우선순위로 찍어누르기가 아니라,
내가 만든 기본 factory를 기본값으로 채택하는 것이었다.
@RabbitListener가 기본으로 찾는 이름은 rabbitListenerContainerFactory그러면 해결은 간단하다.
내가 기본으로 쓰고 싶은 컨테이너 팩토리를 rabbitListenerContainerFactory 라는 이름으로 등록하면 된다.
아래처럼 Bean 이름만 지정해주면 된다.
@Bean(name = "rabbitListenerContainerFactory") // 이 부분이 핵심!
public SimpleRabbitListenerContainerFactory defaultRabbitListenerContainerFactory(
@Qualifier("defaultConnectionFactory") ConnectionFactory connectionFactory,
MessageConverter messageConverter) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(messageConverter);
return factory;
}
@Bean
public SimpleRabbitListenerContainerFactory updateRabbitListenerContainerFactory(
@Qualifier("updateConnectionFactory") ConnectionFactory connectionFactory,
MessageConverter messageConverter) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(messageConverter);
return factory;
}
이렇게 하면 무슨 일이 일어나냐면,
@RabbitListener가 기본으로 참조하는 factory가defaultConnectionFactory 기반 factory로 고정된다.처음엔 "왜 @Qualifier가 있는데도 충돌이 나지?"라는 생각만 했는데,
결국 문제는 @Qualifier 자체가 아니라 Auto-Configuration이 만들어낸 기본 bean이었다.
이번 경험에서 배운 건 이거다.
다중 ConnectionFactory를 구성할 때,
@Qualifier로 각 컨테이너 팩토리의 ConnectionFactory를 지정하는 것만으로는 부족할 수 있고@RabbitListener가 기본으로 찾는 rabbitListenerContainerFactory를