Database

[QueryDsl] fetchCount() 가 deprecated 되었다.

강서월 2024. 8. 28. 11:46

특정 엔티티의 총 개수 를 조회하기 위해서 QueryDSL 의 fetchCount() 메서드를 사용했습니다.

하지만, fetchCount() 메서드가 회색 처리되고, 취소선이 나타나는 것을 발견했습니다. Warning 메세지에는 'fetchCount(): Long' is deprecated. 라는 내용이 뜨는데, 그 이유와 해결 방법에 대해 알아보겠습니다.

 

fetchCount(), fetchResulsts() 의 Deprecation

저희 팀이 사용하는 QueryDSL 5.0.0 버전부터는 fetchCount() 가 deprecated 되었습니다. 이 메서드가 더 이상 권장되지 않는 이유는 공식 문서에서 다음과 같이 설명하고 있습니다.

 

fetchCount() 는 쿼리 결과의 개수를 계산하기 위해서 count 쿼리를 실행합니다. QueryDSL-SQL 에서는 이 작업을 원래 쿼리를 서브 쿼리로 감싸는 방식으로 수행합니다. 예를 들어 `SELECT COUNT(*) FROM (<원래 쿼리>) 와 같은 형태로 쿼리를 작성합니다.

 

그러나 JPA 의 쿼리 언어인 JPQL 에서는 서브 쿼리에서 직접 투영(projection) 하는 것을 허용하지 않습니다. 투영이란 특정 컬럼이나 표현식을 SELECT 구문을 통해 선택하여 결과 집합에 포함 시키는 것을 의미합니다. 예를 들어 SQL 에서는 다음과 같은 쿼리가 가능합니다.

SELECT id, name FROM (SELECT id, name FROM users WHERE active = true) AS active_users;

해당 쿼리에서 active_users 라는 서브 쿼리에서 직접 필드를 투영하고 있습니다. 

 

하지만 JPQL 에서는 이러한 방식이 허용되지 않습니다. JPQL 의 서브쿼리는 오직 WHERE, HAVING 또는 FROM 절 내에서 사용할 수 있으며, 서브 쿼리 내에서 SELECT 문을 통해 특정 필드를 투영하는 것은 불가능합니다. 이로 인해, JPQL에서는 대부분의 SQL 에서 유효한 COUNT(DISTINCT a, b, c) 구문이 유효하지 않게 됩니다.

 

결과적으로 fetchCount() 메서드는 복잡한 쿼리에서 올바르게 동작하지 않으며, 여러 GROUP BY 요소나 HAVING 절을 포함하는 경우 메모리 내에서 결과 수를 방식으로 대체됩니다. 이는 대규모 데이터 셋의 경우 심각한 성능 저하를 일으킬 수 있습니다.

 

요약하자면, fetchCount() 는 복잡한 쿼리에서 성능 문제를 일으킬 수 있으며, 이러한 문제로 인해 deprecated 되었습니다.

 

관련된 공식문서는 해당 링크에 자세하게 확인할 수 있습니다.

 

대안 방법

fetch().size() 로 반환값이 int 형인 동일한 결과를 얻을 수 있습니다.

val count = queryFactory.selectFrom(seller).where(builder).fetch().size()

만약 결과를 Long 타입으로 반환하려면, 다음과 같이 count() 를 명시적으로 사용해 쿼리를 작성할 수 있습니다.

val count = queryFactory.selectFrom(seller.count()).where(builder).fetchOne()

이 경우 실행되는 쿼리는 다음과 같습니다.

select count(seller.id) from seller where (조건)

이러한 방법을 사용하면 fetchCount 가 deprecated 됨에 따라 발생하는 경고를 해결하고, 성능 문제를 방지할 수 있습니다.