7.2 KiB
JPA에서 페이지 처리는 대량의 데이터를 효율적으로 조회하고 사용자에게 필요한 만큼만 보여주기 위해 사용되는 기능입니다. 특히 웹 애플리케이션에서 목록 조회 시, 모든 데이터를 한 번에 가져오는 대신 페이징을 통해 데이터를 분할해서 처리하는 것이 일반적입니다. Spring Data JPA는 이를 간편하게 구현할 수 있도록 PagingAndSortingRepository와 Pageable 인터페이스를 제공합니다. 아래에서 JPA의 페이지 처리에 대해 자세히 설명하겠습니다.
페이지 처리란?
페이지 처리는 데이터베이스에서 조회한 결과를 작은 단위(페이지)로 나누어 반환하는 기법입니다. 예를 들어, 게시판에 1,000개의 글이 있다면 한 페이지에 10개씩 보여주고, 사용자가 원하는 페이지를 선택해 해당 데이터만 조회합니다. 이를 통해 성능 최적화와 사용자 경험 개선을 동시에 달성할 수 있습니다.
Spring Data JPA에서의 페이지 처리
Spring Data JPA는 페이지 처리를 위해 다음과 같은 주요 구성 요소를 제공합니다:
-
Pageable 인터페이스
- 페이지 번호, 페이지 크기, 정렬 조건 등을 정의합니다.
PageRequest클래스를 통해 구현체를 생성합니다.- 예:
PageRequest.of(0, 10)는 첫 번째 페이지(0부터 시작)에 10개의 데이터를 요청합니다.
-
Page 반환 타입
- 조회된 데이터와 함께 페이징 관련 메타데이터(총 페이지 수, 총 데이터 수 등)를 제공합니다.
- 주요 메서드:
getContent(): 현재 페이지의 데이터 목록getTotalElements(): 전체 데이터 개수getTotalPages(): 전체 페이지 수getNumber(): 현재 페이지 번호
-
PagingAndSortingRepository
- Spring Data JPA에서 제공하는 기본 레포지토리 인터페이스로, 페이징과 정렬 기능을 지원합니다.
findAll(Pageable pageable)메서드를 통해 페이징된 데이터를 조회할 수 있습니다.
페이지 처리 구현 방법
Spring Boot와 Spring Data JPA를 기준으로 페이지 처리 구현 과정을 단계별로 설명하겠습니다.
1. 엔티티와 레포지토리 정의
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class Post {
@Id
private Long id;
private String title;
private String content;
// Getter, Setter
}
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
public interface PostRepository extends JpaRepository<Post, Long> {
Page<Post> findAll(Pageable pageable); // 기본 제공 메서드와 동일
}
2. 서비스 계층에서 페이지 처리
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<Post> getPosts(int page, int size) {
Pageable pageable = PageRequest.of(page, size); // 페이지 번호(0부터), 페이지 크기
return postRepository.findAll(pageable);
}
}
3. 컨트롤러에서 결과 반환
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<Post> getPosts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return postService.getPosts(page, size);
}
}
4. 실행 결과
- 요청:
GET /posts?page=0&size=10 - 응답: 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)도 지원합니다. 예를 들어, 제목 기준으로 오름차순 정렬을 추가하려면:
Pageable pageable = PageRequest.of(0, 10, Sort.by("title").ascending());
Page<Post> posts = postRepository.findAll(pageable);
컨트롤러에서 요청 파라미터로 정렬 조건을 받을 수도 있습니다:
@GetMapping("/posts")
public Page<Post> 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 어노테이션이나 메서드 이름을 사용합니다.
public interface PostRepository extends JpaRepository<Post, Long> {
Page<Post> findByTitleContaining(String keyword, Pageable pageable);
@Query("SELECT p FROM Post p WHERE p.content LIKE %:keyword%")
Page<Post> 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를 활용하면 데이터 조회를 효율적으로 관리하고, 웹 애플리케이션에서 필수적인 페이징 기능을 손쉽게 제공할 수 있습니다. 대규모 데이터를 다룰 때 필수적인 이 기능을 잘 활용하면 성능과 사용자 경험 모두를 만족시킬 수 있습니다!