Skip to content

Commit 3b53ecd

Browse files
committed
keyword: 7주차 키워드 정리
1 parent 77bc270 commit 3b53ecd

15 files changed

Lines changed: 266 additions & 1 deletion

File tree

keyword/chapter07/img.png

118 KB
Loading

keyword/chapter07/img_1.png

33.8 KB
Loading

keyword/chapter07/img_2.png

15 KB
Loading

keyword/chapter07/img_3.png

18.1 KB
Loading

keyword/chapter07/img_4.png

10.5 KB
Loading

keyword/chapter07/img_5.png

33.8 KB
Loading

keyword/chapter07/img_6.png

33.8 KB
Loading

keyword/chapter07/img_7.png

15 KB
Loading

keyword/chapter07/keyword.md

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
- Page와 Slice
2+
3+
Pageable은 페이징 요청 정보를 담는 인터페이스이다.
4+
5+
→ 결국, 페이징을 진행할 데이터의 원본은 데이터베이스에 있다.
6+
7+
JPA는 Pageable 정보를 기반으로 DB에 쿼리를 날리고, 결과를 다시 특정 객체로 반환해준다.
8+
9+
이때 결과를 담아주는 객체가 **Page****Slice**이다.
10+
11+
- 객체 상속 관계
12+
13+
![img.png](img.png)
14+
15+
- Slice
16+
- 다음 페이지가 있는지만 알려주는 가벼운 페이징 결과 객체
17+
- **SliceImpl**구현 클래스를 통해 객체를 생성하고 동작을 수행한다.
18+
- 생성 방식
19+
- **List<T>content + pageable + hasNext**
20+
21+
![img_3.png](img_3.png)
22+
23+
```java
24+
// 현재 페이지의 데이터
25+
List<String> content = Arrays.asList("item1", "item2", "item3");
26+
27+
// Pageable 정보 (0번 페이지, 3개씩 조회)
28+
Pageable pageable = PageRequest.of(0, 3);
29+
30+
// 다음 페이지가 있다고 가정
31+
boolean hasNext = true;
32+
33+
// SliceImpl 생성
34+
Slice<String> slice = new SliceImpl<>(content, pageable, hasNext);
35+
```
36+
37+
- hasNext 값을 인자로 받는다.
38+
- 전체 데이터 개수를 알기 위한 별도의 count(*) 쿼리가 필요하지 않다.
39+
- **전체 페이지 수를 알 필요 없는 무한 스크롤과 같은 경우는 Slice를 사용하는 것이 적합하다.**
40+
- 전체 페이지를 모르는데 hasNext()를 판별할 수 있는 이유
41+
42+
기존의 페이지 사이즈보다 한 개를 더해서 불러온다면
43+
다음 페이지에 데이터가 있는지에 대한 여부를 확인 가능하다.
44+
45+
⇒JPA에서는 쿼리를 보낼 때 PageRequest가 요구하는
46+
페이지 사이즈에 +1을 하여 쿼리를 조회한다.
47+
48+
- -**List<T>content**
49+
50+
![img_4.png](img_4.png)
51+
52+
- pageable이 필요 없는 경우에 사용한다.
53+
- hasNext를 false로 이용한다.
54+
55+
```java
56+
this(content, Pageable.unpaged(), false);
57+
58+
```
59+
60+
- Page
61+
- Slice의 모든 기능 + 전체 페이지 수, 총 데이터 개수 제공
62+
- **PageImpl** 구현 클래스를 통해 객체를 생성하고 동작을 수행한다.
63+
- hasNext(): 다음 페이지가 있는지에 대한 여부
64+
- isLast(): 현재 페이지가 마지막인지에 대한 여부
65+
- getTotalPages(), getTotalElements(): 전체 데이터 개수에 대한 정보
66+
- 생성 방식
67+
- **List<T>content + pageable + total**
68+
69+
![img_6.png](img_6.png)
70+
71+
```java
72+
// 현재 페이지의 데이터
73+
List<String> content = Arrays.asList("item1", "item2", "item3");
74+
75+
// Pageable 정보 (0번 페이지, 3개씩 조회)
76+
Pageable pageable = PageRequest.of(0, 3);
77+
78+
// 전체 데이터 개수가 100개
79+
long total = 100
80+
81+
// Page 생성
82+
Page<String> page = new PageImple<>(content, pageable, total);
83+
```
84+
85+
- 전체 데이터 개수를 알아야 하기 때문에,
86+
Spring Data JPA는 내부적으로 count(*) 쿼리를 별도로 실행하여
87+
전체 데이터 수를 조회한다.
88+
89+
- **List<T>content**
90+
91+
- ![img_7.png](img_7.png)
92+
```java
93+
List<String> content = userRepository.findAll() // 전체 데이터
94+
Page<User> page = new PageImpl<>(content); // 전체를 Page로 감싸기
95+
```
96+
97+
- 페이지 정보 없이 단순히 리스트만 주어진 경우에 사용하는 간단한 생성자
98+
- 총 개수는 content.size() 로 대체한다.
99+
100+
- 상황별 사용
101+
102+
103+
| 반환타입 | 설명 | 적합한 상황 |
104+
| --- | --- | --- |
105+
| Page<T> | 전체 개수(count(*))와 전체 페이지 수를 함께 제공 | 페이지 번호와 전체 페이지 수 표시가 필요한 페이징 (예: 게시판) |
106+
| Slice<T> | 다음 페이지 유무만 판단 (count(*)는 실행하지 않음) | 무한 스크롤, 성능이 중요한 경우(ex. 모바일 피드) |
107+
- 단점
108+
- Slice는 오프셋이 큰 경우에 DB 성능이 저하될 가능성이 있다.
109+
- PageCOUNT 쿼리 비용이 클 수 있다.
110+
111+
112+
- Java stream API
113+
114+
Stream API:
115+
람다식을 이용한 기술 중에 하나로 데이터 소스를 조작 및 가공, 변환하여
116+
원하는 값으로 반환해주는 인터페이스이다.
117+
118+
java.util.stream에 있다.
119+
120+
배열의 각 요소를 개발자가 직접 컬렉션 외부로 꺼내올 필요 없이,
121+
컬렉션 내부에서 어떻게 처리할지만 기술하면 된다.
122+
이러한 특성으로 인해 Streams API는 내부 반복자라고 부른다.
123+
124+
- 람다식이란?
125+
126+
익명 함수라고도 한다.
127+
128+
함수를 하나의 식으로 표현한 인터페이스이다. 메소드의 이름이 없다.
129+
130+
131+
- 특징
132+
- Stream은 원본 데이터를 변경하지 않는다.
133+
- 재사용이 불가능하여서 일회용으로 사용된다.
134+
- 내부 반복으로 작업을 처리한다.
135+
136+
- 과정
137+
- Stream 생성 → 중간 연산 → 최종 연산
138+
- 객체.Stream생성().중간연산.최종연산 ⇒ 이런 식으로 작성
139+
140+
- 중간 연산 종류
141+
-
142+
| Stream 필터 | filter(), distinct() |
143+
| --- | --- |
144+
| Stream 변환 | map(), flatMap() |
145+
| Stream 제한 | limit(), skip() |
146+
| Stream 정렬 | sorted() |
147+
| Stream 연산 결과 확인 | peek() |
148+
149+
- 최종 연산 종류
150+
-
151+
| 요소의 출력 | forEach() |
152+
| --- | --- |
153+
| 요소의 검색 | findFirst(), findAny() |
154+
| 요소의 검사 | anyMatch(), allMatch(), noneMatch() |
155+
| 요소의 통계 | count(), min(), max() |
156+
| 요소의 연산 | sum(), average() |
157+
| 요소의 수집 | collect() |
158+
- 사용예시
159+
- 대량 데이터를 한 번만 반복 처리할 때는 parallelStream이 유리
160+
- 작은 데이터에서는 for문이 빠르다.
161+
162+
```java
163+
void mapping_and_sorting(){
164+
List<String> names = Arrays.asList("John", "Jane", "Tom", "Jerry");
165+
166+
List<String> sortedNames = names.stream()
167+
.map(String::toUpperCase) // 중간 연산: 대문자로 변환
168+
.sorted() // 중간 연산: 알파벳 순으로 정렬
169+
.toList();
170+
171+
assertEquals("JANE", sortedNames.get(0));
172+
assertEquals("JERRY", sortedNames.get(1));
173+
assertEquals("JOHN", sortedNames.get(2));
174+
assertEquals("TOM", sortedNames.get(3));
175+
}
176+
```
177+
178+
- 장단점
179+
- 최종 연산을 누락하는 경우 그 스트림은 작업을 처리하지 않고 무시된다.
180+
- 재사용 스트림 문제
181+
- 다음 코드에서 이미 사용한 스트림을 사용하려고 하면 작동하지 않는다.
182+
- 무한 스트림 생성
183+
- iterate() 연산을 사용하면 무한히 생성될 수 있다.
184+
- 반복문 없이 가독성 높은 코드 작성 가능
185+
- 병렬 처리 가능
186+
- parallelStream() 사용하면 중간 연산 병렬 처리 가능
187+
188+
- 객체 그래프 탐색
189+
190+
객체는 상속, 연관 관계만 맺어져 있다면 자유롭게 그래프를 탐색 가능해야 한다.
191+
192+
JPA는 지연로딩을 통해 객체를 사용하는 시점까지 DB 작업을 미룬다.
193+
194+
- 장단점
195+
- SQL 직접 작성과 다르게 조인의 제약에서 벗어나
196+
논리적인 도메인 모델 구조에 따라 데이터를 조회 가능
197+
- 객체 그래프를 무분별하게 탐색할 경우, 하이버네이트 등에서 N+1 문제 발생 가능
198+
- SQL과 차이
199+
- DB는 지정된 테이블만 조회가 가능, 모든 객체 그래프를 탐색하기 어렵다.
200+
- 자유로운 탐색이 가능하다.
201+
202+
- @Valid vs @Validated
203+
204+
둘 다 유효성 검증을 위한 어노테이션이다.
205+
206+
- @Valid
207+
- Bean Validator를 통해 객체의 데이터 유효성 검증을 지시하는 어노테이션
208+
- 구현체가 따로 없다 ⇒ Hibernate Validator 사용
209+
- 동작 과정
210+
- 요청이 들어오면 Dispatcher Servlet에서
211+
요청에 맞는 컨트롤러에 요청을 전달합니다.
212+
- 전달 과정에서 컨트롤러 메소드의 객체를 전달해주는 HandlerMethodArgumentResolver 동작
213+
@Valid 역시 HandlerMethodArgumentResolver 의해 처리가 됩니다.
214+
215+
**ArgumentResolver : @RequestBody가 붙은 데이터를 Json 포멧으로 변경**
216+
217+
- @**Valid**로 시작하는 어노테이션이 붙어있으면
218+
dispathcher servlet 단에서 검사를 진행하는 식으로 동작
219+
220+
**⇒ 주로 Request Body에서 검증하는데 많이 사용된다.**
221+
222+
- Spring에서 @Valid는 기본적으로 Controller에서만 동작하도록 설계되어 있다
223+
**다른 계층에서 검증하기 위해서는 @Validated와 결합**하여 사용합니다.
224+
225+
- 장단점
226+
- 자바 표준
227+
- 컨트롤러에서만 사용 가능
228+
229+
- @Validation
230+
- Controller 뿐만 아니라 다른 계층에서도 데이터 유효성 검증을 해야하는 경우
231+
메소드 요청을 가로채서 유효성 검증을 해주는 **Spring AOP 기반 기능**
232+
- 유효성 검증이 필요한 클래스에 @Validated를 붙이고,
233+
유효성 검증을 할 파라미터에 @Valid를 붙이면 동작하게 됩니다.
234+
235+
```java
236+
@Service
237+
@Validated
238+
public class Service {
239+
240+
public void doSomething(@Valid Request request) {
241+
...
242+
}
243+
}
244+
```
245+
246+
- Spring의 기능이기 때문에 Bean이면 모두 유효성 검증이 가능하다.
247+
- MethodValidationInterceptorValidator이 의존성 주입으로 들어가므로
248+
@Valid와 똑같이 Hibernate Validator를 사용하기 때문에
249+
Bean Validation을 그대로 이용할 수 있게 됩니다.
250+
251+
- 동작 과정
252+
- @Validated를 클래스에 선언하면 해당 클래스에 유효성 검증을 위한
253+
인터셉터인 MethodValidationInterceptor가 등록됩니다.
254+
- 해당 클래스의 메소드가 호출 될 때 AOP가 확인을 하고
255+
요청을 중간에 가로채어 유효성 검증을 진행하게 됩니다.
256+
- 장단점
257+
- 그룹 검증
258+
- 모든 Bean에서 유효성 검증
259+
- 스프링 환경에서만 가능
260+
261+

mission/chapter06/img_11.png

39.3 KB
Loading

0 commit comments

Comments
 (0)