담비의 개발블로그

[Spring Boot]Pagination 본문

언어&프레임워크/Spring&Spring Boot

[Spring Boot]Pagination

담비12 2024. 9. 26. 19:21
Pagination
 

데이터를 정렬기준, 페이지 크기, 몇번째 페이지인지를 토대로 정보를 전달해주는 것이다. 스프링에서는 직접 만들어서 사용도 가능하고 스프링에서 제공해주는 Pageable를 사용해도 된다.

 
 

 

import org.springframework.data.domain.Page; // 페이징을 위한 클래스

import org.springframework.data.domain.PageRequest; // 현재 페이지와 한 페이지에 보여줄 게시물 개수 등을 설정하여 페이징 요청을 하는 클래스

import org.springframework.data.domain.Pageable; // 페이징을 처리하는 인터페이스

 

 

Pageable

 

페이징을 요청할 때 사용되는 인터페이스다. 페이지 번호, 페이지 크기, 정렬 방식 등을 설정할 수 있다. Spring에서 페이징을 지원하는 메서드의 인자로 전달되어, 페이징 관련 정보를 제공한다.

 

장점

- 유연한 페이징 구현이 가능하며, 다양한 정렬 조건을 설정할 수 있다.

- API 설계 시, 클라이언트가 원하는 페이지와 사이즈를 직접 지정할 수 있게 하여 효율적인 데이터 조회가 가능하다.

 

단점

- 인터페이스 자체만으로는 인스턴스를 생성할 수 없고, 이를 구현한 구체 클래스(PageRequest)를 사용해야 한다.

 

 

◆ 주요개념

1. 페이지 번호 (Page Number)

페이지 번호는 0부터 시작한다. 즉, 첫 번째 페이지는 0, 두 번째 페이지는 1로 간주된다.

예: PageRequest.of(0, 10)은 첫 번째 페이지에서 10개의 데이터를 가져온다.

 

2. 페이지 크기 (Page Size)

한 페이지에 포함되는 데이터의 개수를 지정할 수 있다. 페이지 크기가 10이면, 한 페이지에 최대 10개의 데이터가 포함된다.

예: PageRequest.of(1, 20)은 두 번째 페이지에서 20개의 데이터를 반환한다.

 

3. 정렬 (Sorting)

데이터를 특정 컬럼을 기준으로 정렬할 수 있습니다. 오름차순 또는 내림차순으로 설정이 가능하다.

예: PageRequest.of(0, 10, Sort.by("name").ascending())은 이름 기준으로 오름차순으로 정렬된 첫 번째 페이지의 10개 데이터를 반환한다.

 

◆ 인터페이스 또는 클래스

1. Pageable 인터페이스

페이징 처리를 위한 추상 인터페이스로, 페이지 번호와 페이지 크기를 정의할 수 있다.

Pageable pageable = PageRequest.of(page, size);


getPageNumber() : 현재 페이지 번호를 반환(0부터 시작)

getPageSize() : 한 페이지당 최대 항목 수를 반환

getOffset() : 현재 페이지의 시작 위치를 반환

getSort() : 정렬 정보를 반환

next() : 다음 페이지 정보를 반환

previous() : 이전 페이지 정보를 반환

Pageable pageable = PageRequest.of(1, 10);  // 두 번째 페이지 요청 (0-based)
int pageNumber = pageable.getPageNumber();  // 1 반환

Pageable pageable = PageRequest.of(0, 10);  // 한 페이지에 10개의 항목
int pageSize = pageable.getPageSize();      // 10 반환

Pageable pageable = PageRequest.of(1, 10);  // 두 번째 페이지 요청
long offset = pageable.getOffset();         // 10 반환 (1 * 10)

Pageable pageable = PageRequest.of(0, 10, Sort.by("name").ascending());
Sort sort = pageable.getSort();  // name 필드를 오름차순 정렬한 정보 반환

Pageable pageable = PageRequest.of(1, 10);   // 두 번째 페이지
Pageable nextPageable = pageable.next();     // 세 번째 페이지 반환

Pageable pageable = PageRequest.of(1, 10);   // 두 번째 페이지
Pageable previousPageable = pageable.previous();  // 첫 번째 페이지 반환 (0 페이지)

 

