순환 참조 문제 해결: @Lazy는 임시방편일 뿐
순환 참조 문제 해결: @Lazy는 임시방편일 뿐
문제 상황
카카오테크캠퍼스에서 선물하기 API를 구현하다가 순환 참조 문제를 겪었다.
1
2
ProductService → OptionService
OptionService → ProductService
옵션을 생성할 때 상품이 있는지 확인해야 해서 OptionService에서 ProductService를 참조했다. 그런데 ProductService에서도 옵션 관련 로직 때문에 OptionService를 참조하고 있었다.
애플리케이션을 실행하니까 순환 참조 에러가 떴다.
임시 해결: @Lazy
일단 @Lazy 어노테이션을 붙여서 해결했다.
1
2
3
4
public OptionService(OptionRepository optionRepository, @Lazy ProductService productService) {
this.optionRepository = optionRepository;
this.productService = productService;
}
@Lazy를 붙이면 빈 생성 시점을 실제로 사용할 때까지 지연시켜서 순환 참조 에러를 피할 수 있다.
멘토님 피드백
그런데 멘토님한테 이런 피드백을 받았다.
“순환 참조가 발생했다는 건 두 클래스가 서로 너무 깊게 엮여 있다는 증거예요. @Lazy로 순환 참조 에러를 임시적으로 회피할 수는 있지만, 설계를 개선해야 합니다.”
근본적인 해결책
멘토님이 제안한 방법들:
- 정말로 서로 의존해야 하는지 점검
- 양쪽에서 쓰는 로직을 다시 살펴보니, 일부는 굳이 서비스를 통하지 않아도 됐다
- Repository를 직접 주입받아서 해결할 수 있는 부분이 있었다
- 단방향으로 바꾸기
- 양방향 의존을 단방향으로 바꿀 수 있는지 검토
- 한쪽 서비스에서 다른 쪽 서비스를 참조하지 않도록 로직을 재배치
- 파사드 서비스 도입
- 두 서비스를 조합해서 쓰는 상위 레이어 서비스를 만든다
ProductOptionFacade같은 걸 만들어서 두 서비스를 조합
정리
@Lazy는 일단 실행되게 만드는 임시 조치로는 괜찮지만, 문제를 없앤 건 아니다(의존성 꼬임은 그대로 남는다)- 순환 참조가 떴다는 건 보통 역할이 섞였거나, 레이어가 뒤틀렸거나, 책임이 커진 상태라는 신호였다
- 그래서 한 번만 고치고 끝낼 일이 아니라, 의존성 방향을 다시 잡고 단방향으로 정리하거나 파사드로 묶는 식으로 구조를 손보는 게 결국 더 빠르다
프로젝트: 카카오테크캠퍼스 3기 선물하기 API 클론 코딩 관련 링크
- GitHub: Neibce/spring-gift-wishlist
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.
