[Spring Data JPA] JpaRepository 동적으로 Limit 적용하기

Chaeyun·2024년 4월 4일

Spring

목록 보기
2/3

개요

티켓 예매 사이트 스프링 프로젝트를 개발하고 있다.

티켓팅 아이디로 아직 구매되지 않은 티켓들 중 n개만큼만 조회해오는 로직을 구현하려 한다.

이 때 n은 사용자가 구매를 원하는 개수에 따라 동적으로 변화하는 값이다.

limit을 사용하면 쉽게 구현할 수 있어 JpaRepository에 limit을 적용하는 방법을 찾아보게 되었다.

문제 상황

limt을 적용하는 방법에 관해 검색해보니 아래와 같은 방법들을 찾을 수 있었다.
1. Top keyword 사용
2. Pageable 객체 사용
3. 네이티브 쿼리 작성

그런데 위의 방법들은 전부 반려했다. 이유는 다음과 같다.

1. Top Keyword 사용

Top Keyword를 사용하면 아래와 같이 JpaRepsoitory 메소드를 작성할 수 있다.

List<User> findTop10ByName(String name);

문제는 findTopXXX Keyword를 사용하는 경우 Top뒤에 들어가는 숫자를 미리 정해줘야 한다.

하지만 내가 작성하는 로직에서는 N의 값을 동적으로 바꿀 수 있어야 한다.

그래서 반려했다.

2. Pageable 객체 사용

Pageable 객체를 사용하려는 경우 아래와 같이 JpaRepository 메소드를 작성하면 된다.

List<User> findByName(String name, Pageable pageable);

메소드 호출시에는 아래와 같이 PageRequest 객체를 넘겨주면 된다.

int page = 0;
int size = 10;
var users = userRepository.findByName("name",PageRequest.of(page, size));

이때 page를 0으로 두고 size의 값(n)을 동적으로 넘겨주면 내가 원하던 limit과 같은 효과를 낼 수 있다.

하지만 이 또한 반려했다.
이유는 다음과 같다.

페이징 처리를 할때는 count 쿼리가 자동으로 호출된다. count 쿼리가 있어야 전체 페이지를 구할 수 있기 때문이다.

그런데 사실상 페이징을 사용하지 않기 때문에 count 쿼리를 날리는 것은 불필요한 쿼리를 추가로 날리는 것뿐이다.

이로 인해 성능에 영향이 가는 게 싫어서 이 방법은 반려했다.

3. 네이티브 쿼리 작성

아래와 같이 nativeQuery를 작성하여 limit을 줄 수 있다.

@Query("SELECT u FROM Users u WHERE name = ?1 LIMIT ?2", nativeQuery = true))
List<User> findByNameAndLimit(String name, Integer limit);

정 방법이 없으면 위와 같이 직접 쿼리를 작성해도 된다.
하지만 순수 Query를 작성하는 방법은..
가급적 피하고 싶었기에 후순위로 두고 다른 방법을 찾아봤다.

해결 방안

Spring Data JPA 공식 문서에서 방법을 찾았다.

List<User> findByLastname(Limit limit);

위와 같이 Limit 객체를 사용하는 방법이 있었다.

메소드 호출 시에는 다음과 같이 Limit객체를 넘겨주면 된다.

import org.springframework.data.domain.Limit;

var cnt = 10;
userRepository.findByLastname(Limit.of(cnt));

다행히 방법을 찾아서 좋았는데 문서 신뢰도는 조금 떨어졌다.
그 이유는 다음과 같다.

  1. 저 한줄 예시만 있고 자세한 설명이 없어서 메소드 호출시 Limit 객체를 넘겨주는 방법을 따로 찾아야 했다.
  2. 예시에서 ByLastname이라는 키워드를 쓰고 있으니 파라미터로 lastname을 받아야 할 텐데... 안 받고 있다.

적용

이 방법을 이용해 실제로 티켓을 조회하는 로직은 다음과 같이 완성되었다.

List<Ticket> findByTicketingIdAndPurchaseIsNullOrderById(UUID ticketingId, Limit limit);
var tickets = ticketRepository.findByTicketingIdAndPurchaseIsNullOrderById(
			ticketingId, Limit.of(ticketCount));

결론

Spring Data JpaRepository에서 limit을 동적으로 구현하는 방법에 대해 알아봤다.

검색을 통해 알아낸 대부분의 방법(Top 키워드, Pagaeable, 순수 쿼리)은 현재 내 상황에 적합하지 않거나 번거로웠다.

추가+) JPQL에서 .setFirstResult().setMaxResult()로 구현하는 방법도 있다.

하지만 공식 문서를 통해 Limit 객체를 사용하는 방법을 찾아 적용해본 결과, 손쉽게 원하는 기능을 작성할 수 있었다.

나처럼 동적 사이즈로 limit을 JpaRepository에 적용하려는 사람들이 고생하지 않기를 바라며 이 글을 포스팅한다.

References

0개의 댓글