2. PageRequest 클래스

Pageable 인터페이스의 구현체로, 페이지 번호, 페이지 크기, 정렬 방식을 정의하는 데 사용된다.

PageRequest.of(1, 20, Sort.by("createdAt").descending())

 

3. Page<T> 인터페이스

페이징된 데이터를 담고 있는 인터페이스이다. 페이지 번호, 총 페이지 수, 페이지 내 데이터 등의 정보를 제공한다.

Page<User> users = userRepository.findAll(PageRequest.of(0, 10));

// users.getTotalElements() → 전체 데이터 개수 반환
// users.getTotalPages() → 전체 페이지 수 반환
// users.getContent() → 현재 페이지에 포함된 데이터 반환​

 

4. Slice<T> 인터페이스

Page와 유사하지만, 총 페이지 수나 전체 데이터 개수 같은 정보는 제공하지 않고, 단순히 현재 페이지의 데이터를 반환하는 데 중점을 둔다. 더 간단한 페이징이 필요한 경우 유용하다.

 

 

 

사용법

Repository 에서 사용 예시

public interface UserRepository extends JpaRepository<User, Long> {
    Page<User> findAll(Pageable pageable);
}

 

 

Service 에서 사용 예시

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;

    public Page<User> getUsers(int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending());
        return userRepository.findAll(pageable);
    }
}

 

Controller 에서 사용 예시

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/users")
    public Page<User> getUsers(@RequestParam(defaultValue = "0") int page,
                               @RequestParam(defaultValue = "10") int size) {
        return userService.getUsers(page, size);
    }
}

// 만약 0부터 시작하는 페이징을 1부터 시작하고 싶다면 아래처럼 작성하면 된다.
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/users")
    public Page<User> getUsers(@RequestParam(defaultValue = "1") int page, // 기본값 1
                               @RequestParam(defaultValue = "10") int size) {
        // 사용자에게 입력받은 page는 1-based 이므로 1을 빼서 0-based로 변환
        PageRequest pageRequest = PageRequest.of(page - 1, size);
        return userService.getUsers(pageRequest);
    }
}

 

 

 

PageRequest

 

PageRequest는 Pageable을 구현한 구체 클래스이다. 페이지 번호(0부터 시작)와 한 페이지당 몇 개의 데이터를 가져올 것인지 설정할 수 있다. 정렬 옵션을 포함하여, 데이터를 특정 컬럼 기준으로 정렬할 수 있다.

 

장점

- Pageable을 구현함으로써 페이징과 정렬 조건을 손쉽게 설정할 수 있다.

- 간단한 메서드 호출로 페이지 요청을 쉽게 만들 수 있어 편리하다.

 

단점

- 페이지 번호가 0부터 시작하므로, 이를 헷갈리는 경우가 있을 수 있다.

- 고정된 정렬 방식이 필요할 때는 유연성이 떨어질 수 있다.

 

 

◆ 주요개념

1. Page (페이지 번호): 가져올 데이터의 페이지 번호이다. 페이지 번호는 0부터 시작한다. 예를 들어, 1페이지를 요청하고 싶으면 PageRequest.of(0, size)로 요청해야 한다.

 

2. Size (페이지 크기): 한 페이지에 몇 개의 데이터를 가져올지 설정한다. 예를 들어, 한 페이지에 10개의 데이터를 보고 싶다면 PageRequest.of(page, 10)과 같이 설정한다.

 

3. Sort (정렬 기준): 데이터가 특정 속성에 따라 정렬되도록 설정할 수 있다. 정렬 기준과 순서를 추가로 지정할 수 있으며, Sort.by("fieldName").ascending() 또는 Sort.by("fieldName").descending()와 같은 방식으로 사용한다.

 

 사용법

1. 기본사용법

0: 페이지 번호 (첫 번째 페이지, 즉 0부터 시작)

10: 한 페이지에 10개의 데이터

PageRequest pageRequest = PageRequest.of(0, 10);

 

2. 정렬 추가

데이터를 특정 필드에 따라 정렬하려면 Sort를 함께 사용한다.

"fieldName": 정렬할 필드 이름

ascending(): 오름차순 정렬

