Skip to content

Commit c6b3b5f

Browse files
committed
keyword: 9주차 키워드 정리
1 parent 2683726 commit c6b3b5f

1 file changed

Lines changed: 164 additions & 0 deletions

File tree

keyword/chaptor09/keyword.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#
2+
3+
## Spring Data JPA의 Paging
4+
5+
Spring Data JPA는 데이터 조회 결과를 효율적으로 관리하기 위해 **Paging**(페이징) 기능을 제공합니다. 페이징은 대량의 데이터를 페이지 단위로 나누어 클라이언트에 반환하는 기술로, **Page****Slice**라는 두 가지 주요 개념이 있습니다.
6+
7+
### Page
8+
9+
Page는 페이징의 전체 정보를 포함하는 데이터 구조입니다. 페이징 처리 시 요청된 페이지의 데이터뿐만 아니라, **전체 페이지 수**, **전체 데이터 수** 등의 메타데이터를 포함합니다.
10+
11+
**특징**
12+
* 요청한 페이지의 데이터 목록과 함께 페이징 정보를 포함.
13+
* Page 객체는 데이터 외에 추가적인 페이징 정보도 함께 반환합니다.
14+
* 데이터 총 개수를 알아야 하므로 COUNT **쿼리를 추가 실행**합니다.
15+
16+
**Page 인터페이스 주요 메서드**
17+
* `List<T> getContent()`: 현재 페이지에 포함된 데이터.
18+
* `int getNumber()`: 현재 페이지 번호 (0-based).
19+
* `int getSize()`: 요청된 페이지 크기.
20+
* `int getTotalPages()`: 전체 페이지 수.
21+
* `long getTotalElements()`: 전체 데이터 개수.
22+
* `boolean isFirst()`: 첫 번째 페이지 여부.
23+
* `boolean isLast()`: 마지막 페이지 여부.
24+
* `boolean hasNext()`: 다음 페이지가 있는지 여부.
25+
* `boolean hasPrevious()`: 이전 페이지가 있는지 여부.
26+
27+
**사용예제**
28+
```java
29+
import org.springframework.data.domain.Page;
30+
import org.springframework.data.domain.PageRequest;
31+
import org.springframework.data.domain.Pageable;
32+
33+
Pageable pageable = PageRequest.of(0, 10); // 첫 번째 페이지(0), 페이지 크기 10
34+
Page<Member> members = memberRepository.findAll(pageable);
35+
36+
System.out.println("Total Elements: " + members.getTotalElements()); // 전체 데이터 개수
37+
System.out.println("Total Pages: " + members.getTotalPages()); // 전체 페이지 수
38+
System.out.println("Current Page: " + members.getNumber()); // 현재 페이지 번호
39+
System.out.println("Page Size: " + members.getSize()); // 페이지 크기
40+
41+
List<Member> content = members.getContent(); // 현재 페이지 데이터
42+
43+
```
44+
45+
해당 데이터들을 클라이언트에게 전달해주면 됩니다. 필요한 데이터만 뽑아서 주는 것이 깔끔할 것이라고 생각은 합니다. 핵심은 Spring Data JPA가 위의 기능들을 제공해준다는 것입니다.
46+
47+
48+
### Slice
49+
50+
Slice는 Page와 유사하지만, **전체 페이지 수나 데이터 총 개수를 계산하지 않는** 데이터 구조입니다. 필요한 데이터만 조회하여 클라이언트에 반환하므로, 성능이 중요한 경우 유리합니다.
51+
52+
**특징**
53+
* 현재 페이지와 다음 페이지의 존재 여부 정보만 제공.
54+
* COUNT **쿼리를 실행하지 않아** 성능이 더 우수.
55+
* 전체 데이터 수나 전체 페이지 수는 알 수 없음.
56+
* **무한 스크롤**이나 **다음 페이지 요청**이 필요한 상황에서 사용.
57+
58+
**Slice 인터페이스 주요 메서드**
59+
* List<T> getContent(): 현재 페이지에 포함된 데이터.
60+
* int getNumber(): 현재 페이지 번호 (0-based).
61+
* int getSize(): 요청된 페이지 크기.
62+
* boolean isFirst(): 첫 번째 페이지 여부.
63+
* boolean hasNext(): 다음 페이지가 있는지 여부.
64+
65+
```java
66+
import org.springframework.data.domain.PageRequest;
67+
import org.springframework.data.domain.Pageable;
68+
import org.springframework.data.domain.Slice;
69+
70+
Pageable pageable = PageRequest.of(0, 10); // 첫 번째 페이지(0), 페이지 크기 10
71+
Slice<Member> members = memberRepository.findAllByStatus("ACTIVE", pageable);
72+
73+
System.out.println("Current Page: " + members.getNumber()); // 현재 페이지 번호
74+
System.out.println("Page Size: " + members.getSize()); // 페이지 크기
75+
System.out.println("Has Next: " + members.hasNext()); // 다음 페이지 여부
76+
77+
List<Member> content = members.getContent(); // 현재 페이지 데이터
78+
79+
```
80+
81+
| **특징** | **Page** | **Slice** |
82+
|----------------------|------------------------------------|-------------------------------------|
83+
| **전체 데이터 수** | 제공 (`getTotalElements()`) | 제공하지 않음 |
84+
| **전체 페이지 수** | 제공 (`getTotalPages()`) | 제공하지 않음 |
85+
| **성능** | 상대적으로 느림 (`COUNT` 필요) | 상대적으로 빠름 (`COUNT` 필요 없음) |
86+
| **사용 상황** | 일반적인 페이징 | 무한 스크롤, 간단한 페이지 네비게이션 |
87+
88+
89+
**Page vs Slice**
90+
* Page는 **전체 데이터 정보**를 제공하며 일반적인 페이징에 적합.
91+
* Slice는 **성능 최적화**와 무한 스크롤에 적합.
92+
93+
94+
---
95+
## 객체 그래프 탐색
96+
97+
**객체 그래프 탐색**은 JPA에서 엔티티 간의 연관 관계를 탐색하며 데이터를 가져오는 작업을 의미합니다. JPA에서는 @OneToOne, @OneToMany, @ManyToOne, @ManyToMany 등의 연관 관계 매핑을 통해 객체 간 관계를 설정합니다.
98+
99+
### 객체 그래프 탐색 전략
100+
101+
**즉시 로딩 (Eager Loading)**
102+
- 연관된 엔티티를 **즉시 로드**.
103+
- 연관된 엔티티를 **JOIN 쿼리**로 한 번에 가져옴.
104+
* 사용: @OneToOne, @ManyToOne 기본값.
105+
106+
```java
107+
@ManyToOne(fetch = FetchType.EAGER)
108+
@JoinColumn(name = "member_id")
109+
private Member member;
110+
111+
```
112+
113+
**장점**
114+
* 연관 데이터를 미리 가져와서 지연 로딩 문제 해결
115+
116+
**단점**
117+
* 불필요한 데이터를 미리 가져오면 성능 저하 가능
118+
* 쿼리가 예측하기 힘들어짐
119+
120+
121+
**지연 로딩 (Lazy Loading)**
122+
* 연관된 엔티티를 **실제 사용하는 시점**에 로드.
123+
* 기본값: @OneToMany, @ManyToMany.
124+
125+
```java
126+
@OneToMany(mappedBy = "member", fetch = FetchType.LAZY)
127+
private List<Order> orders;
128+
129+
```
130+
131+
**장점**
132+
* 필요한 데이터를 사용할 때만 가져옴.
133+
* 메모리 및 성능 효율성.
134+
135+
**단점**
136+
* 실제 데이터 접근 시 추가 쿼리 발생.
137+
138+
**N+1 문제**
139+
* 즉시 로딩 시 **연관된 데이터의 개수만큼 추가적인 SELECT 쿼리가 발생**.
140+
141+
142+
```sql
143+
SELECT * FROM member;
144+
SELECT * FROM orders WHERE member_id = 1;
145+
SELECT * FROM orders WHERE member_id = 2;
146+
147+
```
148+
149+
150+
해결 방법
151+
152+
**Fetch Join**
153+
```java
154+
@Query("SELECT m FROM Member m JOIN FETCH m.orders")
155+
List<Member> findAllWithOrders();
156+
```
157+
158+
**EntityGraph**
159+
```java
160+
@EntityGraph(attributePaths = {"orders"})
161+
List<Member> findAllWithOrders();
162+
```
163+
164+

0 commit comments

Comments
 (0)