4계층 구조를 학습하면서, 비즈니스 로직과 인프라 구현체를 명확히 분리하는 계층 설계 방식으로 프로젝트를 진행 중에 있습니다.
실제 구현체를 분리하고, 주입 구조를 구성하던 중 발생한 오류에 대해 기록합니다.
DeliveryRepository 인터페이스 생성 public interface DeliveryRepository {
Delivery save(Delivery delivery);
boolean existsByOrderIdAndDeletedAtIsNull(UUID orderId);
}
DeliveryJpaRepository 인터페이스 생성public interface DeliveryJpaRepository extends JpaRepository<Delivery, UUID> {
boolean existsByOrderIdAndDeletedAtIsNull(UUID orderId);
}
DeliveryRepository의 구현체(DeliveryJpaRepositoryImpl) 작성 (DeliveryJpaRepository 사용)@Repository
@RequiredArgsConstructor
public class DeliveryJpaRepositoryImpl implements DeliveryRepository {
private final DeliveryJpaRepository deliveryJpaRepository;
@Override
public Delivery save(Delivery delivery) {
return deliveryJpaRepository.save(delivery);
}
@Override
public boolean existsByOrderIdAndDeletedAtIsNull(UUID orderId) {
return deliveryJpaRepository.existsByOrderIdAndDeletedAtIsNull(orderId);
}
}
스프링을 실행하자마자 deliveryJpaRepositoryImpl 순환 참조 오류가 발생했습니다.
The dependencies of some of the beans in the application context form a cycle:
deliveryService defined in file [C:\nbcamp\project\mcargo\delivery-service\build\classes\java\main\com\mcargo\deliveryservice\application\service\DeliveryService.class]
┌─────┐
| deliveryJpaRepositoryImpl defined in file [C:\nbcamp\project\mcargo\delivery-service\build\classes\java\main\com\mcargo\deliveryservice\infrastructure\repository\DeliveryJpaRepositoryImpl.class]
└─────┘
처음에는 단순히 빈 주입 오류라고 판단하고 @RequiredArgsConstructor, @Repository 설정도 모두 확인했으나 문제를 발견하지 못했습니다.
도메인 인터페이스 → 구현체 → JPA 리포지토리 주입 구조도 이론상 문제가 없는 것처럼 보였습니다.
원인은 네이밍 충돌이었습니다.
Spring Data JPA는 XXXRepository 인터페이스가 있으면 자동으로 XXXRepositoryImpl 이름의 클래스를 그 구현체로 인식하는 규칙이 있습니다.
그런데 제가 만든 클래스 이름이 DeliveryJpaRepositoryImpl이었기 때문에, 스프링은 이 클래스를 DeliveryJpaRepository의 구현체라고 착각하게 된 것입니다.
결과적으로 이런 순환 구조가 생기게 된 것입니다.
DeliveryJpaRepositoryImpl
→ DeliveryJpaRepository (주입 요청)
→ 스프링이 DeliveryJpaRepositoryImpl을 구현체로 인식
→ 다시 DeliveryJpaRepositoryImpl 생성 시도 → 순환 참조 발생!!
Spring Data JPA의 자동 인식 네이밍 규칙과 겹치지 않는 이름을 지정하여 간단하게 문제를 해결할 수 있었습니다.
@Repository
@RequiredArgsConstructor
public class DeliveryRepositoryAdaptor implements DeliveryRepository {
private final DeliveryJpaRepository deliveryJpaRepository;
...
}