관리 메뉴

공부 기록장 💻

[Spring] 컴포넌트 스캔의 중복 등록과 충돌 (수동 빈 등록의 우선권과 오버라이딩 에러) 본문

# Tech Studies/Java Spring • Boot

[Spring] 컴포넌트 스캔의 중복 등록과 충돌 (수동 빈 등록의 우선권과 오버라이딩 에러)

dream_for 2023. 2. 2. 17:57

인프런 - 스프링 핵심 원리 기본편 정리



스프링 컨테이너에 스프링 빈을 자동으로 등록하고 의존 관계를 주입해주는 컴포넌트 스캔에 대해 학습을 하였고,
이번에는 컴포넌트 스캔에서 같은 빈 이름이 등록되는 경우 발생하는 충돌에 대해 알아보자.

AOP에 대해 학습하며 발생한 오류가 스프링 빈 중복 등록인 줄 알았던 며칠 전..


스프링 입문 강의를 수강하며 AOP(Aspect Oriented Programming, 관점 지향 프로그래밍)에 대해 학습하던 중, 스프링 컨테이너와 컴포넌트 스캔에 대한 충분한 이해가 바탕이 되지 않은 상태로 Configuration 설정 정보를 구성하여 오류가 발생한 경험이 있는데, 해당 오류 발생 원인이 스프링 빈 중복 등록으로 인한 충돌이라고 생각했던 경험이 있었다. (아래 글)

https://dream-and-develop.tistory.com/393

 

[Java/Spring] The dependencies of some of the beans in the application context form a cycle /  The bean 'timeTraceAop', defined

dream-and-develop.tistory.com



이 때는 입문 강의에서 스프링 컨테이너에 스프링 빈을 등록하는 방법 2가지

  1. @Bean을 통해 직접 자바 코드로 빈을 등록하고 의존 관계를 직접 주입하는 첫번째 방식
  2. 컴포넌트 스캔을 이용해 @Component@Autowired가 작성되어 있는 객체들에 대하여 빈으로 등록하고 의존관계를 자동으로 주입하는 두번째 방식

에 대해 간략히 배운 배경이 있었기에, 처음에는 @Bean과 @Component를 동시에 작성함에 따라 발생한 스프링 빈 중복 등록으로 인한 충돌 문제인 줄 알았다.
(사실 위의 오류는 실제적으로 스프링 빈 중복으로 인한 충돌이 아니라, AOP 적용 시 스프링 빈으로 등록한 객체가 @Aspect 이 적용되어 자기 자신을 순환적으로 호출하여 발생하는 순환 참조의 문제였다..!)


이후 스프링 핵심 원리 강의를 통해 스프링 컨테이너에 대한 심화 과정에서

1) 싱글톤 레지스트리인 스프링 컨테이너는 스프링 빈을 싱글톤으로 관리한다는 사실,
2) 자동으로 스프링 빈을 등록하고 의존 관계를 주입하는 스프링의 Component Scan (컴포넌트 스캔) 의 기능,
3) 컴포넌트 스캔 시, excludeFilters 옵션를 통해 스프링 빈 등록 대상에서 제외할 수 있음

을 학습하고 난 뒤, 오류의 원인을 착각했던 컴포넌트 스캔에 대해 정확히 이해할 수 있었고 학습한 내용들을 다시 복기할 수 있게 되었다.

 

그래서 문제의 진짜 원인은? -> 스프링 빈 중복 등록과 충돌 발생이 아닌, AOP를 적용한 객체의  자기 자신에 대한 순환 참조였다..


처음에는 bean-definition-overriding 설정 값을 true로 바꾸라는 오류 메세지가 떴기 때문에, Configuration 구성 정보 파일인 SpringConfig에서 @Bean으로 timeTraceAop 객체를 스프링 빈으로 수동 등록한 것과 TimeTraceAop 클래스 위에 @Component 애노테이션을 명시한 것이 스프링 컨테이너에 빈이 중복 등록되어 충돌이 일어났던 것이라고 생각했지만, 사실 진짜 원인은 아니였다. (진짜 원인은 AOP 적용 시 @Aspect 에 지정한 경로에 자기 자신이 포함되어 있어 발생한 순환 참조 문제였다.)


이렇게 헷갈리게 만든 스프링 빈 중복 등록으로 인한 충돌이 무엇인지, 언제 발생하는지, 어떻게 이를 방지할 수 있는지 에 대해 파헤쳐 보자!


일단, 컴포넌트 스캔에서 같은 빈 이름이 등록되어 충돌이 발생하는 상황은 다음의 두 가지가 있다.

  1. 자동 빈 등록 vs 자동 빈 등록
  2. 수동 빈 등록 vs 자동 빈 등록

 

자동 빈 등록 vs 자동 빈 등록 충돌

컴포넌트 스캔에 의해 자동으로 스프링 빈이 등록되는데, 두 개 이상의 스프링 빈의 이름이 같은 경우 스프링은 ConflictingBeanDefinitionException 예외를 발생시킨다.

 

수동 빈 등록 vs 자동 빈 등록 충돌

@Bean을 통해 직접 자바 코드로 객체를 생성하여 스프링 빈을 수동적으로 등록하고, 동시에 컴포넌트 스캔을 통한 자동 빈 등록이 된 객체가 존재하는 경우에는 수동 빈 등록이 우선권을 가진다. 그리고, 수동 빈이 자동 빈을 오버라이딩한다.
위와 같이 수동 빈 등록 시 남는 로그는 다음과 같다. (memoryMemberRepository라는 bean이 다른 빈에 의해 오버라이딩, 교체 되었음을 설명하고 있다.)

Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing


그러나, 현실에서는 개발자가 의도적으로 수동 빈 등록이 우선권을 갖게 설정하여 위와 같은 결과가 만들어지기보다는 여러 설정 정보들이 꼬여 위의 결과가 만들어지며, 정말 잡기 어려운 버그가 만들어진다고 한다.

따라서 최근 spring boot에서는 수동 빈 등록과 자동 빈 등록으로 인한 충돌이 나면, 오류가 발생하도록 기본 값을 바꾸었다.(spring.main.lallow-bean-definition-overriding 값이 false로 변경) 다음의 오류 메시지가 남겨지며, 예전에 AOP 를 학습하며 중복 충돌이 일어나 발생했던 에러와 동일하다. (따라서 중복 빈 등록이 해당 오류의 실제 원인인 줄 착각했던 것이다..)

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true


이는 application.properties에서 spring.main.allow-bean-definition-overiding=true 로 재지정하여, 여러 빈들의 오버라이딩을 허용하라는 메시지이다.


결론적으로, 싱글톤으로 스프링 빈을 관리하는 스프링 컨테이너에 빈을 등록하는 경우 중복으로 인한 충돌이 일어나지 않도록 항상 주의하자. 코드를 더 많이 작성하게 되더라도, 어설픈 추상화보다는 명확한 설계를 목표로 하여, 처음부터 충돌을 막도록 하자.

728x90
반응형
Comments