스프링데이터JPA는 인터페이스만 정의해주고, 구현체는 스프링이 자동생성해준다.
우리가 직접 구현체를 만들려면 어떻게 해야 될까?
보통 쿼리DSL을 작성할 때, 많이 사용한다고 한다.
public interface CustomMemberRepository { 
	List<Member> callCustom(); // 커스텀 메소드
    ...
}
@RequiredArgsConstructor
public class MemberRepositoryImpl implements CustomMemberRepository {
	private final EntityManager em;
	@Override
	public List<Member> callCustom() {
		return em.createQuery("select m from Member m", Member.class)
	        .getResultList();
	}
}
JPA 레포지토리 이름 + Impl 네이밍 규칙을 지켜야한다.public interface MemberRepository extends JpaRepository<Member, Long>, CustomMemberRepository {
	...
}
@RequiredArgsConstructor
public class MemberService {
	private final MemberRepository memberRepository;
    
    List<Member> findAll(){
    	return memberRepository.callCustom(); // Custom 구현체 사용
    }
}
[1] CustomMemberRepository (interface) // 커스텀 Interface
    └─ callCustom()
            ▲
[2] MemberRepositoryImpl (implements CustomMemberRepository) // 커스텀 구현체
    └─ EntityManager로 JPQL 실행
            ▲
[3] MemberRepository (interface) // JPA 레포지토리에 상속
    └─ extends JpaRepository + CustomMemberRepository
            ▲
[4] MemberService // 서비스 단에서 JPA 레포지토리만으로 커스텀 구현체 사용
    └─ memberRepository.callCustom() 호출
핵심로직과 아닌 로직을 클래스로 분리하는 것이 복잡성을 떨어트리는데에 더 좋다.
꼭 해당도구를 사용해서 관련된 모든 문제를 해결하려고 하지 않도록 주의하자.