112 |
--------------------------------------------------------------------------------
/김석진/01_리액트 개발을 위해 꼭 알아야 할 자바스크립트.md:
--------------------------------------------------------------------------------
1 | ## 원시 타입
2 |
3 | - **boolean**
4 | - 조건문에서 false라고 취급되는 값을 falsy, true라고 취급되는 값을 truthy라고 한다.
5 | - **null**
6 | - 명시적으로 비어 있음을 나타내는 값이다.
7 | - typeof로 null을 확인했을 때 해당 타입이 아닌 'object'가 반환된다.
8 | - **undefined**
9 | - 선언됐지만 할당되지 않은 값이다.
10 | - **number**
11 | - 각 진수별로 값을 표현해도 모두 10진수로 해석되어 동일한 값으로 표시된다.
12 | - **string**
13 | - 원시 타입이며 변경이 불가능하다.
14 | - 템플릿 리터럴을 사용하면 줄바꿈이 간으하고, 문자열 내부에 표현식을 사용 할 수 있다.
15 | - **symbol**
16 | - 중복되지 않는 어떠한 고유한 값을 나타내기 위해 만들어졌다.
17 | - 심벌을 생성하려면 반드시 `Symbol()`을 사용한다.
18 | - **bigint**
19 | - number가 다룰 수 있는 한계를 넘어서 더 큰 숫자를 저장 할 수 있게 해준다.
20 | - 끝에 n을 붙이거나, `BigInt` 함수를 사용하여 사용 할 수 있다.
21 |
22 | ## 객체 타입
23 |
24 | 배열, 함수, 정규식, 클래스 등.. 원시 타입 이외의 모든것, 자바스크립트를 이루고 있는 대부분의 타입이 객체 타입이다.
25 |
26 | ```tsx
27 | function hello1() {}
28 | function hello2() {}
29 |
30 | hello1 === hello2; // false
31 | // 객체는 육안으로는 같아 보여도 참조가 다르기 때문에 false가 반환된다.
32 |
33 | let hello = {
34 | hi: "hi",
35 | };
36 |
37 | let hi = {
38 | hi: "hi",
39 | };
40 |
41 | hello === hi; // false
42 | // 객체는 값을 저장하는게 아니라 참조를 저장하기 때문에 동일하게 선헌했던 객체라 하더라도 저장하는 순간 다른 참조를 바라보기 떄문에 false를 반환한다.
43 | ```
44 |
45 | ## 자바스크립트의 또 다른 비교 공식, `Object.is`
46 |
47 | `Object.is`는 `==`과 `===`가 만족하지 못하는 몇 가지 특이한 케이스를 추가하기 위해 생겨난 ES6 최신 문법이다.
48 | 하지만 `Object.is`를 사용하더라도 객체 비교에는 `===`과 똑같이 작동한다.
49 |
50 | ```tsx
51 | Object.is({}, {}); // true
52 |
53 | Number.NaN === NaN; // false
54 | Object.is(Number.NaN, NaN); // true
55 |
56 | NaN === 0 / 0; // false
57 | Object.is(NaN, 0 / 0); // true
58 | ```
59 |
60 | ## 리액트에서의 동등 비교
61 |
62 | 리액트에서는 `objectIs`를 기반으로 동등 비교를 하는 `shallowEqual`이라는 함수를 만들어 사용한다.
63 | `shallowEqual`은 얕은 비교까지만 구현하였는데 이유는 JSX props는 객체이고 props는 일차적으로만 비교하면 되기 때문이다.
64 |
65 | [shallowEqual 코드 바로가기](https://github.com/facebook/react/blob/8e2bde6f2751aa6335f3cef488c05c3ea08e074a/packages/shared/shallowEqual.js)
66 |
67 | ## 함수 표현식
68 |
69 | ```tsx
70 | const sum = function add(a, b) {
71 | return a + b;
72 | };
73 |
74 | sum(10, 14); // 24
75 | add(10, 14); // 에러
76 | ```
77 |
78 | ### 일급 객체란?
79 |
80 | 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 의미한다.
81 |
82 | 자바스크립트에서 함수는 일급 객체다. 함수는 다른 함수의 매개변수가 될 수도 있고, 반환 값이 될 수도 있으며, 앞에서 본 것 처럼 할당도 가능하므로 일급 객체가 되기 위한 조건을 모두 갖추고 있다.
83 |
84 | add를 부르면 에러가 나는 것을 본다면 알 수 있듯이 함수 표현식에서 함수에 이름을 주는 것은 함수 호출에 도움이 안 되는, 코드를 읽는 데 방해가 될 수 있는 요소이다.
85 |
86 | ## 함수 표현식과 선언 식의 차이
87 |
88 | 함수 표현식과 선언 식의 가장 큰 차이는 호이스팅이다.
89 |
90 | ### 호이스팅이란?
91 |
92 | 함수 선언문이 마치 코드 맨 앞단에 작성된 것처럼 작동하는 자바스크립트의 특징을 의미한다.
93 |
94 | ```tsx
95 | hello()
96 |
97 | funtion hello() {
98 | console.log('hello')
99 | }
100 |
101 | hello()
102 | ```
103 |
104 | 함수를 선언한 hello가 중간에 있음에도 불구하고, 맨 앞에서 호출한 `hello()`는 어떠한 에러도 없다.
105 | 이유는 함수의 호이스팅은 함수에 대한 선언을 실행 전에 미리 메모리에 등록하는 작업을 의미한다.
106 | 이러한 호이스팅의 특징 덕분에 함수 선언문이 미리 메모리에 등록됐고, 순서에 상관없이 정상적으로 호출할 수 있게 된 것이다.
107 |
108 | ## 화살표 함수 vs 일반 함수
109 |
110 | 화살표 함수에서는 constructor를 사용할 수 없다.
111 | 생성자로 화살표 함수를 사용하는 것은 불가능하다.
112 | 화살표 함수에서는 arguments가 존재하지 않는다.
113 |
114 | 화살표 함수와 일반 함수에 가장 큰 차이점은 this 바인딩이다.
115 | 화살표 함수는 함수 자체의 바인딩을 갖고 있지 않는다. 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 따르게 된다.
116 |
117 | ## 즉시 실행 함수
118 |
119 | 함수를 정의하는 즉시 실행되는 함수이다.
120 |
121 | ```tsx
122 | (function () {
123 | return a + b;
124 | })(10, 24);
125 |
126 | (() => {
127 | return a + b;
128 | })(10, 24);
129 | ```
130 |
131 | ## 고차 함수
132 |
133 | 자바스크립트가 일급 객체라는 특징을 활용한 함수이다.
134 | 함수를 인수로 받거나 결과로 새로운 함수를 반환시킬 수 있다.
135 |
136 | ```tsx
137 | const doubledArray = [1, 2, 3].map(() => item * 2);
138 |
139 | doubledArray; // [2, 4, 6]
140 | ```
141 |
142 | ## 함수 부수 효과(side-effect)를 최대한 억제하라
143 |
144 | ### 가능한 한 함수를 작게 만들어라
145 |
146 | ### 누구나 이해할 수 있는 이름을 붙여라
147 |
148 | ```tsx
149 | useEffect(function apiRequest() {
150 | // .. do somthing
151 | });
152 | ```
153 |
154 | 위와 같이 useEffect 혹은 useCallback에 이름을 붙여준다면 개발자 경험이 향상될 수 있을 것이다.
155 |
156 | ## 클래스
157 |
158 | ```tsx
159 | const myCar = new Car("자동차");
160 |
161 | Object.getPrototypeOf(myCar);
162 | // getPrototypeOf를 사용하면, 인수로 넘겨준 변수의 prototype을 확인할 수 있다.
163 |
164 | Object.getPrototypeOf(myCar) === Car.prototype; // true
165 | myCar.__proto__ === Car.prototype; // true
166 | // __proto__는 getPrototypeOf와 동일하게 작동하지만, 가급적이면 getPrototypeOf를 사용하면 좋다.
167 | ```
168 |
169 | ## 클로저의 정의
170 |
171 | 클로저는 어휘적 환경을 조합해 코딩하는 기법이다.
172 |
173 | ### 어휘적 환경이란?
174 |
175 | 변수가 코드 내부에서 어디서 선언됐는지를 말하는 것이다.
176 |
177 | ## 변수의 유효 범위, 스코프
178 |
179 | 클로저를 이해하기 위해서는 변수의 유효 범위에 따라서 어휘적 환경이 결정된다.
180 | 이러한 유효 범위를 스코프라고 한다.
181 |
182 | ### 전역 스코프
183 |
184 | 전역 레벨에 선언하는 것을 전역 스코프라고 한다.
185 | 브라우저 환경에서 전역 객체는 Window, Node.js 환경에서는 global이 있다.
186 |
187 | ### 함수 스코프
188 |
189 | 자바스크립트는 기본적으로 함수 레벨 스코프를 따른다. 즉, {} 블록이 스코프 범위를 결정하지 않는다.
190 |
191 | ## 리액트에서의 클로저
192 |
193 | 클로저의 원리를 사용하고 있는 대표적인 것 중 하나가 바로 useState이다.
194 |
195 | ```tsx
196 | function Component() {
197 | const [state, setState] = useState();
198 |
199 | function handleClick() {
200 | // useState 호출은 위에서 끝났지만,
201 | // setState는 계속 내부의 최신값(prev)을 알고 있다.
202 | // 이는 클로저를 활용했기 때문에 가능하다.
203 | setState((prev) => prev + 1);
204 | }
205 | }
206 | ```
207 |
208 | ## 클로저를 사용 할 떄 주의 할 점
209 |
210 | - 클로저에 꼭 필요한 작업만 남겨두지 않는다면 메모리를 불필요하게 잡아먹는 결과를 이야기 할 수 있다.
211 | - 클로즈 사용을 적절한 스코프로 가둬두지 않는다면 성능에 악영향을 미친다.
212 |
213 | ## 이벤트 루프와 비동기 통신의 이해
214 |
215 | 자바스크립트는 싱글 스레드에서 작동한다. 즉, 기본적으로 자바스크립트는 한 번에 하나의 작업만 동기 방식으로만 처리할 수 있다.
216 |
217 | ### 동기란?
218 |
219 | 직렬 방식으로 작업을 처리하는 것을 의미하며, 이 요청이 시작된 이후에는 무조건 응답을 받은 이후에야 비로소 다른 작업을 처리 할 수 있다. 그동안 다른 모든 작업은 대기한다. 이러한 방식은 한번에 다양한 작업을 처리 할 수 없다.
220 |
221 | ### 비동기란?
222 |
223 | 직렬 방식이 아니라 병렬 방식으로 처리하는 것을 의미한다. 요청을 시작한 후 이 응당이 오건 말건 상관없이 다음 작업이 이루어지며, 따라서 한번에 여러 작업이 실행될 수 있다.
224 |
225 | ## 이벤트 루프란?
226 |
227 | 이벤트 루프란 자바스크립트 런타임 외부에서 자바스크립트의 비동기 실행을 돕기 위해 만들어진 장치라고 볼 수 있다.
228 |
229 | ### 호출 스택
230 |
231 | 호출 스택은 자바스크립트에서 수행해야 할 코드나 함수를 순차적으로 담아두는 스택이다.
232 | 코드를 실행하는 작업과 호출 스택이 비어있는지 확인하는 작업은 모두 단일 스레드에서 실행된다.
233 |
234 | ### 태스크 큐
235 |
236 | 태스크 큐란 실행해야 할 태스크의 집합을 의미한다.
237 | 이벤트 루프는 이러한 태스크 큐를 한 개 이상 가지고 있다.
238 | 큐의 형태가 아닌 set의 형태를 띠고 있다.
239 |
240 | ### 마이크로 태스크 큐
241 |
242 | 기존의 태스크 큐와는 다른 태스크를 처리한다.
243 | 마이크로 태스크 큐에는 대표적으로 Promise가 있다.
244 | 마이크로 태스크 큐는 태스크 큐보다 우선권을 갖는다.
245 |
246 | ## 리액트에서 자주 사용하는 자바스크립트 문법
247 |
248 | ### 객체 구조 분해 할당
249 |
250 | ### 전개 구문
251 |
252 | ### 객체 초기자
253 |
254 | ### map
255 |
256 | ### filter
257 |
258 | 기존 배열에 대해 어떠한 조건을 만족하는 새로운 배열을 반환할 때 쓰인다.
259 |
260 | ```tsx
261 | const arr = [1, 2, 3, 4, 5];
262 | const evenArr = arr.filter((item) => item % 2 === 0);
263 | ```
264 |
265 | ### reduce
266 |
267 | reducer 콜백 함수를 실행하고, 이를 초깃값을 추가로 인수를 받는데, 이 초깃값에 따라 배열이나 객체, 또는 그 외의 다른 무언가를 반환할 수 있는 메서스다.
268 |
269 | ```tsx
270 | const arr = [1, 2, 3, 4, 5];
271 | const evenArr = arr.reduce((result, item) => {
272 | return result + item;
273 | }, 0); // result는 0
274 | // 15
275 | ```
276 |
277 | ### forEach
278 |
279 | ### 삼항 조건 연산자
280 |
281 | ### JSX 내부에서 삼항 연산자 말고 다른 조건부 렌더링을 하는 법
282 |
283 | ```tsx
284 | export default function Component() {
285 | const [color, setColor] = useState("");
286 | return (
287 |
296 | );
297 | }
298 | ```
299 |
300 | ## 타입스크립트란?
301 |
302 | 기존 자바스크립트 문법에 타입을 가미한 것이다.
303 | 자바스크립트는 동적 타입의 언어이기 떄문에 대부분의 에러를 코드를 실행했을 때만 확인할 수 있다는 문제점이 있다.
304 |
305 | ### any 대신 unknown을 사용하자
306 |
307 | any는 타입스크립트의 이점을 모두 버리는 것이나 다름없다.
308 |
309 | unknown으로 선언된 변수를 사용하기 우해서는 type narrowing, 즉 타입을 원래 의도했던 대로 적절히 좁혀야 한다.
310 |
311 | ```tsx
312 | function doSomething(callback: unknown) {
313 | if (typeof callback === "fucntion") {
314 | callback();
315 | }
316 |
317 | throw new Error("callback은 함수여야 합니다.");
318 | }
319 | ```
320 |
321 | ### 타입 가드를 적극 활용하자
322 |
323 | 타입을 사용하는 쪽에서는 최대한 타입을 좁히는 것이 좋다.
324 | 이러한 타입을 좁히는 데 도움을 주는 것이 바로 타입 가드다.
325 |
326 | ## 제네릭
327 |
328 | 제네릭은 함수나 클래스 내부에서 단일 타입이 아닌 다양한 타입에 대응할 수 있도록 도와주는 도구다.
329 |
330 | ```tsx
331 | function getFirstAndLast(list: T[]): [T, T] {
332 | return [list[0], list[list.length - 1]];
333 | }
334 | ```
335 |
336 | ### 인덱스 시그니처
337 |
338 | 인덱스 시그니처란 객체의 키를 정의하는 방식을 의미한다.
339 |
340 | ```tsx
341 | // 1
342 | type Hello = {
343 | [key: string]: string;
344 | };
345 |
346 | const hello: Hello = {
347 | hello: "hello",
348 | hi: "hi",
349 | };
350 |
351 | // 2
352 | type Hello = Record<"hello" | "hi", string>;
353 |
354 | const hello: Hello = {
355 | hello: "hello",
356 | hi: "hi",
357 | };
358 | ```
359 |
360 | ## 타입스크립트 전환 가이드
361 |
362 | ### tsconfig.json 먼저 작성하기
363 |
364 | ### JSDoc과 @ts-check를 활용해 점진적으로 전환하기
365 |
366 | 자바스크립트에서 타입스크립트로 전환하지 않고 타입을 체크하는 방법이 있다.
367 | 먼저 파일 최상단에 //@ts-check를 선언하고, JSDoc를 활용해 변수나 함수에 타입을 제공하면 타입스크립트 컴파일러가 자바스크립트 파일의 타입을 확인한다.
368 |
369 | ### 타입 기반 라이브러리 사용을 위해 @types 모듈 설치하기
370 |
371 | ### 파일 단위로 조금씩 전환하기
372 |
--------------------------------------------------------------------------------
/김석진/03_리액트 훅 깊게 살펴보기.md:
--------------------------------------------------------------------------------
1 | ## useState
2 | - 함수형 컴포넌트 내부에서 상태를 정의하고, 이 상태를 관리할 수 있게 해주는 훅이다.
3 |
4 | ```tsx
5 | const [state, setState] = useState(initialState);
6 | ```
7 | - useState의 인수로서 아무런 값을 넘겨주지 않으면 초깃값은 undefined이다.
8 | - 함수의 실행이 끝났음에도 함수가 선언된 환경을 기억할 수 있는 방법은 클로저 덕분이다. 클로저 내부에 useState와 관련된 정보를 저장해 두고, 필요할 떄마다 꺼내놓는 형식으로 구성돼 있다.
9 |
10 | ## 게으른 초기화
11 | - useState에 변수 대신 함수를 넘기는 것을 게으른 초기화(lazy initialization)라고 한다.
12 | - 인수에 함수를 넣으면 state가 처음 만들어질때만 실행되고 이후 리렌더링시에는 실행되지 않는다.
13 | - 게으른 초기화를 사용하여 무거운 연산이 요구되는 작업에 최적화 작업을 할 수 있다.
14 |
15 | ## useEffect
16 | - useEffect는 애플리케이션 내 컴포넌트의 여러 값들을 활용해 동기적으로 부수 효과를 만드는 함수이다.
17 | - useEffect는 생명주기 메서드를 대체하기 위해 만들어진 훅이 아니다.
18 | - useEffect에 첫번째 인수로는 부수 효과가 포함된 함수 두번째 인수에는 의존성 배열을 전달한다.
19 | - 함수형 컴포넌트는 매번 함수를 실행해 렌더링을 수행한다는 특징 덕분에 useEffect가 의존성 배열을 변경된 것을 알고 실행될 수 있는 것이다.
20 |
21 | ## 클린업 함수의 목적
22 | - 클린업 함수는 이벤트를 등록하고 지울 때 사용해야 한다고 알려져 있다.
23 | - 클린업 함수는 이전 state를 참조해 실행된다.
24 | - 클린업 함수는 함수형 컴포넌트가 리렌더링됐을 떄 의존성 변화가 있었을 당시 이전의 값을 기준으로 실행되는, 말 그대로 이전 상태를 청소해 주는 개념으로 보는 것이 좋다.
25 |
26 | ## 의존성 배열
27 | - 의존성 배열이 없는 useEffect는 매 렌더링마다 실행된다면 그냥 useEffect 없이 써도 되는거 아닌가? -> 두개는 명백한 차이점이 존재.
28 |
29 | - 서버 사이드 렌더링 관점에서 useEffect는 클라이언트 사이드에서 실행되는 것을 보장해 준다. useEffect 내부에서는 window 객체의 접근에 의존하는 코드를 사용해도 된다.
30 | - useEffect는 컴포넌트 렌더링의 부수 효과, 즉 컴포넌트의 렌더링이 완료된 이후에 실행된다. 반면 직접 실행은 컴포넌트가 렌더링되는 도중에 실행된다. 따라서 직접 실행은 서버 사이드 렌더링의 경우에 서버에서도 실행된다. 그리고 이 작업은 함수형 컴포넌트의 반환을 지연시키는 행위이기 때문에 무거운 작업일 경우 렌더링을 방해하므로 성능에 악영향을 미칠 수 있다.
31 |
32 | ## useEffect를 사용할 떄 주의할 점
33 | - eslint-disable-line react-hooks/exhaustive-deps 주석은 최대한 자제하라
34 | - useEffect의 첫 번째 인수에 함수명을 부여하라
35 | - 변수명을 통해 useEffect가 어떠한 역할을 하는지 파악 할 수 있다.
36 | - 거대한 useEffect를 만들지 마라
37 | - 만약 큰 useEffect를 만들어야한다면 분리해라
38 | - 의존성 배열이 너무 거대해진다면최대한 useCallback, useMemo 등으로 사전에 정제한 내용들만 useEffect에 담아두는 것이 좋다.
39 | - 불필요한 외부 함수를 만들지 마라
40 |
41 | ## useMemo
42 | - useMemo는 비용이 큰 연산에 대한 결과를 저장해 두고, 이 저장된 값을 반환하는 훅이다.
43 |
44 | ## useCallback
45 | - useCallback은 인수로 넘겨받은 콜백 자체를 기억하는 훅이다. 즉, 콜백 함수를 재생성하지 않고 재사용한다는 의미이다.
46 | - 첫 번째 인수로 함수를, 두 번째 인수로 의존성 배열을 집어 넣으면 useMemo와 마찬가지로 의존성 배열이 변경되지 않는 한 함수를 재생성하지 않는다.
47 |
48 | ## useMemo와 useCallback의 차이
49 | - useMemo와 useCallback의 차이는 메모이제이션을 하는 대상이 변수냐 함수냐 차이이다.
50 |
51 | ## useRef
52 | - useRef는 useState와 동일하게 컴포넌트 내부에서 렌더링이 일어나도 변경 가능한 상태값을 저장한다는 공통점이 있다.
53 | - useRef는 반환값인 객체 내부에 있는 current로 값에 접근 또는 변경할 수 있다.
54 | - useRef는 그 값이 변하더라도 렌더링이 발생시키지 않는다.
55 | - useRef는 컴포넌트가 렌더링될 떄만 생성되며, 컴포넌트 인스턴스가 여러 개라도 각각 별개의 값을 바라본다.
56 |
57 | ## useContext
58 | - props drilling을 극복하기 위해 등장한 개념이 Context이다. Context를 사용하면 이러한 명시적인 props 전달 없이도 선언한 하위 컴포넌트 모두에서 자유롭게 원하는 값을 사용할 수 있다.
59 |
60 | ## useContext를 사용할 때 주의할 점
61 | - useContext를 함수형 컴포넌트 내부에서 사용하면 재홞용이 어려워진다.
62 | - useContext가 선언돼 있으면 Provider에 의존성을 가지고 있는 셈잉 되므로 아무데서나 재활용하기에는 어려운 컴포넌트가 된다.
63 | - useContext는 부모 컴포넌트가 리렌더링되면 하위 컴포넌트도 전부 리렌더링된다.
64 | - 하위 컴포넌트가 리렌더링되지 않게 막으려면 React.memo를 써야한다.
65 |
66 | ## useContext는 상태 관리를 위한 API가 아닙니다
67 | - useContext는 상태를 주입해 주는 API다.
68 |
69 | 상태 관리 라이브러리가 디기 위서는 다음 두 가지 조건을 만족해야 한다.
70 | - 어떠한 상태를 기반으로 다른 상태를 만들어 낼 수 있어야 한다.
71 | - 필요에 따라 이러한 상태 변화를 최적화할 수 있어야 한다.
72 |
73 | ## useReducer
74 | - useReducer는 useState의 심화 버전으로 볼 수 있다. useState와 비슷한 형태를 띠지만 조금 더 복잡한 상태값을 미리 정의해 놓은 시나리오에 따라 관리할 수 있다.
75 |
76 | ## useImperativeHandle
77 | - useImperativeHandle은 부모에게서 넘겨받은 ref를 원하는 대로 수정할 수 있는 훅이다.
78 |
79 | [useImperativeHandle 리액트 공식 문서](https://react.dev/reference/react/useImperativeHandle)
80 |
81 | ## useLayoutEffect
82 | - useEffect와 동일하나, 모든 DOM의 변경 후에 동기적으로 발생하는 훅이다.
83 | - 동일하다는 것은 형태나 사용 예제가 동일하는 것을 의미한다.
84 |
85 | ### 실행 순서
86 | 1. 리액트가 DOM을 업데이트
87 | 2. useLayoutEffect를 실행
88 | 3. 브라우저에 변경 사항을 반영
89 | 4. useEffect를 실행
90 |
91 | [useLayoutEffect 블로그](https://junghyeonsu.com/posts/translation/useeffect-vs-uselayouteffect/)
92 |
93 | ## 훅의 규칙
94 |
95 | - 최상위에서만 훅을 호출해야 한다.
96 | - 반복문이나 조건문, 중첩된 함수 내에서 훅을 실행할 수 없다.
97 | - 이 규칙을 따라야만 컴포넌트가 렌더링될 때마다 항상 동일한 순서로 훅이 호출되는 것을 보장할 수 있다.
98 | - 훅을 호출할 수 있는 것은 리액트 함수 컴포넌트, 혹은 사용자 정의 훅의 두가지 경우 뿐이다.
99 | - 일반 자바스크립트 함수에서는 훅을 사용할 수 없다.
100 | - 이러한 규칙들은 react-hooks/rules-of-hook에 존재한다.
101 |
102 | ## 사용자 정의 훅과 고차 컴포넌트 중 무엇을 써야 할까?
103 |
104 | ### 사용자 정의 훅
105 | - 서로 다른 컴포넌트 내부에서 같은 로직을 공유하고자 할 때 주로 사용되는 것이 바로 사용자 정의 훅이다.
106 | - 복잡하고 반복되는 로직은 사용자 정의 훅으로 간단하게 만들 수 있다.
107 |
108 | ### 고차 컴포넌트
109 | - 고차 컴포넌트는 컴포넌트 자체의 로직을 재사용하기 위한 방법이다.
110 | - 리액트에서는 고차 컴포넌트 기법으로 다양한 최적화나 중복 로직 관리를 할 수 있다. 리액트에서 가장 유명한 고차 컴포넌트는 React.memo
111 |
112 | ### 고차 함수 만들어보기
113 |
114 | ```tsx
115 | function add(a) {
116 | return function (b) {
117 | return a + b
118 | }
119 | }
120 |
121 | const result = add(3) // 클로저에 기억
122 | const result2 = result(5) // 클로저에 기억된 정보를 가지고 더한 값이 출력됨 >> 8
123 | ```
--------------------------------------------------------------------------------
/김석진/04_서버 사이드 렌더링.md:
--------------------------------------------------------------------------------
1 | ## 서버 사이드 렌더링이란?
2 |
3 | **서버 사이드 렌더링(SSR)**은 웹 애플리케이션의 콘텐츠를 서버에서 렌더링하는 기술입니다.
4 |
5 | ## 싱글 페이지 애플리케이션이란?
6 |
7 | ### 과거의 도전과 SPA의 등장
8 |
9 | - 과거에는 자바스크립트로 수행할 수 있는 작업이 제한적이었기 때문에 대부분의 로직이 서버에서 처리되었습니다.
10 | - 이로 인해 애플리케이션 확장과 서버 유지보수가 어려워지는 문제가 발생했습니다.
11 |
12 | ### SPA의 등장
13 |
14 | - 자바스크립트의 진보와 REST API, AJAX의 등장으로 클라이언트 측에서 많은 작업을 수행할 수 있게 되었습니다.
15 | - 이로써 전체 페이지를 다시 요청하지 않고 클라이언트 측에서 라우팅하며 필요한 데이터만 서버에 요청하여 DOM을 업데이트하는 **싱글 페이지 애플리케이션(SPA)**가 등장했습니다.
16 |
17 | ## 다시 부각되는 서버 사이드 렌더링
18 |
19 | ### SSR의 장점
20 |
21 | - 초기 페이지 로딩이 빠릅니다.
22 | - SEO에 유리합니다.
23 | - 서버에서 렌더링을 수행하므로 디바이스 성능에 크게 의존하지 않습니다.
24 |
25 | ### SSR과 CSR 선택 시 고려사항
26 |
27 | - CSR을 적절히 활용하면 훌륭한 애플리케이션을 개발할 수 있습니다.
28 | - 일반적으로 CSR 애플리케이션은 SSR에 비해 성능이 떨어집니다.
29 | - CSR은 SEO에 취약합니다.
30 | - SSR은 서버 부하를 고려해야 합니다.
31 |
32 | ### 결론
33 |
34 | 따라서 어느 하나가 항상 우월한 것은 아니며, 상황에 따라 적절한 기술을 선택해야 합니다.
35 |
36 | ## 리액트에서 제공하는 SSR API
37 |
38 | ### renderToString
39 |
40 | - 주어진 리액트 컴포넌트를 HTML 문자열로 렌더링하는 함수입니다.
41 | - 리액트 훅과 이벤트 핸들러 등은 포함되지 않습니다.
42 |
43 | ```tsx
44 | import { renderToString } from 'react-dom/server';
45 |
46 | const html = renderToString();
47 | console.log(html);
48 | ```
49 |
50 | [자세히 보기](https://react.dev/reference/react-dom/server/renderToString)
51 |
52 | ### renderToStaticMarkup
53 |
54 | - renderToString과 유사하지만 리액트 전용 속성을 생성하지 않고 순수한 HTML 문자열만 반환합니다.
55 |
56 | ```tsx
57 | import { renderToStaticMarkup } from 'react-dom/server';
58 |
59 | const html = renderToStaticMarkup();
60 | ```
61 |
62 | [자세히 보기](https://react.dev/reference/react-dom/server/renderToStaticMarkup)
63 |
64 | ### renderToNodeStream
65 |
66 | - 결과물은 renderToString과 동일하지만 결과물의 타입이 Node.js의 ReadableStream으로 브라우저에서 사용할 수 없습니다.
67 |
68 |
69 | ```tsx
70 | import { renderToStaticNodeStream } from 'react-dom/server';
71 |
72 | const stream = renderToStaticNodeStream();
73 | stream.pipe(response);
74 | ```
75 |
76 | [자세히 보기](https://react.dev/reference/react-dom/server/renderToStaticNodeStream)
77 |
78 | ### renderToStaticNodeStream
79 | - renderToStaticMarkup과 유사하지만 결과물의 타입이 ReadableStream입니다.
80 |
81 |
82 | ```tsx
83 | import { renderToStaticNodeStream } from 'react-dom/server';
84 |
85 | const stream = renderToStaticNodeStream();
86 | ```
87 |
88 | [자세히 보기](https://react.dev/reference/react-dom/server/renderToStaticNodeStream)
89 |
90 | ### hydrate
91 | - renderToString 또는 renderToNodeStream으로 생성된 HTML에 자바스크립트 핸들러와 이벤트를 추가하는 메소드입니다
92 | - 서버에서 제공해 준 HTML과 클라이언트가 렌더링한 결과물이 같을 것으로 예상하고 동작합니다.
93 | - 결과물이 다를 경우 새로 hydrate가 렌더링한 기준으로 웹페이지를 그리게 됩니다.
94 | - 불가피하게 불일치가 발생할 경우 해당 요소에 suppressHydrationWarning을 추가해 경고를 끌 수 있습니다.
95 |
96 | ```tsx
97 | import { hydrate } from 'react-dom';
98 |
99 | hydrate(reactNode, domNode);
100 | ```
101 |
102 | [자세히 보기](https://react.dev/reference/react-dom/hydrate)
103 |
104 | ## NextJs 장점
105 | - 파일 기반 라우팅: App Router와 Page Router를 통해 자동으로 라우트를 생성합니다.
106 | - 자동 정적 최적화: SSR이 필요한지 자동으로 판단하고 필요하지 않은 페이지는 자동으로 SSG로 생성합니다.
107 | - API 라우트: 간단한 API 엔드포인트를 생성하여 백엔드 로직을 쉽게 통합할 수 있습니다.
108 | - 이미지 최적화: next/image를 통해 이미지를 자동으로 최적화하고 성능을 향상시킬 수 있습니다.
109 | - 코드 스플리팅: 필요에 따라 자동으로 코드 스플리팅을 수행하고 링크된 페이지를
110 | 프리페치할 수 있는 기능이 있습니다.
--------------------------------------------------------------------------------
/김석진/05_리액트와 상태 관리 라이브러리:
--------------------------------------------------------------------------------
1 | # 상태와 상태 관리
2 |
3 | ## 상태의 필요성
4 |
5 | - 상태는 애플리케이션에서 지속적으로 변경될 수 있는 값을 나타낸다.
6 | - 웹 애플리케이션에서는 UI, URL, 폼, 서버에서 가져온 값 등 다양한 상태가 존재한다.
7 |
8 | ## 웹 애플리케이션에서의 상태 분류
9 |
10 | - **UI:** 다크/라이트 모드, 각종 Input 값, 알림창의 노출 여부 등
11 | - **URL:** 브라우저에서 관리되는 상태값으로 사용자의 라우팅에 따라 변경됨
12 | - **폼(form):** 로딩 여부, 제출 상태, 값의 유효성 등
13 | - **서버에서 가져온 값:** 서버로 요청을 통해 가져온 데이터
14 |
15 | # Flux 패턴의 등장
16 |
17 | - **MVC 패턴**은 모델과 뷰가 복잡해질수록 복잡성이 증가한다는 문제가 있었다. 모델과 뷰가 서로 간섭하고 양방향 통신을 하면서 코드의 유지보수가 어려워지는 문제가 발생했다.
18 | - 이를 해결하기 위해 **Flux 패턴** 두두등장!
19 |
20 | ## Flux 패턴의 도입
21 |
22 | - Flux 패턴은 단방향 데이터 흐름을 강조하여 데이터의 변화를 추적하기 쉽게 만들어졌다.
23 | - 이 패턴은 페이스북에서 개발한 패턴으로, 데이터의 흐름이 한 방향으로만 흐르도록 하는 아키텍처를 제공한다.
24 |
25 | ## Flux Architecture
26 |
27 | - **Action:** 애플리케이션에서 일어나는 어떤 작업을 나타내는 객체로, 액션 발생 시 함께 전달되는 데이터를 포함한다.
28 |
29 | - **Dispatcher:** 액션을 스토어로 전달하는 역할을 합니다. 액션이 정의한 타입과 데이터를 모두 스토어에 보낸다.
30 |
31 | - **Store:** 애플리케이션의 상태를 저장하고, 상태에 따라 값을 반환하거나 상태를 변경할 수 있는 메서드를 가지고 있다.
32 |
33 | - **View:** 사용자에게 보여지는 부분으로, 스토어에서 가져온 데이터를 이용하여 화면을 렌더링하고 사용자의 입력을 감지하여 액션을 발생시키기도 한다.
34 |
35 | Flux 패턴은 단방향 데이터 흐름으로 데이터를 추적하기 쉽게 만들어주며, 리액트와의 조합이 매우 자연스럽게 이루어지게 된다.
36 |
37 | # 상태 관리 라이브러리의 등장
38 |
39 | ## Redux
40 |
41 | - **Redux**는 하나의 상태 객체를 스토어에 저장하고, 디스패치를 통해 이 객체를 업데이트한다.
42 | - 전역 상태 객체를 사용하여 상태를 하위 컴포넌트에 전파할 수 있으며, 이는 props drilling을 해결하는 데 도움이 된다. 그러나 보일러플레이트의 양이 많아 사용이 번거롭다.
43 |
44 | ## Context API
45 |
46 | - **Context API**는 상태를 주입해주는 기능을 제공한다.
47 | - props를 넘기지 않아도 Context Provider가 주입하는 상태를 사용할 수 있다. 그러나 상태 관리가 아닌 주입을 도와주는 기능이며, 렌더링을 막아주는 기능은 없다.
48 |
49 | ## React Query와 SWR
50 |
51 | - **React Query**와 **SWR**은 외부에서 데이터를 불러오는 데 특화된 라이브러리로, HTTP 요청에 특화된 상태 관리를 제공한다.
52 |
53 | ## useState와 useReducer
54 |
55 | - **useState**의 등장으로 리액트에서는 간단하게 동일한 인터페이스의 상태를 생성하고 관리할 수 있다.
56 | - 하지만 지역 상태의 한계가 존재하며, 컴포넌트마다 초기화되어 컴포넌트별로 다른 상태를 가진다.
57 |
58 | # 상태 관리 라이브러리의 동작 방식
59 |
60 | - 상태 관리 라이브러리는 **useState**와 **useReducer**의 한계를 극복하기 위해 외부에 상태를 둔다.
61 | - 이 외부 상태 변경을 감지하고 컴포넌트의 렌더링을 일으켜 상태를 전파한다.
62 |
63 | # 상태 관리 라이브러리 살펴보기
64 |
65 | ## Recoil
66 |
67 | - **Recoil**은 최상단에 스토어를 만들고, **Atom**이라 불리는 최소 상태 단위를 등록하여 사용한다.
68 | - 컴포넌트는 Recoil에서 제공하는 훅을 통해 Atom의 상태를 구독하고, 값이 변경되면 리렌더링을 실행한다.
69 | - 비동기 작업을 쉽게 처리할 수 있는 API를 제공하며, 별도의 미들웨어 없이도 비동기 작업이 가능하다.
70 |
71 | ## Jotai
72 |
73 | - **Jotai**는 Recoil의 영감을 받아 만들어진 라이브러리로, 불필요한 리렌더링을 최소화하고 메모이제이션을 필요로 하지 않게 설계되었다.
74 | - 별 다른 세팅이 필요없고 사용하기 굉장히 쉽다는 장점이 있다.
75 |
76 | ## Zustand
77 |
78 | - **Zustand**는 하나의 중앙 스토어를 활용해 상태를 관리한다.
79 | - 코드 양이 적고 빠르게 스토어를 생성하고 사용할 수 있는 특징이 있다.
80 | - 리덕스와 함께 사용 가능한 여러 미들웨어를 지원하며, 상태를 간단하게 정의할 수 있어 가볍고 편리하다.
81 |
--------------------------------------------------------------------------------
/김지후/10장.리액트 17,18 변경사항/정리.md:
--------------------------------------------------------------------------------
1 | # 리액트 17 버전 살펴보기
2 |
3 | ### 1. 리액트의 점진적인 업그레이드
4 |
5 | - React 16과 호환 가능
6 | - React 17 애플리케이션 내부에 React 16을 게으르게(lazy) 불러온다. 이 과정에서 React 16을 위한 별도의 루트 요소를 만들고, 여기에 불러온 React 16 모듈을 렌더링하는 구조
7 |
8 | ### 2. 이벤트 위임 방식의 변경
9 |
10 | - document -> 리액트 최상단 요소
11 | 
12 | - 각 이벤트는 해당 리액트 컴포넌트 트리 수준으로 격리되므로 이벤트 버블링으로 인한 혼선을 방지할 수 있다.
13 | - `stopPropagation`를 호출해도 같은 요소에 묶인 다른 이벤트 리스너들은 그대로 호출된다. 따라서 React 16에서 컴포넌트 루트가 아래처럼 나뉘었고, '특정 루트'에 대한 이벤트 전파를 막고 싶을지라도 모든 이벤트가 document에 등록되므로 불가능하다.
14 | ```html
15 |
16 |
17 |
18 |
19 | ```
20 |
21 | ### 3. import React from 'react'가 더이상 필요 없다.
22 |
23 | - 기존에는 JSX 변환을 위해 `import React from 'react'`가 필요했으나 리액트 17부터는 필요없다.
24 | - React 17부터 바벨 변환시 React.createElement가 사라졌고, jsx 변환에 필요한 `react/jsx-runtime` 모듈을 내부적으로 호출한다.
25 | - `npx react-codemod update-react-imports`: 기존 소스코드의 `import React from 'react'` 삭제
26 | - 추후 쓰이지 않게 하려면 ESLint로 강제할 수도 있음
27 | - tsconfig.json의 jsx를 `react-jsx`로 변경하면 해당 방식으로 JSX 변환
28 |
29 | ### 4. 그 밖의 주요 변경 사항
30 |
31 | - 이벤트 풀링 제거
32 | - 이벤트 풀링은 이벤트가 발생할 때마다 새로운 이벤트 객체를 생성하는 대신, 이미 사용된 이벤트 객체를 '풀(pool)'에 저장하고 필요할 때 재사용하는 것을 의미. 이벤트 객체를 재사용하기 위해 사용된 개념
33 | - 해당 시스템에서는 이벤트 핸들러 함수가 호출되고 나면 해당 이벤트 객체의 모든 속성이 null로 설정되는데, 비동기적으로 이벤트 객체에 접근하려고 하면 문제가 발생해 `e.persist()` 같은 처리가 필요했음
34 | - useEffect 클린업 함수의 비동기 실행
35 | - 컴포넌트의 undefined 반환에 대한 일관적인 처리
36 | - 컴포넌트, forwardRef, memo에서 undefined 반환시 에러
37 | - React 18에서는 에러 발생 x
38 |
39 | # 리액트 18 버전 살펴보기
40 |
41 | ### 1. 새로 추가된 훅 살펴보기
42 |
43 | - `useId`: 고유한 키값 생성
44 | - 같은 컴포넌트여도 인스턴스 단위로 다른 값 생성
45 | - 클라이언트와 서버 불일치 해결 (Math.random의 hydration issue)
46 | - `useTransition`: UI의 변경을 가로막지 않고 상태를 업데이트 할 수 있는 함수
47 | - `동시성(concurrency)` 개념이 적용된 훅. 진행 중인 렌더링을 버리고 새로운 상태값으로 다시 렌더링 할 수 있음
48 | - state 업데이트 함수를 감싸서 사용
49 | - `useDeferredValue`: 디바운스와 유사
50 | - state 자체를 감싸서 사용
51 | - 상태를 업데이트할 수 있는 코드에 접근할 수 있다면 `useTransition`, props와 같이 값만 받아와야 하는 상황이라면 `useDeferredValue`
52 | - **`useSyncExternalStore`** -> 중요해 보이네요!!
53 | - 상태관리 라이브러리를 위한 훅
54 | - `동시성 이슈`, 하나의 state 값이 있음에도 서로 다른 값을 기준으로 렌더링되는 `tearing 현상`을 막기 위함
55 | - 리액트에서 관리할 수 없는 외부 데이터 소스에 대한 동시성 처리 추가 필요
56 | - `useSyncExternalStore(callbackToSubscribeExternalState, externalState, defaultValueForServerside)`
57 | - `useInsertionEffect`
58 | - `css-in-js` 라이브러리를 위한 훅
59 | - 실행 순서: 렌더링 -> `useInsersionEffect` -> 레이아웃 계산 -> `useLayoutEffect` -> 페인팅 -> `useEffect`
60 |
61 | ### 2. react-dom/client
62 |
63 | - `createRoot`: reat-dom의 render 메서드를 대체함
64 | - [소스코드](https://react.dev/blog/2022/03/08/react-18-upgrade-guide#updates-to-client-rendering-apis)
65 | - `hydrateRoot`: 서버사이드렌더링을 위한 `hydrate`에서 변경됨
66 | - [소스코드](https://react.dev/reference/react-dom/client/hydrateRoot#usage)
67 |
68 | ### 3. react-dom/server
69 |
70 | - `renderToPipeableStream`
71 | - `hydrateRoot`를 호출하면 서버에서는 HTML을 렌더링하고, 클라이언트의 리액트에서는 여기에 이벤트만 추가하여 첫 번째 로딩을 매우 빠르게 수행한다.
72 | - 이때 `renderToPipeableStream`을 쓰면 최초에 브라우저는 아직 불러오지 못한 데이터 부분을 `Suspense`의 fallback으로 받는다.
73 | - `renderToReadableStream`
74 | - `renderToPipeableStream`는 Node.js 환경, `renderToReadableStream`은 웹 스트림 기반으로 작동. 서버 환경이 아닌 클라우드플레어(Cloudflare)나 디노(Deno) 같은 웹 스트림을 사용하는 모던 엣지 런타임 환경에서 사용되는 메서드.
75 |
76 | ### 4. 자동 배치(Automatic Batchinig)
77 |
78 | - 여러 상태 업데이트를 하나의 리렌더링으로 묶어서 처리
79 | - 루트 컴포넌트를 `createRoot`로 생성하면 동기, 비동기, 이벤트 핸들러 등에 관계 없이 배치 수행됨
80 | - 배치를 피하고 싶은 경우 `flushSync` 사용
81 |
82 | ### 5. 더욱 엄격해진 엄격 모드
83 |
84 | - 더 이상 안전하지 않은 특정 생명주기를 사용하는 컴포넌트에 대한 경고
85 | - 더 이상 componentWillMount, componentWillReceiveProps, componentWillUpdate를 사용할 수 없음
86 | - 문자열 ref 사용 금지
87 | - 문자열이라 참조 파악이 어려운 것과 관련된 문제 발생 가능
88 | - findDOMNode에 대한 경고 출력
89 | - `findDOMNode`: 클래스형 컴포넌트 인스턴스에서 실제 DOM 요소에 대한 참조를 가져오는 데 사용
90 | - 클래스 내부에서 돔을 임의로 조작하면서 '컴포넌트의 렌더링을 위해서는 부모 컴포넌트의 렌더링이 이렁나야 한다'는 리액트의 추상화를 무너뜨림
91 | - 구 Context API 사용 시 발생하는 경고
92 | - childContextAPI, getChildContext
93 | - 예상치 못한 부작용(side-effects) 검사
94 | - strict mode에서 이중 호출되는 경우
95 | - 클래스형 컴포넌트의 constructor, render, shouldComponentUpate, getDerivedStateFromProps
96 | - 클래스형 컴포넌트의 setState의 첫 번째 인수
97 | - 함수형 컴포넌트의 body
98 | - useState, useMemo, useReducer에 전달되는 함수
99 | - console.log에 회색으로 출력됨
100 | - React 18에서 추가된 엄격 모드
101 | - 차후 마운트 해제된 상태에서도 상태값을 유지할 수 있는 기능이 제공될 예정이므로 이를 대비해 useEffect가 두번씩 실행된다.
102 | - 이를 고려해 적절한 cleanup 함수를 배치하는 것이 좋다.
103 |
104 | ### 6. Suspense 기능 강화
105 |
106 | - 수정된 버그
107 | - Suspense 내부에 출력된 컴포넌트가 실제로는 렌더링되지 않았음에도 effect 함수들이 실행되는 이슈
108 | - 서버에서 사용할 수 없는 문제
109 | - fallback 컴포넌트에 자동 스로틀링 지원
110 | - 한계
111 | - React.lazy를 쓰거나 Next.js와 같이 Suspense를 지원하는 프레임워크에서만 사용 가능
112 | - https://react.dev/reference/react/Suspense#usage
113 |
114 | ### 7. 인터넷 익스플로러 지원 중단에 따른 추가 폴리필 필요
115 |
116 | - Promise, Symbol, Object.assign
117 |
--------------------------------------------------------------------------------
/김지후/12장.웹 지표/정리.md:
--------------------------------------------------------------------------------
1 | # 12.2 Core Web Vitals
2 |
3 | 구글에서 제안하는 핵심 웹 지표입니다.
4 |
5 | - 최대 콘텐츠풀 페인트(LCP, Largest Contentful Paint)
6 | - 최초 입력 지연(FID, First Input Delay)
7 | - 누적 레이아웃 이동(CLS, Cumulative Layout Shift)
8 |
9 | 추가로 최초 바이트까지의 시간(TTTB, Time To First Byte), 최초 콘텐츠풀 시간(FCP, First Contentful Paint)도 있습니다.
10 |
11 | # 12.3 최대 콘텐츠풀 페인트(LCP)
12 |
13 | ### LCP, 판단 기준
14 |
15 | LCP는 ‘페이지가 처음으로 로드를 시작한 시점부터 뷰포트 내에서 가장 큰 이미지 또는 텍스트를 렌더링하는 데 걸리는 시간’을 의미합니다. 큰 요소라고 하더라도 뷰포트를 넘어가면 제외됩니다.
16 |
17 | - 2.5초 이내: 좋음
18 | - 4초 이내: 보통
19 | - 그 이상: 나쁨
20 |
21 | 여기서 인식한다는 이미지와 텍스트는 다음과 같이 정의되어 있습니다.
22 |
23 | -
24 | -