spring Data JPA - 페이징 적용

Chooooo·2023년 9월 5일
0

TIL

목록 보기
16/22
post-thumbnail

페이징

😊 spring data JPA에서 페이징을 적용하려면 Pageable 파라미터를 사용하여 메서드 시그니처를 변경하면 된다.

  • Pageable을 통해 페이징된 결과를 얻을 수 있다.
  • 이제 Controller에서 요청을 처리할 때 Pageable을 파라미터로 받아서 사용하면 된다.

Repository

  • 매개변수로 Pageable 타입의 객체를 넘겨주면, 객체의 정보를 읽고 Page 조건을 설정하여 데이터를 가져온다.

Service

page 개수와 size를 매개변수로 받아 pageRequest 객체를 생성하여 repository로 반환한다.
PageRequest는 Pageable을 구현하며, Sort 객체를 매개변수로 받아 정렬조건을 설정할 수 있다.

pageInfo

Service 계층에서 반환한 Page객체에서 제공하는 페이지 개수, 페이지 당 데이터 개수, 총 데이터 수 등의 정보를 이용해 페이지 정보를 담고 있는 PageInfo 객체를 만들어라. (page는 0부터 시작하기 때문에 page -1)

ResponseDto + page

이후 Page 객체의 getContent와 페이지 정보를 함께 반환

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
/**
 * 페이징 관련 통합 Response
 */
public class PageResponse<T> {

    private T content;
    private PageInfo pageInfo;

}
  • 이런 식으로 재사용성 높임

😊 PageRequest 클래스

PageRequest 클래스는 Pageable 인터페이스의 구현체 중 하나

😁 PageRequest 클래스는 생성자로 페이지 번호, 페이지 크기 및 정렬 정보를 생성할 수 있다. 하지만 특이하게도 접근 제어자가 protected로 선언되어 있기 때문에 new를 이용할 수 없어 of() 메서드를 이용해야 한다.

❤️ of(int page, int size) : 페이지 번호와 페이지 크기를 인자로 받아 PageRequest객체를 생성, 이 때 정렬은 지정되지 않는다.

❤️ of(int page, int size, Sort sort) : 페이지 번호, 페이지 크기, 정렬 방향 및 정렬 속성을 인자로 받아 PageRequest 객체를 생성

❤️ of(int page, int size, Direction direction, String... properties) : 페이지 번호, 페이지 크기 및 정렬 관련 정보를 담고 있는 Sort 객체를 인자로 받아 PageRequest 객체를 생성

  • Pageable pageable = PageRequest.of(0,10) : 1페이지에 데이터를 10개씩 가져온다.
  • Page<Member> result = memberRepository.findAll(pageable); : member 테이블의 데이터를 모두 조회. 이 때 pageable의 페이징 설정 적용

Page 정보를 가지고 있는 Pageable 객체

spring boot에서는 JPA를 통해 페이징 처리를 쉽게 활용할 수 있다.

  • controller 단에서 파라미터로 받는 정보들
    page, pageSize, sortBy
  • 여기서는 page, pageSize 정보만 받을꺼야.
Pageable pageable = PageRequest.of(pageNo, pageSize, Sort.by(sortBy).descending);	

Page<TodoEntity> todoPage = todoRepository.findAll(pageable);
// or
Page<TodoResponse> todoDtoPage = todoRepository.findAll(pageable).map(this::mapToDto);

처리 로직 정리 (entity -> Dto)

findAll(pagable); => Page<Entity> -> getContent(); ->List<Entity> 
-> mapping(stream) -> List<Dto>

Response DTO를 만들어줘야 하는 이유!(Page정보 외에도 많이 있음)
❤️ Page<Entity> 의 content 를 따로 List<PostResponse> 로 처리하는 것을 잊으면 안된다!!

front단에 보내주는 Dto 필드에 Paging 내용을 넣어줄 것!
totalElements
totalPages
last

  • last는 꼭 필요하려나 .. ?

😊 페이징 용어 정리

  • page : 현재 페이지 넘버
  • size : 페이지 사이즈
  • offset : 최초 시작점
  • limit : size랑 같음
  • totalElements : 데이터 로우 총 갯수
  • totalPages : 페이지들 총 갯수

구현 소스

  • PageResponse
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PageResponse {

    private List<TodoResponse> content;
    private int pageNo;
    private int pageSize;
    private long totalElements;
    private int totalPages;
    private boolean last;

}
  • 이런 식으로 데이터를 반환해주면 됨 !

service에서 가공

   @Override
    public PageResponse searchAllPaging(int pageNo, int pageSize, String sortBy) {

        // create Pageable instance
        Pageable pageable = PageRequest.of(pageNo, pageSize, Sort.by(sortBy).descending());
        Page<TodoEntity> todoPage = todoRepository.findAll(pageable);
        
		// .map()을 더 추가해서 바로 Page<TodoResponse> 값으로 시작할 수 있어!
		// Page<TodoResponse> todoDtoPage = todoRepository.findAll(pageable).map(this::mapToDto);

        List<TodoEntity> listTodos = todoPage.getContent();

        List<TodoResponse> content = listTodos.stream().map(TodoEntity -> mapToDto(TodoEntity)).collect(Collectors.toList());

        return PageResponse.builder()
                .content(content)	// todoDtoPage.getContent()
                .pageNo(pageNo)
                .pageSize(pageSize)
                .totalElements(todoPage.getTotalElements())
                .totalPages(todoPage.getTotalPages())
                .last(todoPage.isLast())
                .build();

    }

정리

page 개수와 size(page별 데이터 개수)를 매개변수로 받아 PageRequest 객체를 생성하여 repository로 반환.

  • PageRequest는 Pageable을 구현하며, Sort 객체를 매개변수로 받아 정렬조건을 설정할 수도 있다.

😊 즉, Controller 단에서 page와 size를 파라미터로 받기

pageInfo

Service 계층에서 반환한 Page 객체에서 제공하는 페이지 개수, 페이지 당 데이터 개수, 총 데이터 개수 등의 정보를 이용해 페이지 정보를 담고 있는 pageInfo객체를 만들자.

😊 즉, dto + pageInfo 함께 반환

❤️ 실무

실무에서는 Page를 통한 페이징은 잘 하지 않는다.

  • totalCount 때문.. (성능 이슈)

⚽ 그래서 count 쿼리를 분리 !!! - @Query 어노테이션을 통해 가능

@Query(value = "select m from Member m",
 countQuery = "select count(m.username) from Member m")
Page<Member> findMemberAllCountBy(Pageable pageable);
  • 실무에서 중요.
  • 조인이 많다면 countQuery 역시 복잡하게 조인하게 된다.
  • 하지만 countQuery를 따로 뽑아낸다면 특정 타겟에 대해서만 카운트를 셀 수 있기 때문에 성능을 신경 쓸 수 있다.

또한 절대로 페이징 처리한 데이터를 그대로 반환해서는 안돼!! 엔티티는 절대 노출하면 안되고 DTO로 변환해서 반환할 것.

DTO 변환 쉽게 하는 방법

Page<Member> page = memberRepository.findByAge(10, pageRequest);
        Page<MemberDto> toMap = page.map(member -> new MemberDto(member.getId(), member.getUsername(), "null"));

이런 식으로 DTO로 매핑해서(변환해서) 해줘야함!!

profile
back-end, 지속 성장 가능한 개발자를 향하여

0개의 댓글