TIL/[TIL]

[TIL]JPQL의 Projection, Paging, Group Function 정리

namerong 2026. 6. 15. 16:47

1. 학습 주제

이번에는 JPA에서 엔티티를 단순 조회하는 수준을 넘어서,
JPQL로 어떤 값만 뽑아오는 방식페이지 단위 조회집계 함수와 그룹화를 다루는 흐름까지 이어졌다.

즉 핵심은 다음 세 가지였다.

  • Projection: 무엇을 조회할 것인가
  • Paging: 몇 개를, 어디부터 가져올 것인가
  • Group Function: 여러 행을 묶어서 계산할 것인가

이 범위는 “JPQL도 결국 조회 언어인데, SQL처럼 다양한 조회 표현이 가능하다”는 점을 이해하는 데 의미가 있었다.


2. JPQL은 다시 무엇을 기준으로 쓰는가

JPQL은 SQL처럼 보이지만 기준이 다르다.

SQL은:

  • 테이블 이름
  • 컬럼 이름

을 기준으로 쓴다.

JPQL은:

  • 엔티티 이름
  • 엔티티 필드 이름

을 기준으로 쓴다.

예를 들어 SQL이라면 tbl_menu, menu_name을 쓰겠지만,
JPQL은 Section03Menu, menuName처럼 엔티티와 필드를 기준으로 쓴다.

즉 JPQL은
데이터베이스용 SQL이 아니라 객체용 조회 언어라고 이해해야 한다.


3. Projection이란 무엇인가

Projection은
조회 결과로 무엇을 가져올지 선택하는 것이다.

즉 “조회는 한다”는 점은 같지만,
그 결과를 어떤 형태로 받을지에 따라 projection 종류가 달라진다.

이번에 본 것은 크게 세 가지였다.

  • 엔티티 프로젝션
  • 스칼라 프로젝션
  • DTO 프로젝션

이 차이를 구분하는 것이 핵심이다.


4. 엔티티 프로젝션

엔티티 프로젝션은
엔티티 자체를 조회하는 방식이다.

예를 들어:

SELECT m FROM Section03Menu m

처럼 쓰면, 결과는 Menu 엔티티 객체 리스트가 된다.

이 방식의 특징은:

  • 조회 결과가 엔티티다
  • 영속성 컨텍스트 관리 대상이 될 수 있다
  • 조회 후 필드 변경 시 변경 감지와 연결될 수 있다

즉 엔티티 프로젝션은
값만 뽑는 게 아니라, JPA가 관리하는 객체를 가져오는 방식이다.


5. 엔티티 프로젝션과 변경 감지

이번 코드에서 서비스 계층 메서드에 @Transactional이 붙어 있고,
조회한 메뉴의 이름을 바꾸는 흐름이 있었다.

이게 중요한 이유는 엔티티 프로젝션으로 가져온 결과가
영속성 컨텍스트에서 관리되는 엔티티이기 때문이다.

즉:

  1. 엔티티 조회
  2. 영속 상태 엔티티 반환
  3. 필드 값 변경
  4. 트랜잭션 commit 시점에 변경 감지
  5. DB 반영 가능

이 흐름이 성립한다.

그래서 엔티티 프로젝션은
단순 조회 결과가 아니라
상태가 관리되는 객체 조회로 이해해야 한다.


6. 스칼라 프로젝션

스칼라 프로젝션은
엔티티 전체가 아니라 특정 값들만 조회하는 방식이다.

예를 들어:

SELECT c.categoryCode, c.categoryName FROM Section03Category c

처럼 조회하면:

  • 카테고리 코드
  • 카테고리 이름

두 컬럼 값만 가져오게 된다.

이 경우 반환 타입은 보통 Object[]가 된다.
왜냐하면 여러 값을 한 줄씩 묶어서 받아야 하기 때문이다.

즉 스칼라 프로젝션은
엔티티가 아니라 컬럼값 중심 조회라고 보면 된다.


7. 스칼라 프로젝션의 특징

스칼라 프로젝션은 필요한 값만 가져올 수 있어서 가볍지만,
단점도 있다.

  • 반환값이 Object[] 형태라 가독성이 떨어질 수 있다
  • 인덱스로 꺼내야 해서 실수 가능성이 있다
  • 의미가 바로 드러나지 않는다

즉 간단한 값 몇 개를 빨리 조회할 때는 쓸 수 있지만,
구조가 복잡해질수록 DTO 프로젝션 쪽이 더 읽기 좋아진다.


8. DTO 프로젝션과 new 문법

DTO 프로젝션은
조회 결과를 바로 DTO 객체로 만들어서 받는 방식이다.

예를 들어:

SELECT new ...CategoryInfo(c.categoryCode, c.categoryName)
FROM Section03Category c

