순환 참조 문제 해결: @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같은 걸 만들어서 두 서비스를 조합
[이미지 필요] 순환 참조 의존성 다이어그램 (변경 전: 양방향 화살표 / 변경 후: 단방향 또는 파사드 패턴)
배운 점
@Lazy는 순환 참조를 “숨기는” 것이지 “해결하는” 게 아니다- 순환 참조가 생기면 설계가 잘못됐다는 신호다
- 객체지향 설계에서 의존성 방향은 정말 중요하다
- 당장 돌아가게 만드는 것보다 구조를 고치는 게 나중에 훨씬 편하다
카카오테크캠퍼스 3기 선물하기 API 클론 코딩 중 멘토님 피드백 정리.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.