티스토리 뷰

Spring

Springboot JPA Pagination

따강아지 2023. 8. 23. 02:33

실제 운영 환경에서는 Pageable 속성을 지정하여 메모리 아웃을 피해야 합니다. 

1.  도메인 클래스 생성

@Setter
@Getter
@Table(name = "TB_DEMO")
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Builder
public class DemoEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private int id;    
    private String title;
    private String content;
}

2. Repository 생성

페이징 및 정렬을 하기 위해서는 ListPagingAndSortingRepository 또는 PagingAndSortingRepository를 사용 합니다.

  • 확장받을 수 있는 Repository는 다음과 같습니다.
    • CrudRepository  :  CRUD 기능을 제공을 전체 기능을 사용하지 않을 때 사용 
    • PagingAndSortingRepository :  페이지 및 정렬을 수행하는 메서드를 제공 
    • ListPagingAndSortingRepository : PagingAndSortingRepository 를 상속받아List를 반환 하는 메서드 젝공
      - Page<T>, Slice<T>에 있는 페이징 관련 정보 없이 데이터 만 받을 수 있습니다.
      - 전체 페이지(getTotalPages()), 전체 갯수(getTotalElements())를 제공 받지 못 합니다.
    • JpaRepository는 지속성 컨텍스트 플러시 및 일괄처리의 레코드 삭제와 같은 JPA 관련 메소드를 제공 
      - CrudRepository  PagingAndSortingRepository의 전체 API가 포함 되어 있습니다.

JpaRespositoty 상속 구조

다음 예제는 JpaRepository를 상속 받아 List와 Page의 차이점에서 알아보고자 합니다.

@Repository
public interface TourlistRepository extends  JpaRepository<TourlistEntity, String > {
  List<TourlistEntity> findByTitleContains(String title, Pageable pageable);
  Page<TourlistEntity> findTourlistEntitiesByTitleContains(String title, Pageable pageable);
}

 

 3. 서비스 생성 

@RequiredArgsConstructor
@Service
public class TourListService {

  private final TourlistRepository tourlistRepository;

  /**
   * ListPagingAndSortingRepository를 사용해서 Repository에서 List로 반환 값 사용
   * @param title
   * @param pageable
   * @return
   */
  public List<TourlistDTO> getTourList(String title, Pageable pageable) {
     List<TourlistEntity> tourlistEntities = tourlistRepository.findByTitleContains(title, pageable);
     return WorkMapper.INSTANCE.toTourlistDTOs(tourlistEntities);
  }

  /**
   * PagingAndSortingRepository를 사용해서 Repository에서 Page로 반환 값 사용
   * Page<T>, Slice<T> 페이징 관련 속성 사용 
   * @param title
   * @param pageable
   * @return
   */
  public ResponsePageDTO getTourListPage(String title, Pageable pageable) {
    Page<TourlistEntity> tourlistEntityPage = tourlistRepository.findTourlistEntitiesByTitleContains(title, pageable);
    return ResponsePageDTO.setResponsePageDTO(tourlistEntityPage);
  }
}

ResponsePageDTO 객체는 전체 페이지 등 페이지 정보 및 데이터를 담고 있는 객체로 ResponsePageDTO.setResponsePageDTO를 통해서 생성할 됩니다. setResponsePageDTO의 소스는 다음과 같습니다.

@Builder
public class ResponsePageDTO {
  public PageDTO page;
  public Object content;

  public static ResponsePageDTO setResponsePageDTO(Page<?> page) {

    return ResponsePageDTO.builder()
            .page(PageDTO.builder()
                    .total( page.getTotalElements())                // 조회된 전체 갯수
                    .pageCount(page.getTotalPages())                // 조회된 전체 페이지 갯수
                    .pageNumber(page.getPageable().getPageNumber()) // 현재 페이지
                    .pageSize(page.getPageable().getPageSize())     // 페이지에 있는 컨텐츠 갯수
                    .build())
            .content(page.getContent())                             // 컨텐츠
            .build();

  }
}

4. Controller 생성

@RestController
@RequestMapping("/tourlist")
public class TourlistController {

  private final TourListService tourListService;

  @GetMapping("/{title}")
  public ResponseEntity getTourlist(@PathVariable String title,
                                    Pageable pageable) {
    return ResponseUtils.completed(tourListService.getTourList(title, pageable));
  }

  @GetMapping("/page/{title}")
  public ResponseEntity getTourlistPage(@PathVariable String title,
                                        @PageableDefault(page=0, size=10)Pageable pageable) {
    return ResponseUtils.completed(tourListService.getTourListPage(title, pageable));
  }
}

