두 개 이상의 빈이 서로를 참조할 때 발생하는 문제로, 스프링 프레임워크에서는 애플리케이션 컨텍스트가 빈을 초기화할 때 문제가 발생합니다. 간단히 말해, 빈 A가 빈 B를 필요로 하고, 빈 B가 다시 빈 A를 필요로 하는 상황을 말해요. 이로 인해 애플리케이션이 제대로 시작되지 않거나 예기치 못한 런타임 오류가 발생할 수 있어요.
순환 의존성 문제가 발생할 때 보통 다음과 같은 에러 로그를 볼 수 있어요 🤔
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'beanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
이런 에러 메시지는 빈 생성 과정에서 순환 참조가 발견되었음을 알려줍니다. 문제가 발생하는 코드를 보면서 확인해 볼게요.🤨
@Component
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private final ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
위에 코드에서는 ServiceA와 ServiceB가 서로를 의존하고 있어 순환 의존성이 발생해요. Spring 컨테이너가 이 두 빈을 생성하는 데 어려움을 겪게 되죠. 지금은 코드길이가 짧아서 두 클래스 간에 사용이 이상하게 보이지만, 두 클래스가 서로 의존하는 일은 정말 흔하게 볼 수 있을 거예요 😂 그렇다면 어떻게 해결해야 할까요? 🤔
1. @Lazy 어노테이션 사용
@Lazy 어노테이션을 사용하여 지연 초기화를 적용하면, 빈이 필요할 때까지 초기화를 미룰 수 있습니다. 이를 통해 순환 의존성 문제를 해결할 수 있죠.
@Component
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private final ServiceA serviceA;
@Autowired
public ServiceB(@Lazy ServiceA serviceA) {
this.serviceA = serviceA;
}
}
stack overflow에서 동일한 문제가 발생했을 때, 역시나 첫 번째 high score를 가진 댓글이 @Lazy를 사용하여 해결하는 방안이네요 👍
2. 빈의 구조 재설계
두 번째 방안으로는 빈의 의존 관계를 재설계하여 순환 의존성을 피하는 것이 가장 확실한 방법입니다. 주입할 필요가 없는 부분을 분리하거나, @PostConsturct를 이용해 초기화를 지연시키는 등의 방법을 사용할 수 있죠.
@Component
public class ServiceA {
private ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
@PostConstruct
public void initialize() {
serviceB.doSomething();
}
}
@Component
public class ServiceB {
private final ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doSomething() {
serviceA.initialize();
}
}
@PostConstruct를 사용하여 ServiceA의 초기화를 빈 생성 이후로 지연시키면, 빈이 순차적으로 초기화되므로 순환 의존성을 피할 수 있어요. 놀랍게도 두 번째로 인기 있는 해결 방안이 역시 @PostConstruct를 사용해서 빈의 순서를 조작하는 방법이네요 😆
순환 의존성은 작은 실수로 인해 발생할 수 있지만, 문제의 핵심을 이해하고 적절한 방법을 적용하면 쉽게 해결할 수 있는 문제 같아요. 중간 계층을 도입하거나, 초기화를 지연시키는 전략은 흔한 해결책으로, 개발하다가 위와 같은 에러가 발생하면 "아 빈이 문제가 있구나", "빈이 서로 의존하고 있구나"라고 여유롭게 인지하고 해결할 수 있을 거 같네요 ☕️