이 글은 Top Difficult Java Spring Boot Problems Every Developer Should Know: Unraveling the Circular Dependency Dilemma를 읽고 정리한 글입니다.
Circular dependency는 Spring Boot Framework의 의존성 주입(Dependency Injection(DI)) 지원
이라는 특징으로 인해 빈번하게 발생하는 문제입니다.
Spring Boot에서는 Bean
이라는 개념을 이용해 의존성 주입(Dependency Injection(DI))
을 지원하는데 다음과 같은 상황에서 Circular dependency가 발생합니다.
순환 참조가 생길 경우 다음과 같은 문제가 발생합니다.
Spring Boot에서는 Circular dependency가 발생할 경우 애플리케이션 실행간 다음과 같은 로그가 발생합니다.
Error creating bean with name 'A': Unsatisfied dependency expressed through field 'B'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'B': Requested bean is currently in creation: Is there an unresolvable circular reference?
Circular dependency가 어떤 Bean에서 발생했는지에 대해 자세하게 로그로 남겨주어 해당 부분을 해결해야만 정상적인 실행이 가능하도록 하였습니다.
그렇다면 어떻게 해결해야 할까요?
애플리케이션에서 순환참조가 발생할 수 없는 아키텍쳐로 재디자인 합니다. 대표적인 예시로는 Hexagonal Architecute가 있습니다.
Spring Boot는 애플리케이션 초기화간 필요한 의존성에 대해서 바로 구성하는 특징이 있습니다. @Lazy를 이용할 경우, Bean 초기화간 해당 Bean이 실제로 필요로 할 때까지 Bean 초기화를 지연시킬 수 있습니다.
@Component
public class A {
@Autowired
@Lazy
private B b;
}
setter injection 또는 @Autowired를 통한 의존성 주입 방식은 관련된 Bean을 인스턴스화 한 뒤 초기화 할 수 있다는 특징이 있습니다.
다만, Spring Boot에서는 injection 방식을 Constructor Injection을 권장한다는 글을 공식 문서를 통해 제공하고 있어 해당 방식은 비권장드립니다. (참고)
@Component
public class A {
private B b;
@Autowired
public void setB(B b) {
this.b = b;
}
}
Spring boot 에서는 @PostConstruct를 통해 모든 Bean이 생성된 이후 추가 초기화를 할 수 있습니다. 해당 방식을 통해 모든 Bean이 생성될 때까지 DI 작업을 연기하여 Circular dependency를 해결할 수 있습니다.
@Component
공개 클래스 A {
@Autowired
비공개 B b;
@PostConstruct
public void init ( ) {
// 여기서 'b'에 의존하는 작업을 수행합니다.
}
}
Spring Boot의 Circular dependency는 종종 발생하는 문제입니다. Circular dependency가 왜 발생하는지 원인을 알고 해결방법을 알고 있다면 이는 개발자에게 더이상 어려운 문제가 아니라 생각합니다.
다만, 2~4번과 같은 방식은 빠르게 해결할 수 있는 방법들이지만 해당 코드로 지속적인 개발을 진행한다면 계속해서 Circular dependency가 발생할 확률이 높은 문제가 있습니다.
따라서 원인을 해결하기 이전에 왜 Circular dependency가 발생했을지
에 대한 고민을 깊게 해보고 아키텍쳐가 원인인지 개발자의 클래스 분리가 문제인지 확인해보신 뒤 해결을 진행하시는 것을 권장드립니다.
읽어주셔서 감사합니다.