동적쿼리

Yeeun·2025년 4월 24일
0

SpringBoot

목록 보기
19/46

좋아요! 지금 작성한 코드는 QueryDSL을 활용한 동적 쿼리 예제예요. 각 부분을 천천히, 아주 자세하게 설명드릴게요. 😌


📌 전체 흐름 요약

이 코드는 사용자의 검색 조건(searchCondition)검색어(searchKeyword) 에 따라 조건문을 동적으로 생성하고, 해당 조건에 맞는 Board 엔티티들을 페이징하여 조회하는 동적 쿼리 테스트입니다.


1. DynamicBoardRepository

public interface DynamicBoardRepository extends JpaRepository<Board, Long>, QuerydslPredicateExecutor<Board>
  • JpaRepository<Board, Long>
    → 일반적인 CRUD 메소드들 사용 가능 (save, findById 등)

  • QuerydslPredicateExecutor<Board>
    BooleanBuilder 같은 QueryDSL 조건을 기반으로 동적 쿼리를 실행할 수 있게 함
    → 즉, findAll(Predicate, Pageable) 같은 메소드 사용 가능


2. @Test 메소드 전체 설명

@Test
public void testDynamicQuery() {

테스트 실행용 메소드입니다.


🔹 String searchCondition = "TITLE";

사용자가 제목으로 검색할지, 내용으로 검색할지를 의미합니다.
예: "TITLE" 혹은 "CONTENT"


🔹 String searchKeyword = "title10";

검색어를 의미합니다. 지금은 "title10"을 기준으로 검색합니다.


🔹 BooleanBuilder builder = new BooleanBuilder();

이게 핵심입니다. QueryDSL에서 제공하는 조건 조합 객체입니다.

  • 여러 조건을 .and(), .or() 등으로 연결해서 쿼리 조건을 동적으로 누적할 수 있어요.
  • 즉, WHERE 절을 동적으로 만들 수 있게 해주는 빌더입니다.

🔹 QBoard qboard = QBoard.board;

이것도 매우 중요해요.

  • QBoardBoard 엔티티로부터 QueryDSL이 자동으로 생성한 클래스입니다.
  • QBoard.board.titleBoard 엔티티의 title 필드에 해당
  • QBoard.board.contentBoardcontent 필드

즉, 이 객체를 통해 타입 안정성 있게 엔티티의 필드에 접근할 수 있어요.


🔹 조건 분기 (동적 쿼리 핵심!)

if (searchCondition.equals("TITLE")) {
    builder.and(qboard.title.contains(searchKeyword));
} else if (searchCondition.equals("CONTENT")) {
    builder.and(qboard.content.like("%" + searchKeyword + "%"));
}
  • 사용자가 "TITLE"을 검색 조건으로 지정하면 → title 컬럼에 대해 LIKE '%title10%'
  • "CONTENT"이면 → content 컬럼에서 LIKE '%title10%'

이렇게 조건을 코드 실행 시점에 구성할 수 있습니다.


🔹 페이징 처리

Pageable paging = PageRequest.of(0, 5);
  • 첫 번째 페이지(0번 인덱스)
  • 페이지당 5개씩 조회하겠다는 뜻

🔹 최종 쿼리 실행

Page<Board> boardList = boardRepo.findAll(builder, paging);
  • QuerydslPredicateExecutor 덕분에, BooleanBuilder로 만든 조건을 넘겨서 페이징된 결과를 받을 수 있어요.
  • 이건 SQL로 치면:
SELECT * FROM board 
WHERE title LIKE '%title10%' 
LIMIT 5 OFFSET 0;

같은 동작을 해주는 셈입니다.


✅ 요약 정리

구성 요소설명
QBoardBoard 엔티티를 QueryDSL이 자동 생성한 클래스, 필드 접근용
BooleanBuilder동적 WHERE 조건을 조립할 수 있는 쿼리 조건 빌더
QuerydslPredicateExecutorfindAll(BooleanBuilder) 사용 가능하게 해주는 인터페이스
PageRequest페이지 번호와 크기를 정해주는 페이징 객체
findAll(builder, paging)동적 조건 + 페이징 결과를 조회하는 메소드


✅ 핵심 정리

🔸 BooleanBuilder 로 동적 WHERE 조건 생성

if (searchCondition.equals("TITLE")) {
	builder.and(qboard.title.contains(searchKeyword));
} else if (searchCondition.equals("CONTENT")) {
	builder.and(qboard.content.like("%" + searchKeyword + "%"));
}
  • searchCondition 에 따라 조건 분기
  • BooleanBuilder는 쿼리 실행 시점에 조건을 동적으로 조합

🔸 페이징 객체 생성

Pageable paging = PageRequest.of(0, 5);
  • 0번 페이지, 5개씩 가져오기

🔸 결과 조회

Page<Board> boardList = boardRepo.findAll(builder, paging);
  • 동적 조건 + 페이징이 함께 적용된 결과

🔸 getContent()로 실제 결과 꺼내오기

List<Board> list = boardList.getContent();
  • Page<Board> 는 단순 리스트가 아니라 전체 페이지 정보도 포함되어 있음
  • getContent()로 실제 List<Board> 데이터 추출

🔸 for문으로 출력

for(Board board:list) {
	System.out.println(board);
}
  • 필요한 출력 로직 작성 가능

☑️ 정리하면

이 코드는 QueryDSL + BooleanBuilder + Pageable을 조합하여
실행 시점에 조건이 유동적인 동적 검색 쿼리를 페이징 처리와 함께 구현한 예제입니다.


맞아요! 아주 잘 이해하셨어요 😊
이번 코드는 QueryDSL을 활용한 동적 쿼리 + 페이징 처리를 아주 깔끔하게 잘 구현한 예제예요.
마무리로 다시 정리해드릴게요:


✅ 핵심 정리

🔸 BooleanBuilder 로 동적 WHERE 조건 생성

if (searchCondition.equals("TITLE")) {
	builder.and(qboard.title.contains(searchKeyword));
} else if (searchCondition.equals("CONTENT")) {
	builder.and(qboard.content.like("%" + searchKeyword + "%"));
}
  • searchCondition 에 따라 조건 분기
  • BooleanBuilder는 쿼리 실행 시점에 조건을 동적으로 조합

🔸 페이징 객체 생성

Pageable paging = PageRequest.of(0, 5);
  • 0번 페이지, 5개씩 가져오기

🔸 결과 조회

Page<Board> boardList = boardRepo.findAll(builder, paging);
  • 동적 조건 + 페이징이 함께 적용된 결과

🔸 getContent()로 실제 결과 꺼내오기

List<Board> list = boardList.getContent();
  • Page<Board> 는 단순 리스트가 아니라 전체 페이지 정보도 포함되어 있음
  • getContent()로 실제 List<Board> 데이터 추출

🔸 for문으로 출력

for(Board board:list) {
	System.out.println(board);
}
  • 필요한 출력 로직 작성 가능

☑️ 정리하면

이 코드는 QueryDSL + BooleanBuilder + Pageable을 조합하여
실행 시점에 조건이 유동적인 동적 검색 쿼리를 페이징 처리와 함께 구현한 예제입니다.



🧩 페이징(Paging)이란?

데이터가 많을 때 한 번에 다 불러오지 않고, 조금씩 나눠서 가져오는 방식이에요.
→ 웹사이트에서 흔히 보는 "다음 페이지", "1 2 3 4" 이런 거 전부 페이징이에요.


PageRequest.of(0, 5) 의미

PageRequest.of(0, 5)

이건 0번째 페이지에서 5개 가져와라는 뜻이에요.

✳️ 구조는 이렇게 생겼어요:

PageRequest.of(페이지 번호, 한 페이지에 가져올 데이터 개수)
표현식의미
PageRequest.of(0, 5)1페이지를 뜻함 (0번 인덱스가 첫 페이지)
PageRequest.of(1, 5)2페이지
PageRequest.of(2, 5)3페이지
PageRequest.of(0, 100)1페이지에서 100개씩 가져오겠다는 뜻

📌 예시로 이해해 볼게요:

데이터가 총 12개 있다고 가정하면:

  • PageRequest.of(0, 5) → 1~5번 데이터
  • PageRequest.of(1, 5) → 6~10번 데이터
  • PageRequest.of(2, 5) → 11~12번 데이터

즉, 페이지 번호는 가져오는 "시작 위치"를 의미하고
두 번째 인자는 한 페이지에 보여줄 개수를 말해요.


🧠 "그럼 페이지 나눠서 뭐하죠?"

  • 한 번에 1000개 가져오면 속도 느려짐
  • 특히 사용자에게 보여줄 때 10개씩, 20개씩 나눠서 보여줘야 UX가 좋아짐
  • 검색 결과도 1페이지, 2페이지 나누어 보여야 사용자가 보기 편해짐

그래서 Spring Data JPA, QueryDSL, MyBatis 전부 페이징 기능을 갖고 있어요.


🎯 요약

  • PageRequest.of(0, 5)1페이지, 5개씩
  • PageRequest.of(1, 5)2페이지, 다음 5개
  • 페이징은 속도와 사용자 편의성을 위한 중요한 기능
  • 실제로는 pageInfo.getTotalPages(), getContent(), hasNext() 등을 통해 웹 UI와 연동돼요

0개의 댓글