4-1. RestApi : http://localhost:8080/tourlist/{title}?page=0&size=2

  • Method : get
  • PathVariable :
    • title : 검색 조건
    • page : 현재페이지
    • size : 페이지당 개수
  • 기능 : 조건에 맞는 것을 처음부터 페이지당 개수만큼 조회하는 것으로 내부적으로는 PageRequest.of(page, pageSize, sort)를 사용합니다.
    • 예제는 sort 조건이 없는 것입니다, page는 0부터 시작하는 것에 주의를 해야 합니다.
    • page : 0, size : 10 이면 처음부터 10개를 의미하고
    • page : 1, size : 처음 + 1부터 10개를 의미합니다. 
  • 결과 :
{
    "contentid": "127480",
    "title": "가거도(소흑산도)",
    ....
    "overview": "가거도는 중국의 새벽닭 울음소리가 들릴만큼 중국땅과 가깝다는 ...."
    ....
},
{
    "contentid": "129139",
    "title": "신안 가거도 등대",
    ....
    "overview": "가거도(소흑산도)등대는 중국 상하 ....",
    ....
}

4-2. RestApi : http://localhost:8080/tourlist/page/{title}?page=0&size=2

  • Method : get
  • PathVariable :
    • title : 검색 조건
    • page : 현재페이지
    • size : 페이지당 개수
  • 기능 : 조건에 맞는 것을 처음부터 페이지당 개수만큼 조회하는 것으로 내부적으로는 PageRequest.of(page, pageSize, sort)를 사용하며 페이지 정보를 포함해서 반환합니다.
"page": {
            "total": 382,
            "pageNumber": 0,
            "pageCount": 191,
            "pageSize": 2
        },
        "content": [
            {
                "contentid": "127480",
                "title": "가거도(소흑산도)",
                .....
                "overview": "가거도는 중국의 새벽닭 울음소리가 ...",
                ....
            },
            {
                "contentid": "126273",
                "title": "가계해변",
                ....
                "overview": "바닷물이 갈라지는 현대판 모세의 ...",
                ....
            }
        ]
    }

** page, size를 입력하지 않고  "http://localhost:8080/tourlist/page/가" 로 호출 이면 @PageableDefault(page=0, size=10) 로 설정 한 값을 설정이 됩니다.

** Pageable은 org.springframework.data.web 패키지의 안에 정의된 ArgumentResolver인 PageableHandlerMethodArgumentResolver에 정의되어 있습니다.

public class PageableHandlerMethodArgumentResolver extends PageableHandlerMethodArgumentResolverSupport
		implements PageableArgumentResolver {
        ...
}

5. Pageable의 속성

Pageable의 기본 속성을 정의하는 방법은 다음과 같이 3 가지 방법이 있습니다.
 PageRequest.of(page, pageSize, sort) -> PageRequest(int page, int size, Sort sort)

5-1. applicatpn.yml 에 설정하는 방법 

@ConfigurationProperties("spring.data.web")
public class SpringDataWebProperties {
	... 
}

에 정의되어 있으며 applicaion.yml에 다음과 같이 정의합니다.

  data:
    web:
      pageable:
        default-page-size: 10
        max-page-size: 30

5-2. bean 등록하는 방법

@Configuration
public class CustomPageableConfiguration {
    @Bean
    public PageableHandlerMethodArgumentResolverCustomizer customize() {
        return p -> p.setFallbackPageable(PageRequest.of(0, 50));
    }
}

spring-boot-starter-web의 SpringDataWebConfiguration Class에 정의되어 있으면 위 소스와 같이 별도로 구성 개발 하지 않으면 SpringDataWebAutoConfiguration Class에 정의된 PageableHandlerMethodArgumentResolverCustomizer  메서드에 의해서 기본 설정이 됩니다, ( applicaion.yml  읽음 )

기본 속성 정의는 @PageableDefault ->  CustomPageableConfiguration bean -> applicaion.yml ->등록 순서로 적용이 됩니다.

6. Sort 

다음과 같이 정렬할 수 있습니다.

Sort sort1 = Sort.by("name").descending();   // 이름 기준 내림차순
Sort sort2 = Sort.by("name").ascending();    // 이름 기준 오름차순


Pageable sortedByName = 
  PageRequest.of(0, 3, Sort.by("name"));

Pageable sortedByPriceDesc = 
  PageRequest.of(0, 3, Sort.by("name").descending());

// 이름과 나이로 정렬
Pageable sortedByPriceDescNameAsc = 
  PageRequest.of(0, 5, Sort.by("name").descending().and(Sort.by("age")));

http로 sort 하는 방법 

단건 :
http://localhost:8080/tourlist/가?page=0&size=2&sort=contentid,desc

다중 :
http://localhost:8080/tourlist/가?page=0&size=2&sort=contentid,desc&sort=areacode,asc

 

'Spring' 카테고리의 다른 글

Query-DSL 참고 문서  (0) 2023.08.24
Springboot - JPA 이름 규칙  (0) 2023.08.24
SpringBoot 로그 출력  (0) 2023.08.15
SpringBoot - ES 개발  (0) 2023.08.15
SpringBoot Mybatis 구성  (0) 2023.08.13