이렇게 쓰면 JPQL 조회 결과가 CategoryInfo 객체 리스트로 바로 반환된다.

즉:

  • 스칼라 프로젝션은 Object[]
  • DTO 프로젝션은 내가 원하는 DTO 타입

으로 받을 수 있다.

이 방식의 장점은 다음과 같다.

  • 의미가 분명하다
  • 타입이 안전하다
  • 코드 가독성이 좋다
  • 결과를 바로 전달하기 좋다

즉 DTO 프로젝션은
조회 결과를 목적에 맞는 응답용 객체로 바로 만드는 방식이라고 보면 된다.


9. 엔티티 프로젝션 vs DTO 프로젝션

이 둘은 꽤 다르다.

엔티티 프로젝션

  • 엔티티를 조회
  • 영속성 컨텍스트 관리 대상
  • 변경 감지 가능

DTO 프로젝션

  • DTO를 조회 결과로 생성
  • 영속성 컨텍스트 관리 대상 아님
  • 조회 전용 응답에 적합

즉 “조회 후 수정까지 연결되는가”가 큰 차이점이다.

실무에서는 보통:

  • 수정 가능성이 있는 내부 처리 -> 엔티티 프로젝션
  • 화면 응답, 목록 출력 -> DTO 프로젝션

으로 많이 나뉜다.


10. Paging이란 무엇인가

Paging은
조회 결과를 한 번에 전부 가져오지 않고, 일부 범위만 잘라서 가져오는 것이다.

즉 게시판, 상품 목록, 회원 목록처럼 데이터가 많을 때
필요한 구간만 가져오는 방식이다.

JPQL에서는 setFirstResult()와 setMaxResults()로 이를 처리할 수 있다.


11. setFirstResult()와 setMaxResults()

이 둘의 역할은 다음과 같다.

setFirstResult(offset)

조회 시작 위치를 정한다.

setMaxResults(limit)

가져올 데이터 개수를 정한다.

즉 조합하면:

  • 몇 번째부터
  • 몇 개를 가져올지

정할 수 있다.

예를 들어:

  • offset = 10
  • limit = 5

라면 11번째 데이터부터 5개를 가져오는 감각으로 이해하면 된다.

즉 JPQL의 페이징은
SQL의 LIMIT/OFFSET과 비슷한 역할을 객체 조회 문법에서 제공하는 것이다.


12. 왜 Paging이 중요한가

전체 데이터를 매번 다 가져오면 비효율적이다.

예:

  • 메뉴 10만 개
  • 게시글 100만 개
  • 주문 내역 수십만 개

이걸 한 번에 다 메모리에 올리면:

  • 조회 속도 저하
  • 메모리 낭비
  • 화면 렌더링 비효율

문제가 생긴다.

그래서 페이징은
실제 서비스에서 거의 기본적으로 필요한 조회 전략이다.

즉 “조회는 되는데 느리다” 문제를 피하려면
필요한 만큼만 가져오는 사고가 중요하다.


13. Group Function이란 무엇인가

Group Function은 여러 행을 대상으로 계산하는 함수들이다.

대표적으로:

  • COUNT
  • SUM
  • AVG
  • MAX
  • MIN

등이 있다.

즉 “한 행을 읽는 것”이 아니라,
여러 행을 묶어서 계산 결과를 얻는 조회라고 보면 된다.


14. COUNT

예제에서는 특정 카테고리에 속한 메뉴 개수를 구했다.

예:

SELECT COUNT(m.menuCode)
FROM Section05Menu m
WHERE m.categoryCode = :categoryCode

즉 조건에 맞는 메뉴가 몇 개인지 세는 것이다.

이건 실무에서도 매우 자주 쓰인다.

예:

  • 특정 카테고리 상품 수
  • 특정 사용자의 게시글 수
  • 특정 상태의 주문 수

즉 COUNT는
개수를 세는 집계 함수다.


15. SUM과 결과 없음

SUM도 예제에 들어 있었다.

예를 들어 특정 카테고리 메뉴 가격 합계를 구하는 방식이다.

여기서 중요한 포인트는 COUNT와 다른 집계 함수의 차이이다.

  • COUNT는 조건에 맞는 행이 없어도 보통 0을 반환한다
  • SUM, AVG 같은 함수는 조건에 맞는 행이 없으면 null이 나올 수 있다

즉 집계 함수는 같아 보여도
“결과 없음” 상황에서 반환값이 다를 수 있다는 점을 알아야 한다.

이건 나중에 null 처리할 때 중요하다.


16. GROUP BY

GROUP BY는
같은 값끼리 묶어서 그룹별로 계산하는 방식이다.

