
Spring Data JPA countQuery는 Pageable 결과에서 전체 개수를 구하기 위해 실행되는 별도 쿼리입니다. Page를 반환하면 현재 페이지 목록만 필요한 것이 아니라 전체 페이지 수를 계산할 전체 row count도 필요합니다.
문제는 목록을 가져오는 쿼리와 개수를 세는 쿼리가 항상 같은 모양일 수 없다는 점입니다. fetch join, group by, distinct, native query가 들어가면 Spring Data JPA가 count 쿼리를 안전하게 추론하기 어려워질 수 있습니다.

Pageable은 왜 count를 필요로 할까
Slice가 아니라 Page를 반환하면 Spring Data JPA는 전체 데이터 개수를 알아야 합니다. 그래야 totalElements, totalPages 같은 값을 채울 수 있습니다.
Page<Order> findByStatus(OrderStatus status, Pageable pageable);이 메서드는 목록 쿼리만 실행하는 것이 아닙니다. 보통 현재 페이지 데이터를 가져오는 쿼리와 전체 개수를 세는 count 쿼리가 함께 실행됩니다.
자동 count 추론이 잘 되는 경우
단순한 derived query나 간단한 JPQL은 Spring Data JPA가 count 쿼리를 무난하게 만들 수 있습니다. where 조건이 단순하고 join 구조가 복잡하지 않다면 직접 countQuery를 적지 않아도 됩니다.
Spring Data JPA countQuery를 직접 써야 하는 신호
- @Query가 복잡해서 count 쿼리 변환이 실패한다
- fetch join 때문에 count에 불필요한 join이 들어간다
- group by 결과 개수와 row 개수가 다르다
- distinct 때문에 count 대상이 달라진다
- native query에서 자동 count를 기대하기 어렵다
- 목록 쿼리는 느리지만 count는 더 단순하게 만들 수 있다
fetch join과 Pageable이 만날 때
fetch join은 연관 엔티티를 함께 가져오기 위한 목록 조회 전략입니다. 하지만 count 쿼리에서는 연관 데이터를 가져올 필요가 없습니다. 오히려 fetch join이 count 쿼리에 섞이면 오류나 성능 문제가 생길 수 있습니다.
@Query(
value = """
select o
from Order o
join fetch o.member
where o.status = :status
""",
countQuery = """
select count(o)
from Order o
where o.status = :status
"""
)
Page<Order> findOrders(OrderStatus status, Pageable pageable);group by에서는 무엇을 세는지 먼저 정해야 한다
group by가 들어가면 count의 의미가 달라집니다. 원본 row 수를 셀지, 그룹 결과 개수를 셀지 결정해야 합니다. 페이지네이션이 그룹 결과를 대상으로 한다면 count도 그룹 결과 수를 세야 합니다.
성능 때문에 분리하는 경우
목록 쿼리는 화면에 필요한 연관 데이터와 정렬 조건을 포함할 수 있습니다. 하지만 count 쿼리는 전체 개수만 알면 됩니다. 그래서 countQuery를 직접 분리하면 불필요한 join을 줄여 성능을 개선할 수 있습니다.
실무 판단 기준
- 반환 타입이 Page인지 Slice인지 먼저 본다
- Page라면 count 쿼리가 실행된다고 가정한다
- @Query가 단순하지 않으면 실행 SQL을 확인한다
- fetch join, group by, distinct, native query가 있으면 countQuery 분리를 검토한다
- 목록 쿼리와 count 쿼리의 의미가 같은지 테스트로 확인한다
정리
Spring Data JPA countQuery는 Pageable을 위한 부가 옵션이 아니라, Page 결과의 의미를 완성하는 쿼리입니다. 단순 조회에서는 자동 추론에 맡길 수 있지만, 복잡한 조회에서는 목록 쿼리와 count 쿼리를 분리하는 편이 안전합니다.
공식 기준은 Spring Data JPA query methods 문서에서 확인할 수 있습니다. fetch join과 페이지네이션 위험은 JPA fetch join과 Pageable 글과 함께 보면 좋습니다.