Skip to content

Commit 58c25b0

Browse files
authored
[백지연] 챕터 12, 13 (#100)
* docs: 챕터 12 추가 * chore: 불필요한 라인 제거 * �docs: 챕터 13 추가 * chore: 내용 수정 * fix: ISR 내용 수정 및 보완
1 parent c12ed45 commit 58c25b0

File tree

2 files changed

+454
-0
lines changed

2 files changed

+454
-0
lines changed

챕터_12/백지연.md

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
# CHAPTER 12 리액트 디자인 패턴
2+
3+
## 리액트에서 널리 사용되는 디자인 패턴
4+
5+
- [고차 컴포넌트 패턴](#고차-컴포넌트-패턴)
6+
- [렌더링 Props 패턴](#렌더링-props-패턴)
7+
- [Hooks 패턴](#hooks-패턴)
8+
- [정적 가져오기](#정적-가져오기)
9+
- [동적 가져오기](#동적-가져오기)
10+
- [코드 스플리팅](#코드-스플리팅)
11+
- [PRPL 패턴](#PRPL-패턴)
12+
- [로딩 우선순위](#로딩-우선순위)
13+
14+
## 고차 컴포넌트 패턴
15+
16+
고차 컴포넌트는 **컴포넌트를 인자로 받아 새로운 컴포넌트를 반환**하는 컴포넌트
17+
즉, 인자로 받은 컴포넌트에 추가 기능을 적용한 컴포넌트를 반환
18+
19+
```javascript
20+
// 컴포넌트를 인자로 받음
21+
function withStyles(Component) {
22+
return (props) => {
23+
const style = { padding: '0.2rem', margin: '1rem' };
24+
return <Component style={style} {...props} />; // 새로운 컴포넌트 반환
25+
};
26+
}
27+
28+
const Button = () => <button>Click me!</button>;
29+
const Text = () => <p>Hello World!</p>;
30+
31+
const StyledButton = withStyles(Button);
32+
const StyledText = withStyles(Text);
33+
```
34+
35+
### 목적
36+
37+
여러 컴포넌트에 동일한 로직을 사용하고 싶을 때 사용
38+
39+
### 활용 예시
40+
41+
특정 스타일 적용, 인증 요구, 전역 상태 추가
42+
43+
### 장점
44+
45+
- 로직을 한 곳에 집중시켜 중복 제거
46+
- 효과적으로 관심사 분리
47+
48+
### 단점
49+
50+
고차 컴포넌트가 대상 컴포넌트에 전달하는 prop의 이름이 일으키는 충돌 → 디버깅과 애플리케이션 확장에 어려움 발생
51+
52+
## 렌더링 Props 패턴
53+
54+
자신의 렌더링 로직을 구현하는 대신, 렌더링 prop을 호출
55+
렌더링 prop의 이름이 `render`일 필요는 없고, JSX를 렌더링하면 렌더링 prop으로 간주
56+
57+
```javascript
58+
<Component render={() => <h1>I am a render prop!</h1>} />
59+
60+
<Component render={data => <ChildComponent data={data} />} />
61+
```
62+
63+
### 장점
64+
65+
고차 컴포넌트 패턴에서 발생할 수 있는 **이름 충돌 문제를 해결** → 어떤 props가 어디에서 오는지 정확하게 파악 가능
66+
67+
### 단점
68+
69+
- 대부분의 경우 렌더링 Props 패턴을 Hooks 패턴으로 대체 가능
70+
- 라이프사이클 관련 메서드를 추가할 수 없음 → 데이터를 변경하지 않는 렌더링에 치중한 컴포넌트에만 사용 가능
71+
72+
### 상태 끌어올리기
73+
74+
A 컴포넌트와 B 컴포넌트가 상태를 공유하기 위해 **가장 가까운 공통 조상 컴포넌트**로 상태를 끌어올리는 것
75+
작은 규모의 애플리케이션에서는 전역 상태 관리 라이브러리 대신 이 패턴 사용하는 것만으로도 충분
76+
하지만 큰 규모라면 상태 끌어올리기가 복잡해지며, 리렌더링으로 인해 성능에 악영향 줄 수 있음
77+
이때 렌더링 Props 패턴을 사용하면 좋음
78+
79+
```javascript
80+
function Input(props) {
81+
const [value, setValue] = useState('');
82+
return (
83+
<>
84+
<input type="text" value={value} onChange={(e) => setValue(e.target.value)} />
85+
// 여기
86+
{props.render(value)}
87+
</>
88+
);
89+
}
90+
91+
export default function App() {
92+
return (
93+
<div className="App">
94+
<h1>Temperature Converter</h1>
95+
<Input
96+
// 여기
97+
render={(value) => (
98+
<>
99+
<Kelvin value={value} />
100+
<Fahrenheit value={value} />
101+
</>
102+
)}
103+
/>
104+
</div>
105+
);
106+
}
107+
```
108+
109+
### 컴포넌트의 자식으로 함수 전달하기
110+
111+
렌더링 Prop으로 전달하는 대신 **컴포넌트의 자식**으로 함수 전달 가능
112+
113+
```javascript
114+
function Input(props) {
115+
const [value, setValue] = useState('');
116+
return (
117+
<>
118+
<input type="text" value={value} onChange={(e) => setValue(e.target.value)} />
119+
// 여기
120+
{props.children(value)}
121+
</>
122+
);
123+
}
124+
125+
export default function App() {
126+
return (
127+
<div className="App">
128+
<h1>Temperature Converter</h1>
129+
<Input>
130+
// 여기
131+
{(value) => (
132+
<>
133+
<Kelvin value={value} />
134+
<Fahrenheit value={value} />
135+
</>
136+
)}
137+
</Input>
138+
</div>
139+
);
140+
}
141+
```
142+
143+
## Hooks 패턴
144+
145+
Hooks를 사용하면 **클래스 컴포넌트를 사용하지 않고도 상태와 라이프사이클 메서드 활용 가능**
146+
147+
### 장점
148+
149+
- 간결한 코드
150+
- 복잡한 컴포넌트의 단순화
151+
- 상태 관련 로직 재사용
152+
- UI에서 분리된 로직 공유
153+
154+
## 정적 가져오기
155+
156+
`import 모듈 from '모듈'`을 사용해 가져오는 모듈은 모두 정적으로 가져온 것
157+
158+
컴포넌트를 정적으로 가져오면 웹팩은 모듈을 초기 번들에 포함시킴
159+
애플리케이션을 빌드한 후에는 웹팩이 생성한 번들 확인 가능
160+
161+
## 동적 가져오기
162+
163+
모듈이 초기 번들에 불필요하게 포함되어 로딩 시간이 증가하는 문제를 해결하기 위해 컴포넌트를 동적으로 가져올 수 있음
164+
컴포넌트를 정적으로 가져오는 대신 실제로 필요한 시점에 맞춰 불러오는 것
165+
리액트의 `Suspense` 컴포넌트로 동적으로 로드할 컴포넌트를 감싸면 된다.
166+
리액트 18부터 SSR 환경에서도 `Suspense` 사용 가능
167+
168+
### 상호작용 시 가져오기
169+
170+
ex. 채팅 애플리케이션에서 사용자가 이모지를 클릭하면 EmojiPicker 컴포넌트를 동적으로 가져오는 상황
171+
172+
### 화면에 보이는 순간 가져오기
173+
174+
IntersectionObserver API를 사용해 컴포넌트가 화면에 보이는지 확인
175+
ex. 사용자가 스크롤해야 화면에 나타나는 컴포넌트
176+
177+
## 코드 스플리팅
178+
179+
복잡한 애플리케이션에서는 적절한 시기에 정적/동적 임포트가 가능하도록 코드를 최적으로 스플리팅하고 번들링해야 함
180+
181+
### 경로 기반 분할
182+
183+
때로는 특정 페이지(경로)에서만 필요한 리소스가 있음
184+
이런 경우 경로별로 컴포넌트를 지연 로딩하면, 현재 경로에 필요한 코드가 포함된 번들만 요청 가능
185+
186+
### 번들 분할
187+
188+
거대한 번들 하나를 요청하는 대신, 여러 개의 작은 번들로 분할하는 방법
189+
190+
#### 장점
191+
192+
- 첫 번째 콘텐츠가 사용자 화면에 표시되는 시간(`FCP`) 단축
193+
- 가장 큰 콘텐츠가 화면에 렌더링되는 시간(`LCP`) 개선
194+
- 모든 콘텐츠가 화면에 표시되고 인터랙티브해지는 데 걸리는 시간(`TTI`) 개선
195+
196+
## PRPL 패턴
197+
198+
저사양 기기나 인터넷 연결이 불안정한 지역에서도 애플리케이션이 원활하게 작동해야 함
199+
어떤 환경에서도 애플리케이션이 최대한 효율적으로 로드될 수 있도록 PRPL 패턴 사용
200+
201+
4가지 핵심 성능 고려사항에 중점
202+
203+
- `Push` : 중요한 리소스를 효율적으로 푸시해 서버 왕복 횟수를 최소화하고 로딩 시간 단축
204+
- `Render` : 초기 경로를 최대한 빠르게 렌더링해 UX 개선
205+
- `Pre-cache` : 자주 방문하는 경로의 에셋을 백그라운드에서 미리 캐싱해 서버 요청 횟수 줄이고 더 나은 오프라인 경험 제공
206+
- `Lazy-load` : 자주 요청되지 않는 경로나 에셋은 지연 로딩
207+
208+
## 로딩 우선순위
209+
210+
`preload`는 브라우저의 최적화 기능으로, **중요한 리소스를 더 일찍 요청** 가능
211+
로딩 순서를 지정하면 Core Web Vitals의 로딩 성능 및 지표에 긍정적인 영향 미침
212+
213+
`TTI` 또는 `FID`를 최적화할 때 유용
214+
상호작용에 필요한 리소스를 먼저 로딩하다가
215+
`FCP`, `LCP`에 필요한 리소스(ex. 폰트, 히어로 이미지)의 로딩이 지연되지 않도록 주의해야 함
216+
217+
자바스크립트 자체의 로딩을 최적화하려면, `<body>` 태그보다는 `<head>` 태그 안에서 `<script defer>`를 사용하는 게 좋음
218+
219+
### SPA의 Preload
220+
221+
#### `prefetching`
222+
223+
- 앞으로 사용될 가능성이 높은 리소스를 미리 가져와 캐시에 저장하는 방식
224+
- 브라우저가 인터넷 연결 상태와 대역폭을 고려해 어떤 리소스 미리 가져올지 결정
225+
226+
#### `preload`
227+
228+
- **즉시** 사용해야 하는 리소스 (ex. 초기 렌더링에 사용되는 특정 폰트나 접속 시 바로 보이는 히어로 이미지 등)
229+
- **어떤 상황에서든 무조건 미리 로드**
230+
- 초기 렌더링 후 약 1초 이내에 표시되어야 하는 리소스만 선별해 미리 로드하는 것이 좋음
231+
232+
### Preload + async 기법
233+
234+
브라우저가 스크립트를 높은 우선순위로 다운로드하면서도, 스크립트를 기다리는 동안 파싱이 멈추지 않도록 하기 위한 기법
235+
236+
```html
237+
<link rel="preload" href="emoji-picker.js" as="script" />
238+
<script src="emoji-picker.js" async></script>
239+
```
240+
241+
### 크롬 95+ 버전에서의 Preload
242+
243+
크롬 95+ 버전에서는 preload의 queue-jumping 동작이 개선되어 preload 기능이 더 안전해짐
244+
245+
> queue-jumping이란?
246+
> 브라우저는 리소스를 요청할 때 queue에 쌓아서 순차적으로 처리합니다.
247+
> 기본적으로 브라우저는 HTML 파일을 읽고, 필요한 스크립트나 스타일시트를 순서대로 로드합니다.
248+
> 하지만 preload를 사용하면, 중요한 리소스를 우선적으로 로드하도록 **큐에서 뛰어넘을 수 있습니다**.
249+
250+
## 리스트 가상화
251+
252+
대규모 데이터 리스트의 렌더링 성능을 향상시키는 기술
253+
현재 화면에 보이는 행만 동적으로 렌더링
254+
`react-virtualized` 같은 라이브러리를 사용하여 구현
255+
스크롤되는 뷰포트 내에서 현재 보이는 항목만 렌더링하므로, 한 번에 수천 개의 행 데이터를 렌더링하는 데 드는 리소스를 절약
256+
257+
### 윈도잉/가상화의 작동 방식
258+
259+
`react-virtualized`에서의 작동 방식
260+
261+
https://github.com/user-attachments/assets/c4afb058-8c99-4adb-8a04-1a99cc89949f
262+
263+
윈도잉이 동작하기 위해 필요한 요소
264+
265+
- 스크롤을 위한 큰 DOM 요소
266+
- `position: relative`를 가지는 작은 컨테이너 DQM 요소 (영상에서 ul에 해당)
267+
- 컨테이너 내부에 위치하고 `position: absolute`이며 `top`, `left`, `width`, `height` 등을 설정한 자식 요소들
268+
269+
[react-window](https://github.com/bvaughn/react-window)[react-virtualized](https://github.com/bvaughn/react-virtualized) 개발자 Brian Vaughn이 다시 만든 라이브러리
270+
smaller, faster, more tree-shakeable, beginner-friendly
271+
272+
> 두 라이브러리가 어떻게 다른지 개발자가 직접 설명한 [](https://github.com/bvaughn/react-window#how-is-react-window-different-from-react-virtualized)이 있네요 (둘 중에는 react-window를 사용하라고 권장하고 있습니다)
273+
>
274+
> 토스증권에서는 `react-virtuoso`, `@tanstack/react-virtual`을 상황에 맞게 사용 중이라고 해요
275+
> `react-virtuoso`는 선언적이고, `@tanstack/react-virtual`은 headless라서 커스텀 하기 좋다고 합니다
276+
>
277+
> https://npmtrends.com/@tanstack/react-virtual-vs-react-virtualized-vs-react-virtuoso-vs-react-window
278+
> 4가지를 같이 비교해 보니, 확실히 `react-virtuoso`, `@tanstack/react-virtual`이 가볍긴 하네요
279+
> ![image](https://github.com/user-attachments/assets/8b6eeeb7-e507-4119-b22c-6c79f624bf8f)
280+
281+
### 웹 플랫폼의 발전
282+
283+
`content-visibility: auto`를 설정하면 화면 밖 콘텐츠의 렌더링과 페인팅을 필요한 시점까지 지연할 수 있음
284+
렌더링 비용이 큰 HTML 문서에 적용하면 좋음
285+
동적인 콘텐츠 목록을 렌더링하는 경우에는 `react-window` 같은 라이브러리 사용하는 게 좋음
286+
287+
> https://developer.mozilla.org/en-US/docs/Web/CSS/content-visibility
288+
> [(번역) CSS content-visibility를 이용해 렌더링 성능 향상 시키기](https://velog.io/@superlipbalm/improving-rendering-performance-with-css-content-visibility)

0 commit comments

Comments
 (0)