결론부터 말하면,
아래처럼 코드 처럼 JpaRepository를 상속받은 인터페이스가 있다고 할 때,
public interface BoardJpaRepository extends JpaRepository<BoardEntity, Long>
"BoardJpaRepositoryImpl" 이라는 클래스를 만들면, Spring이 해당 레포지토리의 "커스텀 구현체" 라고 인식합니다.
---
Spring 스터디를 진행하면서 과제를 받았습니다.
과제 내용은 "JDBC 로 간단하게 CRUD 구현하고, JPA로 바꿔보기"
저는 순수 JDBC / Spring JDBC / JPA 순으로 구현하려고 했습니다.
DB접근 방식만 바뀌고, 동작하는 메서드는 동일하기 때문에
BoardRepository 라는 추상화된 레포지토리 인터페이스를 두고, JDBC / Spring JDBC / JPA가 구현하도록 했습니다.
클래스 다이어그램은 아래와 같습니다.
JDBC / Spring JDBC 까지 구현할 때는 문제가 없었습니다.
BoardService에서는 BoardRepository에만 의존하고, Profile에 따라 구현체를 주입해주기때문에 Service나 Controller코드에서도 변경이 없었습니다.
그런데 JPA로 구현할 때가 문제였습니다.
먼저, BoardJpaRepository는 JpaRepository를 상속받게 하였고
public interface BoardJpaRepository extends JpaRepository<BoardEntity, Long> {
// 필요한 메서드들..
}
BoardJpaRepositoryImpl 클래스는 아래와 같습니다.
@Profile("jpa")
@Repository
@RequiredArgsConstructor
public class BoardJpaRepositoryImpl implements BoardRepository {
private final BoardJpaRepository boardJpaRepository;
@Override
public Board findById(Long id) {
return boardJpaRepository.findById(id)
.map(e -> e.toBoard())
.orElse(null);
}
// 기타 BoardRepository 인터페이스 구현...
}
그런데 위처럼 구현하고 서버를 켰더니..
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
문구와 함께
boardJpaRepositoryImpl 에서 순환참조가 일어난다고 하는 것이었습니다. 제일 처음에 말한 결론을 모른 상태로 문제를 해결하려니 많은 시간이 소요됐습니다.
Custom Repository Implementations :: Spring Data JPA
The approach described in the preceding section requires customization of each repository interfaces when you want to customize the base repository behavior so that all repositories are affected. To instead change behavior for all repositories, you can cre
docs.spring.io
스프링 공식 문서에서
사진과 같은 내용을 확인할 수 있습니다.
결국에는 JpaRepository를 상속받은 인터페이스명 + Impl을 붙인 클래스는 Spring이 자동으로 커스텀 구현체로 감지한다는 것이었고,
BoardJpaRepository프록시를 생성하려고 하니, 커스텀 구현체(BoardJpaRepositoryImpl)를 생성해야 하고, BoardJpaRepositoryImpl는 생성자에서 BoardJpaRepository를 주입받도록 정의되어있으니까, 생성 -> 주입 -> 생성 -> ... 의 무한 루프에 빠지게 되는 것이었습니다.. 단순히 BoardJpaRepositoryImpl 같은 네이밍이 아닌 다른 이름, 저는 BoardJpaCustomRepositoryImpl 로 바꾸어 해결했습니다.
굳이 BoardJpaRepositoryImpl를 써야겠다고 하면 공식문서에는 @EnableJpaRepositories 의 repositoryImplementationPostfix 옵션을 바꾸거나, @Bean 팩토리 메서드를 직접 만들라고 합니다.