공부함

SpringDataJpa 사용자 정의 Repository 적용 본문

스프링

SpringDataJpa 사용자 정의 Repository 적용

찌땀 2023. 12. 9. 18:43

사용자 정의 Repository가 필요한 이유

 

public interface ReviewRepository extends JpaRepository<Review, Long>{ 
...
}

 

Spring이 JpaRepository를 상속하는 인터페이스의 구현체를 자동으로 생성해준다. Service에서는 자동 생성된 구현체를 주입받아 사용한다. 

 

기본으로 제공하는 메서드들을 사용할 수 있다. 더해서 명명규칙에 따라 메서드를 작명하면 알아서 쿼리를 짜준다. 개발자는 메서드를 가져다 쓰기만 하면 되는 편리한 기능이다. 

 

https://zara49.tistory.com/130

 

[JPA] JPA Repository 메서드 명명규칙

JPA는 우리가 쿼리를 열심히 짜는것을 대신해 간단하게 표현하여 쿼리를 사용할 수 있도록 해준다. 그렇다면 어떤 형태로 메소드 이름을 붙이면 되는지, 명명 규칙에 대해 간략하게 정리해두자.

zara49.tistory.com

메서드 명명규칙에 대해서는 위 글을 참고하자.

 

하지만 명명규칙 만으로는 해결할 수 없고 쿼리를 직접 작성해야 할 때가 있기 마련이다.

 

https://velog.io/@codren/Query-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98

 

@Query 어노테이션

Spring Data JPA @Query 어노테이션, JPQL, @Param, nativeQuery

velog.io

public interface ReviewRepository extends JpaRepository<Review, Long>, QueryDslReviewRepository {
    ...
    @Query("select distinct function('date_format', r.createdAt, '%d') from Review r where r.member.id = :memberId and function('date_format', r.createdAt, '%m') = :month")
    List<Integer> findAllByMonth(@Param("memberId") Long memberId, @Param("month") int month);
    ...
}

위와 같이 @Query 애노테이션을 사용할 수 있다. JPQL을 통해 쿼리를 작성할 수 있다. nativeQuery=true로 지정하면 JPQL이 아닌 SQL로 작성할 수 있다. (@Query에 관해서는 위 글을 참고하자.) 

 

하지만 @Query로 직접 JPQL 쿼리를 작성하더라도 동적 쿼리 작성은 어렵다. 따라서 Querydsl을 사용하는 것이 좋다. 

Querydsl 뿐만이 아니라 JPA 직접 사용, 스프링 JDBC Template 등이 필요할 때가 있다.

이럴 때 사용자 정의 Repository를 사용한다. 

사용자 정의 Repository 사용 방법

public interface ReviewRepository extends JpaRepository<Review, Long>{
...
}

 

사용자 정의 리포지토리를 추가하려면 어떻게 해야 할까?

단순하게 CustomReviewRepository 클래스를 생성하고 사용하는 것을 생각할 수 있다. 실제로 내가 Querydsl을 처음 적용할 때 이렇게 하려고 했다. 

 

public class ReviewService {

    private final ReviewRepository reviewRepository;
    private final CustomReviewRepository customReviewRepository;
    ...
    }

JpaRepository의 메서드를 호출할 때는 reviewRepository를 사용하고, 커스텀한 메서드를 호출할 때는 customReviewRepository를 사용해야 한다. 불편하고 코드가 지저분해진다.

 

public class CustomReviewRepository implements ReviewRepository{
 // 모든 메서드를 직접 구현해야 함!
}

 

그렇다면 CustomReviewRepository가 ReviewRepository를 구현하게 하고 커스텀 메서드를 CustomReviewRepository에 구현하면 되는 것 아닐까? 이렇게 하면 CustomReviewRepository로 ReviewRepository의 메서드도 호출할 수 있기 때문이다. 하지만 위에서 설명했듯이, Spring에서 JpaRepository의 구현체를 자동으로 생성해준다. 개발자가 직접 JpaRepository의 구현체를 구현하면 JpaRepository의 메서드를 전부 직접 구현해야 한다. 즉 Spring Data Jpa를 사용하는 의미가 없다.

 

그럼 도대체 어떻게 사용해야 할까?

public interface QueryDslProblemRepository {
    PageImpl<Problem> findAll(
            Pageable pageable, DataStructureConstant dataStructure, AlgorithmConstant algorithm, SortConstant orderBy);
}

public class QueryDslProblemRepositoryImpl implements QueryDslProblemRepository {

    private final JPAQueryFactory jpaQueryFactory;

    public PageImpl<Problem> findAll(
            Pageable pageable, DataStructureConstant dataStructure, AlgorithmConstant algorithm, SortConstant orderBy) {
        List<Problem> results = getResults(pageable, dataStructure, algorithm, orderBy);
        long size = getSize(dataStructure, algorithm);
        return new PageImpl<>(results, pageable, size);
    	}
    ...
    }
    
@Repository
public interface ProblemRepository extends JpaRepository<Problem, Long>, QueryDslProblemRepository {
    Optional<Problem> findByLink(String link);
}

이런 식으로 사용하면 된다. ProblemRepository가 JpaRepository와 QueryDslProblemRepository를 상속하게 한다.

QueryDslProblemRepository에는 커스텀 메서드들을 선언한다. 이에 대한 구현은 QueryDslProblemRepositoryImpl에서 한다. 이 때, 명명규칙을 지켜야 한다. 커스텀 repository를 구현하는 구현체의 이름은 커스텀repository+Impl의 형태여야 한다. 이렇게 규칙을 지켜 커스텀 repository를 만들면 ProblemRepository를 통해 커스텀 Repository 구현체에서 구현한 메서드들을 호출할 수 있다. 또한 Spring이 알아서 ProblemRepository의 구현체를 생성해주므로 JpaRepository의 메서드를 일일히 구현할 필요도 없다. 커스텀한 메서드와 JpaRepository의 메서드들을 하나의 객체로 전부 접근 가능해 코드가 간결해지고 관리가 쉬워진다.

 

'스프링' 카테고리의 다른 글

ApplicationContext  (0) 2024.08.28
서블릿과 MVC 패턴과 프론트 컨트롤러 패턴  (1) 2024.08.27
카카오 로그인  (0) 2024.08.02
@RestControllerAdvice를 활용한 스프링 예외 처리  (0) 2023.12.19
Pageable  (0) 2023.12.08