✅ 개요
Spring Data Jpa 인터페이스에서 default 키워드를 사용할 때의 발생하는 문제점과 해결 방법을 기록하고자 합니다.
✅ 문제 상황
평소와 같이 `JpaRepository`를 사용하여 편리하게 JPA를 사용할 때 default 키워드를 사용하면 서비스 로직에서 더 편리하게 사용할 수 있을 것 같아서 다음과 같이 작성했습니다.
public interface MemberRepository extends JpaRepository<Member, Long> {
default Member getMemberById(Long id) {
return findById(id).orElseThrow(() -> new EntityNotFoundException("Cannot be found Member for id: " + id));
}
}
이렇게 하면 레포지토리를 사용하는 여러 서비스 계층에서 각각 예외 처리를 할 필요가 없어 코드 중복을 줄일 수 있을 것이라 생각했습니다. 그리고 예외가 발생하면 컨트롤러 예외 처리 핸들러(어드바이스)에도 `EntityNotFoundException`이 정상적으로 전달될 거라 생각했습니다.
하지만 예상과 다르게 뜬금없는 `JpaObjectRetrievalFailureException` 예외가 전달되고 cause로 `EntityNotFoundException`이 담겨있는 현상을 발견했습니다. 대체 왜 이런 것인지 분석해 보았습니다.
✅ 원인 분석
`JpaRepository`를 상속받아 사용하면 내부적으로 `SimpleJpaRepository`에 의해 동작합니다. 그리고 `SimpleJpaRepository`는 `@Repository`가 선언되어 있습니다.

여기서 중요한 점은 `@Repository`는 데이터 계층 예외를 스프링 예외로 변환해주는 기능을 갖고 있다는 것입니다. 이 점을 유의한 채 `JpaObjectRetrievalFailureException` 클래스를 살펴 보았습니다.

Javadoc 설명을 보면 JPA의 `EntityNotFoundException` 예외를 변환한다고 합니다. 그리고 패키지 부분을 주목해보면 ` JpaObjectRetrievalFailureException`은 스프링 계층에 속하고, `EntityNotFoundException`은 표준JPA 예외로 데이터 접근 계층에 속한다는 것을 알 수 있습니다.
또 Javadoc See Also 부분에 있는 메서드를 확인해보면 다양한 데이터 접근 계층 예외를 스프링 계층 예외로 변환하는 것을 확인할 수 있습니다.

즉 원인을 정리해보면 default 메서드에서 작성한 `findById()`는 `SimpleJpaRepository`에서 동작하고, 예외가 발생하면 `@Repository`의 기능으로 인해 위 메서드를 거쳐 예외가 변환되었기 때문입니다.
✅ 해결 방법
해결 방법은 간단했습니다. default 키워드를 사용하는 대신 서비스 계층에서 직접 예외 처리를 하면 됩니다. 서비스 계층에서는 `@Repository`를 선언하지 않기 때문에 `EntityNotFoundException`을 throw하면 변환되지 않고 그대로 예외 처리 핸들러로 전달이 됩니다.
📌 정리
내부적으로 처리되는 과정을 모르고 개발을 편리하게만 하려다가 문제 상황을 마주했습니다. 하지만 이번 트러블슈팅을 통해 스프링 데이터 JPA를 이해하는 데 도움이 되었던 것 같습니다.
'트러블슈팅' 카테고리의 다른 글
| [Redis] 캐시 스탬피드 현상 (0) | 2025.12.14 |
|---|---|
| [Spring WebSocket] STOMP - convertAndSendToUser 알고 사용하기 (0) | 2025.11.04 |
| 쿠키 Samesite 설정으로 이슈 해결하기 (0) | 2025.10.19 |
| Spring OAuth AccessToken 제대로 추출하기 (0) | 2025.10.19 |