포스트

순환 참조 문제 해결: @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로 순환 참조 에러를 임시적으로 회피할 수는 있지만, 설계를 개선해야 합니다.”

근본적인 해결책

멘토님이 제안한 방법들:

  1. 정말로 서로 의존해야 하는지 점검
    • 양쪽에서 쓰는 로직을 다시 살펴보니, 일부는 굳이 서비스를 통하지 않아도 됐다
    • Repository를 직접 주입받아서 해결할 수 있는 부분이 있었다
  2. 단방향으로 바꾸기
    • 양방향 의존을 단방향으로 바꿀 수 있는지 검토
    • 한쪽 서비스에서 다른 쪽 서비스를 참조하지 않도록 로직을 재배치
  3. 파사드 서비스 도입
    • 두 서비스를 조합해서 쓰는 상위 레이어 서비스를 만든다
    • ProductOptionFacade 같은 걸 만들어서 두 서비스를 조합

unnamed 8.png

정리

  • @Lazy는 일단 실행되게 만드는 임시 조치로는 괜찮지만, 문제를 없앤 건 아니다(의존성 꼬임은 그대로 남는다)
  • 순환 참조가 떴다는 건 보통 역할이 섞였거나, 레이어가 뒤틀렸거나, 책임이 커진 상태라는 신호였다
  • 그래서 한 번만 고치고 끝낼 일이 아니라, 의존성 방향을 다시 잡고 단방향으로 정리하거나 파사드로 묶는 식으로 구조를 손보는 게 결국 더 빠르다

프로젝트: 카카오테크캠퍼스 3기 선물하기 API 클론 코딩 관련 링크

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.