오봉이와 함께하는 개발 블로그

스프링 데이터 JPA - Web 확장 페이징과 정렬 본문

BE/JPA

스프링 데이터 JPA - Web 확장 페이징과 정렬

오봉봉이 2022. 9. 14. 02:29
728x90

Web 확장 - 페이징과 정렬

스프링 데이터가 제공하는 페이징과 정렬 기능을 스프링 MVC에서 편리하게 사용할 수 있다.

페이징과 정렬 예제

@GetMapping("/members")
public Page<Member> list(Pageable pageable) {
    Page<Member> page = memberRepository.findAll(pageable);
    return page;
}
  • 파라미터로 Pageable을 받을 수 있다.
  • Pageable은 인터페이스다.
    • 실제는 org.springframework.data.domain.PageRequest 객체 생성
{
        .........................
        {
            "createdDate": "2022-09-14T01:54:54.034161",
            "lastModifiedDate": "2022-09-14T01:54:54.034161",
            "createdBy": "cbd93077-4360-4b66-975d-64e3acab775e",
            "lastModifiedBy": "cbd93077-4360-4b66-975d-64e3acab775e",
            "id": 20,
            "username": "user19",
            "age": 19,
            "team": null
        }
    ],
    "pageable": {
        "sort": {
            "sorted": false,
            "unsorted": true,
            "empty": true
        },
        "pageNumber": 0,
        "pageSize": 20,
        "offset": 0,
        "paged": true,
        "unpaged": false
    },
    "totalPages": 5,
    "totalElements": 100,
    "last": false,
    "numberOfElements": 20,
    "first": true,
    "number": 0,
    "sort": {
        "sorted": false,
        "unsorted": true,
        "empty": true
    },
    "size": 20,
    "empty": false
}

요청 파라미터

  • 예 : /members?page=0&size=3&sort=id,desc&sort=username,desc
    • page : 현재 페이지, 0부터 시작한다.
    • size : 한 페이지에 노출할 데이터 건수
    • sort : 정렬 조건을 정의한다.
      • 예 : 정렬 속성,정렬 속성...(ASC | DESC)
      • 정렬 방향을 변경하고 싶으면 sort파라미터 추가 (asc 생략 가능)
{
    "content": [
        {
            "createdDate": "2022-09-14T01:54:54.291475",
            "lastModifiedDate": "2022-09-14T01:54:54.291475",
            "createdBy": "6def164a-778b-4ae5-bf45-5c67914d2c8b",
            "lastModifiedBy": "6def164a-778b-4ae5-bf45-5c67914d2c8b",
            "id": 100,
            "username": "user99",
            "age": 99,
            "team": null
        },
        {
            "createdDate": "2022-09-14T01:54:54.288771",
            "lastModifiedDate": "2022-09-14T01:54:54.288771",
            "createdBy": "6975352b-9832-4445-9caf-89ea4a2c4b27",
            "lastModifiedBy": "6975352b-9832-4445-9caf-89ea4a2c4b27",
            "id": 99,
            "username": "user98",
            "age": 98,
            "team": null
        },
        {
            "createdDate": "2022-09-14T01:54:54.285728",
            "lastModifiedDate": "2022-09-14T01:54:54.285728",
            "createdBy": "da495521-9b0b-499f-9390-76c68d19eb08",
            "lastModifiedBy": "da495521-9b0b-499f-9390-76c68d19eb08",
            "id": 98,
            "username": "user97",
            "age": 97,
            "team": null
        }
    ],
    "pageable": {
        "sort": {
            "sorted": true,
            "unsorted": false,
            "empty": false
        },
        "pageNumber": 0,
        "pageSize": 3,
        "offset": 0,
        "paged": true,
        "unpaged": false
    },
    "totalPages": 34,
    "totalElements": 100,
    "last": false,
    "numberOfElements": 3,
    "first": true,
    "number": 0,
    "sort": {
        "sorted": true,
        "unsorted": false,
        "empty": false
    },
    "size": 3,
    "empty": false
}

