Spring Boot API에서 페이징 처리 개념 및 구현하기

Jayson·2024년 12월 20일
post-thumbnail

프로젝트가 3차 스프린트로 들어서면서 1, 2차 스프린트 때 클라이언트 파트 팀원의 리소스를 고려해 구현하지 않았던 페이지네이션을 이번 스프린트에서 적용하기로 결정했습니다. 이를 계기로 페이지네이션이 적용되어 있지 않은 API에 기능을 도입하는 과정에서 배운 점을 정리하고, 개념과 구현 방법을 함께 공유하고자 이 글을 작성했습니다.


1. 페이징 처리란?

페이징 처리(Pagination)는 데이터를 여러 페이지로 나누어 클라이언트에 제공하는 방법입니다. 대량의 데이터를 한 번에 로드하지 않고 필요한 부분만 요청하여 성능을 최적화할 수 있습니다.

페이징 처리의 주요 목표는 다음과 같습니다:

  • 효율적인 데이터 로드: 대량의 데이터를 한 번에 처리하지 않음으로써 성능 최적화.
  • 사용자 경험 개선: 클라이언트가 데이터를 탐색하기 쉽도록 분리된 페이지 제공.

페이징 처리 시 클라이언트는 보통 다음 정보를 요청하거나 받을 수 있습니다:

  • 요청 정보:
    • page: 요청할 페이지 번호.
    • size: 한 페이지에 포함될 항목 수.
    • sort: 정렬 기준.
  • 응답 정보:
    • result: 요청된 페이지의 데이터.
    • totalPages: 전체 페이지 수.
    • totalCount: 전체 항목 수.
    • hasNext: 다음 페이지 존재 여부.

2. Spring Boot에서의 Pageable 인터페이스

Spring Boot는 페이징 처리를 쉽게 구현할 수 있도록 PageablePage 인터페이스를 제공합니다.

Pageable이란?

  • 클라이언트의 요청 정보를 기반으로 데이터베이스 쿼리에 필요한 페이지와 정렬 정보를 캡슐화한 객체입니다.
  • Pageable은 다음 정보를 포함합니다:
    • 페이지 번호 (page): 0부터 시작.
    • 페이지 크기 (size): 기본값 또는 클라이언트 요청값.
    • 정렬 기준 (sort).

Page란?

  • 페이징 처리 결과를 캡슐화한 객체입니다.
  • Page는 다음 정보를 포함합니다:
    • 현재 페이지의 데이터.
    • 전체 페이지 수 (totalPages).
    • 전체 항목 수 (totalCount).
    • 다음 페이지 존재 여부 (hasNext).

3. Controller에서 페이징 처리 구현

Pageable 객체를 Controller 메서드에 추가하여 클라이언트 요청을 처리할 수 있습니다.

예시 코드

@GetMapping("/home")
public ResponseEntity<SuccessResponse<HomeAnnouncementsResponseDto>> getAnnouncements(
        @RequestParam(value = "sortBy", required = false, defaultValue = "deadlineSoon") String sortBy,
        @PageableDefault(size = 10) Pageable pageable) {
    HomeAnnouncementsResponseDto announcements = homeService.getAnnouncements(sortBy, pageable);
    return ResponseEntity.ok(SuccessResponse.of(SUCCESS_GET_ANNOUNCEMENTS, announcements));
}

설명

  1. Pageable 객체 생성:

    • @PageableDefault(size = 10) 어노테이션을 사용하여 기본 페이지 크기를 설정합니다.
    • 클라이언트 요청 시 page, size, sort 파라미터를 전달하면 Pageable이 자동 생성됩니다.
  2. 요청 예시:

    GET /home?sortBy=mostViewed&page=1&size=5
    
  3. Pageable 자동 처리:

    • page: 0부터 시작.
    • size: 요청된 값(또는 기본값).
    • sort: 정렬 기준.

4. Service 계층에서 페이징 처리

Service 계층에서는 Pageable 객체를 활용하여 페이징된 데이터를 처리하고 DTO로 반환합니다.

예시 코드

public HomeAnnouncementsResponseDto getAnnouncements(String sortBy, Pageable pageable) {
    Page<Tuple> pagedAnnouncements = internshipRepository.findFilteredInternships(sortBy, pageable);

    if (pagedAnnouncements.isEmpty()) {
        return HomeAnnouncementsResponseDto.of(0, 0, false, List.of());
    }

    List<HomeResponseDto> responseDtos = pagedAnnouncements.getContent().stream()
            .map(tuple -> HomeResponseDto.of(tuple))
            .toList();

    return HomeAnnouncementsResponseDto.of(
            pagedAnnouncements.getTotalPages(),
            (int) pagedAnnouncements.getTotalElements(),
            pagedAnnouncements.hasNext(),
            responseDtos
    );
}

설명

  1. Repository 호출:
    • Page<Tuple>를 반환하는 Repository 메서드 호출.
  2. DTO 변환:
    • Page.getContent()를 활용하여 현재 페이지의 데이터를 변환.
  3. 응답 객체 생성:
    • totalPages, totalCount, hasNext를 포함한 DTO를 반환.

5. Repository에서 페이징 처리

Spring Data JPA와 QueryDSL은 페이징 처리를 지원합니다. Pageable을 활용하여 데이터베이스에서 필요한 데이터를 가져옵니다.

예시 코드

public Page<Tuple> findFilteredInternships(String sortBy, Pageable pageable) {
    List<Tuple> content = jpaQueryFactory
            .select(internshipAnnouncement, scrap.id, scrap.color)
            .from(internshipAnnouncement)
            .where(getFilters())
            .orderBy(getSortOrder(sortBy))
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();

    JPAQuery<Long> countQuery = jpaQueryFactory
            .select(internshipAnnouncement.count())
            .from(internshipAnnouncement)
            .where(getFilters());

    return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchOne);
}

설명

  1. 쿼리 분리:
    • 데이터 쿼리 (fetch)와 개수 쿼리 (countQuery)를 분리하여 효율적으로 처리.
  2. Page 객체 생성:
    • PageableExecutionUtils.getPage()를 사용하여 페이징된 데이터를 반환.

6. 결과 및 응답 예시

클라이언트 요청

GET /home?sortBy=mostViewed&page=1&size=10

서버 응답

{
  "totalPages": 5,
  "totalCount": 50,
  "hasNext": true,
  "result": [ ... ]
}

7. 페이징 처리 도입의 장점

  1. 효율적인 데이터 로드:
    • 대량의 데이터를 처리하지 않아 성능이 최적화됩니다.
  2. 확장성:
    • 클라이언트 요청에 따라 데이터 크기와 정렬 기준을 유연하게 설정 가능합니다.
  3. 표준화:
    • Spring Boot의 표준 인터페이스를 활용하여 일관된 페이징 처리 구현.

Spring Boot의 PageablePage는 페이징 처리에 필요한 개념과 구현을 모두 지원합니다. 이 문서를 통해 페이징 처리의 개념을 이해하고, 실제 프로젝트에 적용하는데 도움이 되었으면 좋겠습니다.

profile
Small Big Cycle

0개의 댓글