카테고리 없음

트러블 슈팅(2025-05-13)

dydwo6018 2025. 5. 13. 18:53

JUnit 테스트 중 순환 참조(Circular Dependency) 문제 해결

  배경 

JWT 인증 기반의 인증/인가 시스템을 구현한 후, AuthService 클래스의 회원가입 기능에 대한 단위 테스트 AuthServiceTest를 작성하여 검증을 진행하려 했습니다.
Spring Boot의 @SpringBootTest 환경에서 테스트 실행 시, 애플리케이션 컨텍스트가 정상적으로 로딩되지 않는
문제가 발생했습니다.



문제

테스트 실행 결과가 아래와 같은 에러가 발생하였습니다. 

Error creating bean with name 'securityConfig': Requested bean is currently in creation

 

해당 에러를 검색한 후 Spring에서 순환 참조(Circular Dependency) 가 발생했다는 것을 인지하였습니다.



🔍 원인 분석

순환 구조 분석

  • SecurityConfig 클래스:
.addFilterBefore(new JwtAuthenticationFilter(jwtUtil, authService.getUserStore()), ...)

AuthService를 주입 받아서 내부의 userStore를 사용합니다.

 

  • AuthService 클래스:
private final BCryptPasswordEncoder passwordEncoder;
private final JwtUtil jwtUtil;

이 JwtUtil은 @Component로 등록된 상태이며, SecurityConfig가 @Configuration이므로 Spring이 AuthService를 초기화할 때 다시 SecurityConfig를 필요로 하게 됩니다

 

결과적으로

 

  • SecurityConfig → AuthService → JwtUtil → 다시 SecurityConfig
  • 위와 같은 빈 생성 순환이 발생하게 됩니다.
  • Spring Boot 2.6 이후 버전에서는 기본적으로 순환 참조를 허용하지 않기 때문에, 이 구조에서는 앱이 실행조차 되지 않으며 테스트도 실패합니다.

✅ 해결 방법 

 

 

 

의존성 직접 분리 + 책임 재배분

1. SecurityConfig → AuthService 의존 제거
    SecurityConfig가 AuthService를 의존할 필요는 없음.
    JWT 필터가 필요한 것은 userStore만이기 때문.

2. UserStore를 별도 @Component로 분리하고 SecurityConfig에 직접 주입

private final UserStore userStore;
...
.addFilterBefore(new JwtAuthenticationFilter(jwtUtil, userStore.getAll()), ...)

 

3.  AuthService가 UserStore를 의존하도록 재설계

private final UserStore userStore;
...
if (userStore.exists(username)) ...

 

최종 구조

 

  • SecurityConfig → JwtUtil, UserStore
  • AuthService → JwtUtil, UserStore
  • 더 이상 SecurityConfig ↔ AuthService 간 순환 관계가 없음 

변경 전/후 비교  

 

[변경 전]

// SecurityConfig
.addFilterBefore(new JwtAuthenticationFilter(jwtUtil, authService.getUserStore()), ...)

 

[변경 후]

// SecurityConfig
.addFilterBefore(new JwtAuthenticationFilter(jwtUtil, userStore.getAll()), ...)

 


📌  교훈 및 정리 

 

  • 서로가 서로를 참조하는 구조는 리팩토링 대상
    의존 관계가 강하게 얽히면 테스트도 어렵고 유지보수도 힘들어진다는 것을 몸소 깨달을 수 있었다.
  • 공통 데이터는 중립적인 별도 컴포넌트로 관리
    UserStore처럼 여러 클래스가 공유해야 하는 책임은 명확하게 분리한다는 것이 좋겠다고 느낄 수 있었다.
  • SpringBootTest는 전체 빈 설정을 다 읽기 때문에 구조 문제가 바로 드러난다
    단위 테스트 전에 애플리케이션 구조를 점검할 수 있어서 좋았다.