01 Paging
01 Paging
1. Note
- Pageable = 요청 / Page
= 응답 / QueryDSL = 쿼리 도구 - Pageable과 Paging은 그냥 warp에 가깝구나
- 조회할때 레파지토리에서 설정해서 리턴만해 주면 그대로 나옴!
2. Paging & Pageable
1. Paging
- 데이터를 N개 나눠보는 것이 페이징
- 예시
1 2 3 4
전체 100개 데이터를 10개씩 나눠서 보여주는 것 1페이지: 1~10번 2페이지: 11~20번 ...
2. Pageable
- 몇 페이지, 몇 개씩 줘” 라는 요청 정보
- 흐름
1 2 3 4 5 6 7 8 9
// 클라이언트가 이렇게 요청 GET /members?page=0&size=10&sort=username,asc // Spring이 자동으로 Pageable로 변환해줌 Pageable pageable = PageRequest.of(0, 10, Sort.by("username")); pageable.getPageNumber() // 0 (몇 번째 페이지) pageable.getPageSize() // 10 (몇 개씩) pageable.getOffset() // 0 (몇 번째부터, SQL offset) - 데이터구분
- 스프링에서는 내부적으로 알아서 처리함. Request에 정보가 더 있어도 무시
- Request에서 필요한 정보(쿼리파라미터나 바디 둘다 무관)
1 2 3 4 5
// 클라이언트가 넘겨주는 것 pageable.getPageNumber() // 몇 번째 페이지 pageable.getPageSize() // 몇 개씩 pageable.getSort() // 정렬 pageable.getOffset() // (page * size) 자동 계산
- Response에 담겨있는 정보
1 2 3 4 5 6 7 8
page.getContent() // 실제 데이터 page.getTotalElements() // 전체 데이터 수 page.getTotalPages() // 전체 페이지 수 page.getNumber() // 현재 페이지 page.getSize() // 페이지 크기 page.isFirst() // 첫 페이지 여부 page.isLast() // 마지막 페이지 여부 page.hasNext() // 다음 페이지 여부
- Response에 담겨있는 정보
3. Page
- 요청한 데이터 + 페이지 메타정보를 담은 응답
- 소스
1 2 3 4 5 6 7
Page<Member> page = ...; page.getContent() // 실제 데이터 List page.getTotalElements() // 전체 데이터 수 (100개) page.getTotalPages() // 전체 페이지 수 (10페이지) page.getNumber() // 현재 페이지 (0) page.isLast() // 마지막 페이지 여부
4. QueryDSL & Paging
1
2
3
// Pageable을 받아서 QueryDSL 쿼리에 적용하는 것뿐!
.offset(pageable.getOffset()) // Pageable에서 꺼내서
.limit(pageable.getPageSize()) // QueryDSL에 넣어주는 것
3. Spring - Paging
1. Cotnroller
- 컨트롤러 내부에서 처리
1 2 3 4 5 6 7 8
@GetMapping("/members") public MemberPageResponse getMembers( @RequestParam String username, Pageable pageable // Spring이 자동으로 변환해줌 ) { Page<Member> page = memberService.searchPage(username, pageable); return new MemberPageResponse(page); } - Response
- Page
타입으로 그대로 리턴할 경우 모든 Paging 데이터가 날라감. - 별도 ResponseDTO에 생성자로 page
타입을 받고, 원하는것만 필드에 담으면 필요한것만! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
@Getter public class MemberPageResponse { // Page에서 필요한 것만 꺼내서 private List<MemberDto> content; // 실제 데이터 private long totalElements; // 전체 데이터 수 private int totalPages; // 전체 페이지 수 private int pageNumber; // 현재 페이지 private boolean isLast; // 마지막 페이지 여부 public MemberPageResponse(Page<Member> page) { this.content = page.getContent() .stream() .map(MemberDto::new) // Entity → DTO 변환 .collect(Collectors.toList()); this.totalElements = page.getTotalElements(); this.totalPages = page.getTotalPages(); this.pageNumber = page.getNumber(); this.isLast = page.isLast(); } }
- Page
2. Repository
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Repository
public class MemberRepository {
private final JPAQueryFactory queryFactory;
public Page<Member> searchPage(String username, Pageable pageable) {
// 컨텐츠 쿼리
List<Member> content = queryFactory
.selectFrom(member)
.where(member.username.eq(username))
.offset(pageable.getOffset()) // 시작위치
.limit(pageable.getPageSize()) // 가져올 개수
.fetch();
// 카운트 쿼리
JPAQuery<Long> countQuery = queryFactory
.select(member.count())
.from(member)
.where(member.username.eq(username));
//PageableExecutionUtils는 의존성추가하면 생김.
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
}
}
4. Paging 분해
1. 특정 페이지의 특정 인덱스 데이터 꺼내기
1
2
3
4
5
6
7
8
9
10
Page<Member> page = memberService.searchPage(pageable);
// 1. content 리스트로 꺼내기
List<Member> content = page.getContent();
// 2. 원하는 인덱스 접근
// 인덱스는 현재 페이지 기준
Member first = content.get(0); // 첫 번째
Member second = content.get(1); // 두 번째
Member last = content.get(content.size() - 1); // 마지막
2. 페이징 정보 + 추가 데이터 함께 보내기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Getter
public class MemberPageResponse {
// 페이징 정보
private List<MemberDto> content;
private long totalElements;
private int totalPages;
private int pageNumber;
private boolean isLast;
// 👇 추가로 함께 보낼 데이터
private MemberSummary summary; // 특정 타입
private long totalOrderCount; // 추가 필드
public MemberPageResponse(Page<Member> page, MemberSummary summary, long totalOrderCount) {
// Page 풀어서 담기
this.content = page.getContent()
.stream()
.map(MemberDto::new)
.collect(Collectors.toList());
this.totalElements = page.getTotalElements();
this.totalPages = page.getTotalPages();
this.pageNumber = page.getNumber();
this.isLast = page.isLast();
// 추가 데이터 담기
// 별도 편의성 메소드로 진행함.
this.summary = summary;
this.totalOrderCount = totalOrderCount;
}
}
This post is licensed under CC BY 4.0 by the author.