예를 들어 카테고리별 메뉴 가격 합계를 보고 싶다면:

  • 카테고리 코드를 기준으로 묶고
  • 각 그룹의 SUM(menuPrice)를 계산

하는 식이다.

즉 GROUP BY는
집계의 기준 축을 만드는 역할을 한다.


17. HAVING

HAVING은 그룹화된 결과에 조건을 거는 것이다.

예를 들어:

  • 카테고리별 합계 금액을 계산하고
  • 그 합계가 50000 이상인 카테고리만 보고 싶다

면 HAVING을 쓴다.

즉:

  • WHERE는 그룹화 전 개별 행 필터링
  • HAVING은 그룹화 후 집계 결과 필터링

이다.

이 구분은 SQL과 JPQL 모두에서 매우 중요하다.


18. Object[]가 다시 나오는 이유

그룹 함수와 GROUP BY 결과는 보통 한 줄에 여러 값이 같이 나온다.

예:

  • 카테고리 코드
  • 합계 금액

그래서 결과 타입이 보통 Object[]가 된다.

이건 스칼라 프로젝션 때와 비슷하다.

즉 여러 값을 한 번에 조회하면
엔티티 하나가 아니라 Object[] 형태가 자주 등장한다는 점을 기억하면 좋다.


19. Projection, Paging, Group Function을 한 흐름으로 보면

이 세 개는 따로 보이지만 사실 모두 “조회 최적화”와 연결된다.

Projection

무엇을 가져올지 정한다.

Paging

얼마나 가져올지 정한다.

Group Function

가져온 데이터를 어떻게 계산할지 정한다.

즉 JPQL 조회를 설계할 때는 단순히:

  • 데이터 조회

가 아니라

  • 어떤 형태로
  • 몇 개를
  • 어떤 기준으로 묶어서
  • 어떤 계산값으로

가져올지까지 생각해야 한다.


20. 언제 엔티티를 그대로 조회하고, 언제 DTO로 뽑는가

복습 포인트로 정리하면 이렇다.

엔티티 그대로 조회

  • 이후 수정 가능성이 있다
  • 영속성 컨텍스트 관리가 필요하다
  • 도메인 로직 안에서 사용할 목적이다

DTO로 조회

  • 화면 응답용이다
  • 필요한 값만 가볍게 가져오고 싶다
  • 조회 전용 결과가 목적이다

즉 “조회 결과가 관리 대상이어야 하는가”를 기준으로 보면 구분이 쉬워진다.


21. 학습 흐름 정리

21.1 Projection

  • 엔티티 프로젝션: 엔티티 자체 조회
  • 스칼라 프로젝션: 특정 값들만 조회
  • DTO 프로젝션: new 문법으로 DTO 생성

21.2 Paging

  • setFirstResult()로 시작 위치 지정
  • setMaxResults()로 개수 제한
  • 대량 데이터를 나눠서 조회하는 방식 이해

21.3 Group Function

  • COUNT, SUM 같은 집계 함수 사용
  • GROUP BY로 그룹화
  • HAVING으로 집계 결과 필터링
  • 결과 없음일 때 COUNT와 다른 함수의 차이 이해

22. 핵심 정리

  1. JPQL은 엔티티와 필드명을 기준으로 조회하는 객체지향 쿼리 언어이다.
  2. Projection은 조회 결과로 무엇을 가져올지 결정하는 방식이다.
  3. 엔티티 프로젝션은 엔티티 자체를 조회하며 영속성 컨텍스트 관리 대상이 될 수 있다.
  4. 스칼라 프로젝션은 특정 컬럼값만 조회하며 보통 Object[]로 받는다.
  5. DTO 프로젝션은 new 문법으로 조회 결과를 원하는 DTO 객체로 바로 만들 수 있다.
  6. 페이징은 많은 데이터를 한 번에 다 가져오지 않고 필요한 범위만 조회하는 방식이다.
  7. setFirstResult()는 시작 위치, setMaxResults()는 조회 개수를 지정한다.
  8. 집계 함수는 여러 행을 대상으로 계산하며 COUNT, SUM, AVG 등이 있다.
  9. COUNT는 결과가 없어도 0이 나올 수 있지만, 다른 집계 함수는 null이 될 수 있다.
  10. GROUP BY는 그룹 단위 계산을, HAVING은 그룹 결과 조건 필터링을 담당한다.
  11. 조회 후 수정까지 이어질 가능성이 있으면 엔티티 프로젝션, 조회 전용 응답이면 DTO 프로젝션이 더 적합하다.

복습할 때는 이 세 문장만 먼저 떠올리면 흐름이 빨리 잡힌다.

  • Projection은 무엇을 가져올지 정한다.
  • Paging은 얼마나 가져올지 정한다.
  • Group Function은 가져온 데이터를 어떻게 계산할지 정한다.