PageRequest pageRequest = PageRequest.of(0, 10, Sort.by("fieldName").ascending());

 

내림차순 정렬을 사용하려면 descending() 메서드를 사용하면 된다.

PageRequest pageRequest = PageRequest.of(0, 10, Sort.by("fieldName").descending());

 

 

3. 다중 정렬

여러 필드를 기준으로 정렬하려면 Sort 객체를 추가하여 사용한다. field1에 대해 오름차순으로, field2에 대해 내림차순으로 정렬하는 방식이다. findAll(pageable)은 PageRequest로 정의된 페이징 정보에 따라 데이터베이스에서 데이터를 조회한다.

PageRequest pageRequest = PageRequest.of(0, 10, Sort.by("field1").ascending().and(Sort.by("field2").descending()));

 

 

4. Service에서 사용하기

public Page<Construction> getConstructionList(Pageable pageable) {
    return constructionRepository.findAll(pageable);
}

 

Page

 

Page는 페이징된 결과를 나타내는 구체 클래스이다. 데이터 리스트와 함께 총 페이지 수, 현재 페이지, 전체 아이템 수 등을 포함하는 페이징 메타 데이터를 제공한다. 페이징 결과를 포함한 다양한 정보(총 데이터 개수, 페이지 정보 등)를 함께 반환하므로, 클라이언트에 필요한 메타 정보를 제공할 수 있다.

 

 

장점

- 페이징된 데이터 외에도 전체 데이터 개수, 현재 페이지 정보 등을 한 번에 제공하여 클라이언트가 더 많은 정보를 효율적으로 사용할 수 있다.

- 페이징된 데이터를 추가로 처리하는 기능(e.g., map 메서드)이 있어 다양한 후처리가 가능하다.

 

단점

- 전체 데이터 개수를 계산하기 위해 추가적인 쿼리가 실행되므로, 대규모 데이터셋에서 성능이 저하될 수 있다.

 

 

◆ 주요개념

1. 페이징된 데이터 접근: Page 객체는 해당 페이지에 포함된 데이터 목록을 제공하는 역할을 한다. 이 데이터는 일반적으로 List<T> 형태로 반환된다.

Page<MyEntity> page = myRepository.findAll(pageRequest);
List<MyEntity> entities = page.getContent();

 

2. 페이징 메타데이터 제공: Page 객체는 단순히 데이터 리스트만 제공하는 것이 아니라, 그와 함께 페이징과 관련된 다양한 메타 정보를 제공한다.

총 페이지 수 (getTotalPages()): 전체 데이터 집합을 주어진 페이지 크기로 나눈 후, 필요한 총 페이지 수를 반환

전체 데이터 개수 (getTotalElements()): 전체 데이터의 개수를 반환

현재 페이지 번호 (getNumber()): 현재 요청된 페이지의 번호를 반환(0부터 시작)

페이지 당 데이터 수 (getSize()): 한 페이지에 들어가는 데이터의 개수를 반환

현재 페이지 데이터 개수 (getNumberOfElements()): 현재 페이지에 포함된 데이터의 개수를 반환

 

 

3. 유용한 메서드들: Page는 다양한 데이터 처리와 관련된 유용한 메서드들을 제공한다.

map(Function): 페이지에 있는 데이터를 다른 타입으로 변환할 수 있습니다. 이는 서비스 계층에서 DTO(Data Transfer Object)로 데이터를 변환할 때 유용하다.

hasNext(): 다음 페이지가 존재하는지 여부를 확인한다.

hasPrevious(): 이전 페이지가 존재하는지 여부를 확인한다.

isFirst(): 현재 페이지가 첫 페이지인지 여부를 반환한다.

isLast(): 현재 페이지가 마지막 페이지인지 여부를 반환한다.

 

PageRequest pageRequest = PageRequest.of(0, 10, Sort.by("name").descending());
Page<MyEntity> pageResult = myRepository.findAll(pageRequest);

// 페이징된 데이터 리스트
List<MyEntity> content = pageResult.getContent();

// 페이징 메타 정보
int totalPages = pageResult.getTotalPages();
long totalElements = pageResult.getTotalElements();
boolean isLastPage = pageResult.isLast();