Post

Spring Boot ConflictingBeanDefinitionException 해결하기

Spring Boot ConflictingBeanDefinitionException 해결하기

Spring Boot 프로젝트 개발 중 ConflictingBeanDefinitionException이 발생했다. 콘서트 예약 시스템을 구현하면서 마주친 이 문제와 해결 과정을 기록한다.

문제 상황

테스트 실행 시 다음과 같은 오류가 발생했다:

1
2
3
org.springframework.context.annotation.ConflictingBeanDefinitionException: 
Annotation-specified bean name 'paymentGateway' for bean class [...PaymentGatewayImpl] 
conflicts with existing, non-compatible bean definition of same name

원인 분석

문제의 원인은 두 가지였다:

  1. 구현체 누락: PaymentGateway 인터페이스는 정의했지만 실제 구현체가 없었다
  2. ComponentScan 필터: 과도한 필터링으로 인해 필요한 Bean도 제외되고 있었다
1
2
3
4
5
6
7
// 문제가 된 ComponentScan 설정
@ComponentScan(
    basePackages = "kr.hhplus.be.server",
    excludeFilters = {
        @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Test.*") // 너무 광범위
    }
)

해결 방법

1단계: PaymentGateway 구현체 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class PaymentGatewayImpl implements PaymentGateway {
    
    @Override
    public Payment processPayment(Payment payment) {
        try {
            // 결제 처리 시뮬레이션
            Thread.sleep(100);
            return payment.complete("txn-" + System.currentTimeMillis());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("결제 처리 중 오류가 발생했습니다.", e);
        }
    }
}

2단계: 테스트용 구현체 분리

1
2
3
4
5
6
7
8
9
10
@Component
@Profile("test")
public class MockPaymentGatewayImpl implements PaymentGateway {
    
    @Override
    public Payment processPayment(Payment payment) {
        // 테스트용 즉시 완료 처리
        return payment.complete("test-txn-" + System.currentTimeMillis());
    }
}

3단계: ComponentScan 필터 개선

1
2
3
4
5
6
7
8
9
10
11
12
@SpringBootApplication
@ComponentScan(
    basePackages = "kr.hhplus.be.server",
    excludeFilters = {
        @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Test[s]?$") // 더 정확한 패턴
    }
)
public class ServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServerApplication.class, args);
    }
}

대안적 해결책

ComponentScan을 완전히 제거하고 Spring Boot의 기본 패키지 스캔을 사용하는 것도 가능하다:

1
2
3
4
5
6
@SpringBootApplication  // ComponentScan 제거
public class ServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServerApplication.class, args);
    }
}

교훈

인터페이스를 정의할 때는 반드시 구현체도 함께 제공해야 한다. ComponentScan 필터를 사용할 때는 예상치 못한 Bean 제외가 발생할 수 있으므로 신중하게 패턴을 작성해야 한다.

This post is licensed under CC BY 4.0 by the author.