2025-04-08T19:56:24

This commit is contained in:
2025-04-08 19:56:24 +09:00
parent a75a1dbd0f
commit eef061c1c9
100 changed files with 18639 additions and 0 deletions

178
docs/jpa/13_paging.md Normal file
View File

@@ -0,0 +1,178 @@
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<T> 반환 타입**
- 조회된 데이터와 함께 페이징 관련 메타데이터(총 페이지 수, 총 데이터 수 등)를 제공합니다.
- 주요 메서드:
- `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<Post, Long> {
Page<Post> 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<Post> 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<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 형태로 현재 페이지의 데이터와 페이징 정보가 반환됩니다.
```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<Post> posts = postRepository.findAll(pageable);
```
컨트롤러에서 요청 파라미터로 정렬 조건을 받을 수도 있습니다:
```java
@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` 어노테이션이나 메서드 이름을 사용합니다.
```java
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`를 활용하면 데이터 조회를 효율적으로 관리하고, 웹 애플리케이션에서 필수적인 페이징 기능을 손쉽게 제공할 수 있습니다. 대규모 데이터를 다룰 때 필수적인 이 기능을 잘 활용하면 성능과 사용자 경험 모두를 만족시킬 수 있습니다!