Spring Data JPA에서 커스텀 리포지토리 구현 시 주의사항

이지니·2025년 11월 9일

TIL

목록 보기
10/11

4계층 구조를 학습하면서, 비즈니스 로직과 인프라 구현체를 명확히 분리하는 계층 설계 방식으로 프로젝트를 진행 중에 있습니다.
실제 구현체를 분리하고, 주입 구조를 구성하던 중 발생한 오류에 대해 기록합니다.

1. 초기 설계

  1. 순수 비즈니스 로직만을 담당하는 DeliveryRepository 인터페이스 생성
public interface DeliveryRepository {
    Delivery save(Delivery delivery);
    boolean existsByOrderIdAndDeletedAtIsNull(UUID orderId);
}
  1. 외부 기술을 모아둔 infrastructure 계층 추가
    2-1. DeliveryJpaRepository 인터페이스 생성
public interface DeliveryJpaRepository extends JpaRepository<Delivery, UUID> {
    boolean existsByOrderIdAndDeletedAtIsNull(UUID orderId);
}
  1. 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);
    }
}

2. 문제 상황

스프링을 실행하자마자 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 리포지토리 주입 구조도 이론상 문제가 없는 것처럼 보였습니다.

2. 원인 분석

원인은 네이밍 충돌이었습니다.

Spring Data JPA는 XXXRepository 인터페이스가 있으면 자동으로 XXXRepositoryImpl 이름의 클래스를 그 구현체로 인식하는 규칙이 있습니다.
그런데 제가 만든 클래스 이름이 DeliveryJpaRepositoryImpl이었기 때문에, 스프링은 이 클래스를 DeliveryJpaRepository의 구현체라고 착각하게 된 것입니다.

결과적으로 이런 순환 구조가 생기게 된 것입니다.

DeliveryJpaRepositoryImpl 
→ DeliveryJpaRepository (주입 요청)
→ 스프링이 DeliveryJpaRepositoryImpl을 구현체로 인식
→ 다시 DeliveryJpaRepositoryImpl 생성 시도 → 순환 참조 발생!!

3. 해결 방법

Spring Data JPA의 자동 인식 네이밍 규칙과 겹치지 않는 이름을 지정하여 간단하게 문제를 해결할 수 있었습니다.

@Repository
@RequiredArgsConstructor
public class DeliveryRepositoryAdaptor implements DeliveryRepository { 
    private final DeliveryJpaRepository deliveryJpaRepository;
    ...
}

참조:
https://devwriter.tistory.com/24

profile
화이팅

0개의 댓글