기본값

  • 글로벌 설정 : 스프링 부트
spring.data.web.pageable.default-page-size=20 /# 기본 페이지 사이즈/
spring.data.web.pageable.max-page-size=2000 /# 최대 페이지 사이즈/
  • 개별 설정
    • @PageableDefault 어노테이션을 사용
@RequestMapping(value = "/members_page", method = RequestMethod.GET)
public String list(@PageableDefault(size = 12, sort = "username",
                    direction = Sort.Direction.DESC) Pageable pageable) {
    // ...............
}

접두사

  • 페이징 정보가 둘 이상이면 접두사로 구분
  • @Qualifier 에 접두사명 추가 {접두사명}_xxx
  • 예 : /members?member_page=0&order_page=1

Page 내용을 DTO로 변환하기

  • 엔티티를 API로 노출하면 다양한 문제가 발생한다.
    • 그래서 엔티티를 꼭 DTO로 변환해서 반환해야 한다.
  • Page는 map()을 지원해서 내부 데이터를 다른 것으로 변경할 수 있다.

MemberDTO

@Data
public class MemberDto {
    private Long id;
    private String username;

    public MemberDto(Long id, String username) {
        this.id = id;
        this.username = username;
    }
}

Page.map() 사용

@GetMapping("/members")
public Page<MemberDto> list(Pageable pageable) {
    Page<Member> page = memberRepository.findAll(pageable);
    Page<MemberDto> memberDto = page.map(m -> new MemberDto(m.getId(), m.getUsername()));
    return memberDto;
}

MemberDTO 최적화

DTO는 엔티티를 알고 있어도 괜찮기 때문에 아래와 같이 엔티티를 생성자 파라미터로 받을 수 있다.

@Data
public class MemberDto {
    private Long id;
    private String username;

    public MemberDto(Member member) {
        this.id = member.getId();
        this.username = member.getUsername();
    }
}
@GetMapping("/members")
public Page<MemberDto> list(Pageable pageable) {
    Page<Member> page = memberRepository.findAll(pageable);
    Page<MemberDto> memberDto = page.map(MemberDto::new);
    return memberDto;
}

Page를 1부터 시작하기

스프링 데이터는 Page를 0부터 시작하는데, 만약 1부터 시작하려면?

  1. Pageable, Page를 파리미터와 응답 값으로 사용히지 않고, 직접 클래스를 만들어서 처리한다. 그리고 직접 PageRequest(Pageable 구현체)를 생성해서 리포지토리에 넘긴다. 물론 응답값도 Page 대신에 직접 만들어서 제공해야 한다.
@GetMapping("/members")
public MyPage<MemberDto> list() {
    PageRequest request = PageRequest.of(1, 2); // page(시작 페이지), size(페이지 사이즈)
    Page<MemberDto> memberDto = page.map(MemberDto::new);
    MyPage<MemberDto> result = ..............
}
  1. spring.data.web.pageable.one-indexed-parameterstrue로 설정한다. 그런데 이 방법은 web에서 page파라미터를 -1 처리 할 뿐이다. 따라서 응답값인 Page에 모두 0 페이지 인덱스를 사용하는 한계가 있다.

application.properties

spring.data.web.pageable.one-indexed-parameters=true

one-indexed-parameters Page 1요청 (http://localhost:8080/members?page=1)

{
    "content": [
      ...
    ],
    "pageable": {
        "offset": 0,
        "pageSize": 10,
        "pageNumber": 0 //0 인덱스
    },
    "number": 0, //0 인덱스
    "empty": false
}

위 결과와 같이 1로 요청해도 시작은 0을 기준으로 한다.

http://localhost:8080/members?page=0으로 요청 했을 때는 기본적으로 1로 요청한것과 동일하게 요청된다.

인프런 김영한 지식공유자님 강의 : 실전! 스프링 데이터 JPA
728x90
Comments