JPA에서 페이지 처리는 대량의 데이터를 효율적으로 조회하고 사용자에게 필요한 만큼만 보여주기 위해 사용되는 기능입니다. 특히 웹 애플리케이션에서 목록 조회 시, 모든 데이터를 한 번에 가져오는 대신 페이징을 통해 데이터를 분할해서 처리하는 것이 일반적입니다. Spring Data JPA는 이를 간편하게 구현할 수 있도록 `PagingAndSortingRepository`와 `Pageable` 인터페이스를 제공합니다. 아래에서 JPA의 페이지 처리에 대해 자세히 설명하겠습니다. --- ### 페이지 처리란? 페이지 처리는 데이터베이스에서 조회한 결과를 작은 단위(페이지)로 나누어 반환하는 기법입니다. 예를 들어, 게시판에 1,000개의 글이 있다면 한 페이지에 10개씩 보여주고, 사용자가 원하는 페이지를 선택해 해당 데이터만 조회합니다. 이를 통해 성능 최적화와 사용자 경험 개선을 동시에 달성할 수 있습니다. --- ### Spring Data JPA에서의 페이지 처리 Spring Data JPA는 페이지 처리를 위해 다음과 같은 주요 구성 요소를 제공합니다: 1. **Pageable 인터페이스** - 페이지 번호, 페이지 크기, 정렬 조건 등을 정의합니다. - `PageRequest` 클래스를 통해 구현체를 생성합니다. - 예: `PageRequest.of(0, 10)`는 첫 번째 페이지(0부터 시작)에 10개의 데이터를 요청합니다. 2. **Page 반환 타입** - 조회된 데이터와 함께 페이징 관련 메타데이터(총 페이지 수, 총 데이터 수 등)를 제공합니다. - 주요 메서드: - `getContent()`: 현재 페이지의 데이터 목록 - `getTotalElements()`: 전체 데이터 개수 - `getTotalPages()`: 전체 페이지 수 - `getNumber()`: 현재 페이지 번호 3. **PagingAndSortingRepository** - Spring Data JPA에서 제공하는 기본 레포지토리 인터페이스로, 페이징과 정렬 기능을 지원합니다. - `findAll(Pageable pageable)` 메서드를 통해 페이징된 데이터를 조회할 수 있습니다. --- ### 페이지 처리 구현 방법 Spring Boot와 Spring Data JPA를 기준으로 페이지 처리 구현 과정을 단계별로 설명하겠습니다. #### 1. 엔티티와 레포지토리 정의 ```java import jakarta.persistence.Entity; import jakarta.persistence.Id; @Entity public class Post { @Id private Long id; private String title; private String content; // Getter, Setter } ``` ```java import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; public interface PostRepository extends JpaRepository { Page findAll(Pageable pageable); // 기본 제공 메서드와 동일 } ``` #### 2. 서비스 계층에서 페이지 처리 ```java import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; @Service public class PostService { private final PostRepository postRepository; public PostService(PostRepository postRepository) { this.postRepository = postRepository; } public Page getPosts(int page, int size) { Pageable pageable = PageRequest.of(page, size); // 페이지 번호(0부터), 페이지 크기 return postRepository.findAll(pageable); } } ``` #### 3. 컨트롤러에서 결과 반환 ```java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.data.domain.Page; @RestController public class PostController { private final PostService postService; public PostController(PostService postService) { this.postService = postService; } @GetMapping("/posts") public Page getPosts( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) { return postService.getPosts(page, size); } } ``` #### 4. 실행 결과 - 요청: `GET /posts?page=0&size=10` - 응답: JSON 형태로 현재 페이지의 데이터와 페이징 정보가 반환됩니다. ```json { "content": [ {"id": 1, "title": "Post 1", "content": "Content 1"}, {"id": 2, "title": "Post 2", "content": "Content 2"}, ... ], "pageable": { "pageNumber": 0, "pageSize": 10, ... }, "totalElements": 100, "totalPages": 10, "number": 0 } ``` --- ### 정렬 추가하기 `Pageable`은 정렬(Sorting)도 지원합니다. 예를 들어, 제목 기준으로 오름차순 정렬을 추가하려면: ```java Pageable pageable = PageRequest.of(0, 10, Sort.by("title").ascending()); Page posts = postRepository.findAll(pageable); ``` 컨트롤러에서 요청 파라미터로 정렬 조건을 받을 수도 있습니다: ```java @GetMapping("/posts") public Page getPosts( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(defaultValue = "title") String sortBy) { Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy).ascending()); return postService.getPosts(pageable); } ``` --- ### 커스텀 쿼리와 페이지 처리 특정 조건으로 데이터를 조회하면서 페이징을 적용하려면 `@Query` 어노테이션이나 메서드 이름을 사용합니다. ```java public interface PostRepository extends JpaRepository { Page findByTitleContaining(String keyword, Pageable pageable); @Query("SELECT p FROM Post p WHERE p.content LIKE %:keyword%") Page searchByContent(@Param("keyword") String keyword, Pageable pageable); } ``` --- ### 장점 - **성능 최적화**: 필요한 데이터만 조회하므로 메모리와 DB 부하를 줄입니다. - **사용자 경험 개선**: 대량 데이터를 한 번에 로드하지 않고, 페이지 단위로 제공합니다. - **유연성**: 정렬, 필터링 등과 결합해 다양한 요구사항을 처리할 수 있습니다. ### 주의점 - **페이지 번호**: Spring Data JPA는 페이지 번호가 0부터 시작합니다(예: 0은 첫 페이지). - **쿼리 최적화**: 페이징 쿼리는 `OFFSET`과 `LIMIT`을 사용하므로, 데이터가 많아질수록 성능 저하가 발생할 수 있습니다. 이 경우 인덱스 사용이나 커서 기반 페이징을 고려해야 합니다. - **총 개수 계산**: `Page` 객체는 `totalElements`를 계산하기 위해 별도의 `COUNT` 쿼리를 실행합니다. 성능에 민감한 경우 `Slice`를 사용하는 것도 대안입니다(`totalElements`와 `totalPages`를 제공하지 않음). --- ### 결론 JPA의 페이지 처리는 Spring Data JPA를 통해 매우 간단하면서도 강력하게 구현할 수 있습니다. `Pageable`과 `Page`를 활용하면 데이터 조회를 효율적으로 관리하고, 웹 애플리케이션에서 필수적인 페이징 기능을 손쉽게 제공할 수 있습니다. 대규모 데이터를 다룰 때 필수적인 이 기능을 잘 활용하면 성능과 사용자 경험 모두를 만족시킬 수 있습니다!