DEV/Spring

[트러블슈팅] 의존성 주입 간 순환참조 문제 해결 방법

Bi3a 2024. 2. 20. 13:25

목차
728x90

안되는 이유는 모르겠고 되는 이유는 더더욱 모르겠을 때


 

문제 개요

Spring Boot Application Initialize 후 의존성 주입 과정에서 순환참조 문제가 발생했다.

이런..

 

 

원인 분석

Bean 생성 과정에서 상호 의존성을 주입하는 과정에서 순환참조 문제가 발생했음.

 

문제의 코드

java
닫기
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final Rq rq;
// JwtAuthFilter → memberService 의존
private final MemberService memberService;
// ...
}
java
닫기
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService {
private final MemberRepository memberRepository;
// memberService → SecurityConfig.PasswordEncoder 의존
private final PasswordEncoder passwordEncoder;
private final AuthTokenService authTokenService;
// ...
}
java
닫기
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig {
// SecurityConfig → JwtAuthFilter 의존
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

 

IOC 컨테이너는 빈 생성 과정에서 모든 빈 오브젝트를 싱글톤으로 만들게 되는데,

이 과정에서 싱글톤으로 만들어진 빈 오브젝트가 우로보로스마냥 서로를 호출해서 문제가 발생함.

 

이렇게 서로를 꼬리물듯이 의존성을 주입하게 되면  스프링 IOC 컨테이너는 어떤 스프링 빈을 기준으로 먼저 생성하고 의존성을 주입할지 결정할 수 없는 문제가 발생한다.

 

 

해결 방법

문제의 빈 객체를 다른 클래스로 이동 (해결)

java
닫기
@Configuration
@RequiredArgsConstructor
public class AppConfig {
@Bean
// 기존 SecurityConfig → AppConfig으로 이동
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

 

순환참조 구조를 해소할 수 있게끔 순환참조 문제의 빈 컨텍스트를 다른 클래스로 이동시켰다.

따라서 개선된 구조는 아래와 같이 순환참조 문제를 해결하였다.

  • 기존 : JwtAuthFilter → MemberService → SecurityConfig → JwtAuthFilter → ...
  • 개선 : AppConfig → MemberService → SecurityConfig → JwtAuthFilter

 

 

@Lazy를 사용하는 방법 (권장되지 않음 / 대안)

Lazy를 사용하면 프록시 객체를 통해 Bean을 생성하며, 필요 시점에 호출하여 빈을 생성한다.
이는 단순히 잘못된 구조에서 Bean 생성시점을 늦출 뿐이기에 근본적인 해결책이 되지 못한다.

기존에 의존성 구조를 구성할 때 순환참조가 발생하지 않게 짜는 것이 좋다.

 

 


틀린 부분이 있으면 댓글로 알려주세요!