본문 바로가기
JAVA/Error

[Spring] The bean 'delegatingApplicationListener' 에러 해결 방법

by 알기 쉬운 코딩 사전 2024. 5. 28.
반응형

🚨 application run 시 발생한 에러 메시지

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'delegatingApplicationListener', defined in class path resource [hello/sns/configuration/AuthenticationConfig.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class] and overriding is disabled.

Action:

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

 

❓에러 발생 이유

delegatingApplicationListener Bean 중복으로 인한 에러 메시지입니다.

 

✅ 해결 방법

혹시 본인이 작성한 코드가 아래와 같이 WebSecurityConfiguration를 상속받았는지를 먼저 확인해 볼 필요가 있습니다.
 
AuthenticationConfig.java

@EnableWebSecurity
@Configuration
public class AuthenticationConfig extends WebSecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)
                .authorizeRequests(authReq ->
                        authReq.requestMatchers("/api/v1/users/join", "api/v1/users/login").permitAll()
                                .requestMatchers("/api/**").authenticated()
                )
                .formLogin(Customizer.withDefaults())
                .sessionManagement(sessionManage ->
                        sessionManage.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        return http.build();
    }
}

 

참고: Spring 2.x 버전에서는 WebSecurityConfiguration를 상속받아 SecurityConfig를 작성하고
Override를 통하여 delegatingApplicationListener Bean를 수동으로 등록하였습니다.

하지만 Spring 3.X 이후의 버전에서는 수동으로 delegatingApplicationListener를 등록하는 방법이 사라졌습니다.

그렇기 때문에 Spring 3.x 이후의 버전에서는 SecurityFilterChain를  직접 Bean으로 등록하고 사용하여야 합니다.

해당 정보는 스프링 공식 문서에서 확인하실 수 있습니다.

 

❌ 올바르지 않은 해결 방법

application.properties에서 Bean Overriding 설정을 켜서 해결한다는 블로그 글을 보았습니다.
이는 Spring Security 설정에 대해서는 절대 올바른 해결 방법이 아닙니다.

아래 코드와 설명을 참고해 주세요.

 
application.properties

spring.main.allow-bean-definition-overriding: true //bean-overriding 설정 켜기

 
Bean Overriding 설정을 키고 application을 run 하면 아래와 같은 에러 메시지가 나오는 것을 확인할 수 있습니다.

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  authenticationConfig (field private org.springframework.security.config.annotation.web.builders.HttpSecurity org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.httpSecurity)
↑     ↓
|  org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity defined in class path resource [org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.class]
└─────┘


Action:

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.


Process finished with exit code 1
참고: 순환 참조 문제는 아래 설정으로 해결할 수 있지만, 이는 절대 올바른 해결 방법이 아닙니다!

 
application.properties

spring.main.allow-circular-references: true // spring 순환 참조 허락 설정

 

🟢 올바른 해결 방법

상속을 제거하고 AuthenticationConfig를 구현하면 됩니다.

아래 코드를 참고해 주세요.

 
AuthenticationConfig.java

@EnableWebSecurity
@Configuration
public class AuthenticationConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)
                .authorizeRequests(authReq ->
                        authReq.requestMatchers("/api/v1/users/join", "api/v1/users/login").permitAll()
                                .requestMatchers("/api/**").authenticated()
                )
                .formLogin(Customizer.withDefaults())
                .sessionManagement(sessionManage ->
                        sessionManage.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
        return http.build();
    }
}
반응형

댓글