From 2f5fa7a47e24fdd623a7c655419d1851fc92c49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EB=8C=80=EC=9C=A4?= <59507527+sensecodevalue@users.noreply.github.com> Date: Wed, 13 Dec 2023 00:02:02 +0900 Subject: [PATCH 1/4] =?UTF-8?q?1=EC=9E=A5=20=EC=A0=95=EB=8C=80=EC=9C=A4=20?= =?UTF-8?q?(#6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\354\240\225\353\214\200\354\234\244.md" | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 "1\354\236\245/\354\240\225\353\214\200\354\234\244.md" diff --git "a/1\354\236\245/\354\240\225\353\214\200\354\234\244.md" "b/1\354\236\245/\354\240\225\353\214\200\354\234\244.md" new file mode 100644 index 0000000..9e23b31 --- /dev/null +++ "b/1\354\236\245/\354\240\225\353\214\200\354\234\244.md" @@ -0,0 +1,134 @@ +# 01장 리액트 개발을 위해 꼭 알아야할 자바스크립트 + +## JS 타입과 동등 비교 + +> 리엑트의 상태들은 동등비교, 얕은 비교를 기반으로 이루어짐 + +**JS의 데이터 타입** + +원시타입 + +- boolean, number, null, undefiend, string, symbol, bigint + +객체 타입 + +- object + +**Falsy** + +false, 0, NaN, ‘’, null, undefiend… -0? + +**Object.is** + +Object.is는 강제 형변환을 하지않고 타입 비교를하며, 두 개의 인수가 동일한지 구분할 수 있다. + +BUT 객체에대해서는 못한다. (기존 === 보다 개선된정도인듯) + +```jsx +Number.NaN === NaN; // false +Object.is(Number.NaN, NaN); // true +Object.is({}, {}); // false +``` + +React에서는 ES6부터 제공되는 Object.is를 통해서 동등비교를 한다. + +추가적으로 React에서는 `shallowEqual`를 만들어 1Depth까지는 비교가 가능할 수 있도록했다. + +왜냐하면 JSX Props는 객체이고 이를 비교만하면 된다고 설계했기때문?! + +```jsx +function Foo() { + return +} + +funtion Bar(props) { + return

{props.hello}

+} +``` + +## 함수 + +함수 선언 방식 + +- 함수 선언문 `function foo() {}` +- 함수 표현식 `const foo = function () {}` + +이두가지는 호이스팅의 차이가 있겠다. + +- new Function `const bar = new Function('a','b','return a + b')` +- Arrow Function `const bar = (a,b) => a + b` + - constructor, argument가 없으며, 선언 당시의 상위 this를 참조한다. + +**함수 주의사항** + +1. **함수의 side effect를 최대한 억제하라.** + 1. side-effect를 처리하는 useEffect는 사용은 최소화해야된다. + 2. 함수의 역할을 좁히고, 컴포넌트의 안정성을 올릴 수 있음 +2. 가능한 함수를 작게 만들어라. + 1. 함수의 원래의 목적인 재사용성에 좋도록 가볍게 만들려고해라. +3. 누구나이해할 수 있는 이름으로 만들어라. + ```jsx + useEffect(function apiRequest() {}, []); // 처럼 명확하게 붙혀보는것도 좋을 것 같다. 나름 꿀팁 + ``` + +## 클래스 + +constructor, property, getter, setter, instance method ,static method + +## 클로저 + +함수형 리액트 컴포넌트의 훅, 의존성 배열, 구조등 주요 기술이 모두 클로저에 의존한다. + +쉽게 코드가 선언된 순간에 정적으로 결정된 환경의 조합 + +```jsx +function foo() { + var x = 1; + function bar() { + console.log(x); // 선언될 당시 랙시컬(어휘전) 환경에 x가 존재함 해서 출력가능 + } + + return bar; +} +foo()(); // 1 +``` + +**스코프 (유효범위)** + +- 전역 스코프: global, window +- 함수 스코프 +- 블록 스코프 + +**`setState((prev) => prev + 1)`** + +react에서는 useState를 보면 클로저를 활용해서 set, get를 반환해서 보호하고, 지속해서 해당 state가 사용될 수 있도록 환경을 유지해줌. + +## 이벤트 루프 + +보통 JS는 싱글스레드 동기방식으로 작동한다. + +이벤트 루프는 이런 JS에서 비동기적인 방식을 처리할 수 있게함. + +어떻게 호출 스택이 비었으면 테스크큐의 실행작업들을 호출스택으로 넣음 + +- 호출 스택 +- 태스크 큐 : setTimeout, interval, immediate +- 마이크로 태스크 큐: process.nextTick, promise, mutationObserver + +> \*requestAnimationFrame: 리패인트 전 호출된다. +> 랜더링은 마이크로 태스크 큐가 끝날때마다 기회를 얻는다. + +## 타입스크립트 + +**any → unknown 사용** + +- 이후 type guard, narrowing등 타입에 맞게 사용할 수 있도록해야됨 + - instanceof, typeof, in 을 활용해보쟈 + +**제네릭** + +다양한 타입에 대응하게 만들 수 있도록도와줌 `useState('')` + +**덕타이핑** + +구조적으로 동일하면 동일한 타입 From ccef1ba5e6a80bb1a4e25a60183ee9d51f6bb9b4 Mon Sep 17 00:00:00 2001 From: eunddodi <87167786+eunddodi@users.noreply.github.com> Date: Fri, 15 Dec 2023 00:06:58 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[=EC=9D=B4=EC=9D=80=EC=A7=80]=201=EC=9E=A5:?= =?UTF-8?q?=20=EB=A6=AC=EC=95=A1=ED=8A=B8=20=EA=B0=9C=EB=B0=9C=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=EA=BC=AD=20=EC=95=8C=EC=95=84=EC=95=BC=20?= =?UTF-8?q?=ED=95=A0=20=EC=9E=90=EB=B0=94=EC=8A=A4=ED=81=AC=EB=A6=BD?= =?UTF-8?q?=ED=8A=B8=20(#9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 1장 막차🚕🚕 * gitignore --------- Co-authored-by: deunb Co-authored-by: hyesungoh --- .gitignore | 1 + .../\354\235\264\354\235\200\354\247\200.md" | 137 ++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 .gitignore create mode 100644 "1\354\236\245/\354\235\264\354\235\200\354\247\200.md" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..496ee2c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git "a/1\354\236\245/\354\235\264\354\235\200\354\247\200.md" "b/1\354\236\245/\354\235\264\354\235\200\354\247\200.md" new file mode 100644 index 0000000..e65d520 --- /dev/null +++ "b/1\354\236\245/\354\235\264\354\235\200\354\247\200.md" @@ -0,0 +1,137 @@ +- ## 1.1 자바스크립트의 동등 비교 + - 자바스크립트만의 객체 비교 방식 + - ==, ===, Object.is 모두 객체 비교시 참조값만을 비교한다. + - 리액트는 동등 비교 시 `shallowEqual` 함수를 사용한다. + - = Object.is + 객체 간 얕은 비교 + - 얕은 비교란? 객체의 첫번째 뎁스에 존재하는 값만을 비교 + - **리액트는 props에서 꺼내온 값을 기준으로 렌더링을 수행하기 때문에, 얕은 비교로 충분** + - 질문 + - 동등 비교와 리액트 렌더링 간의 관계 + - + ``` + Object.is({ hello: 'world'}, { hello: 'world'}) // false + shallowEqual({ hello: 'world'}, { hello: 'world'}) // true + shallowEqual({ hello: { hi: 'world'}}, { hello: { hi: 'world'}}) // false + ``` +- ## 1.2 함수 + - 함수 정의 4가지 방법 + - 함수 선언문과 함수 표현식 + - 표현식은 **값을 산출**하는 구문. 선언은 그 자체로 값을 산출하지 않으므로 선언'문'으로 분류된다. + - 가장 큰 차이는 호이스팅이다. + - 선언문: 선언 자체가 미리 메모리에 등록된다. 따라서 (코드 순서 상)선언 전 호출이 가능하다. + - 표현식: 함수가 변수에 담겨있으므로 변수의 호이스팅 방식을 따른다. 런타임 전에는 undefined로 초기화되고, 할당문이 실행될 때 함수가 할당된다. + - 화살표 함수 + - 일반 함수와의 차이: this 바인딩 + - this를 사용할 수밖에 없는 클래스형 컴포넌트에서 각별한 주의가 필요 + - Function 생성자 + - 함수 작성 시 주의사항 + - 사이드 이펙트를 억제 + - 사이드 이펙트: 함수 내의 작동이 함수 외부에 영향을 끼치는 것 + - 순수함수: 사이드 이펙트가 없고, 언제 어디서나 동일한 인수에 대해 동일한 결과를 반환하는 함수 + - 가능한한 작게 + - @max-lines-per-functions + +- ## 1.3 클래스 + - 자바스크립트의 클래스가 작동하는 방식은 자바스크립트의 프로토타입을 활용하는 것이다. + - 인스턴스 메서드 = 프로토타입 메서드 + - 클래스 내부에서 선언한 메서드는 자바스크립트의 prototype에 선언된다. + - 객체의 프로토타입에 접근하기 + - Object.getPrototypeOf(객체) + - 객체.prototype + - `객체.__proto__` + - 프로토타입 체이닝 + - 직접 객체에서 선언하지 않았음에도 프로토타입에 있는 메서드를 찾아서 실행을 도와주는 것 + - 정적 메서드 + - 인스턴스를 생성하지 않아도 사용할 수 있다는 점 때문에 전역에서 사용하는 유틸 함수를 정적 메서드로 많이 활용하는 편이다. + - <-> 프로토타입 메서드 +- ## 1.4 클로저 + - '클로저'라는 표현이 한 개념에서 비롯된 여러 부산물(?)을 지칭하는데 사용되는 것 같다. (그리고 이게 클로저에 대한 이해를 어렵게 만드는 원인인듯) + - 개념 + - 함수를 생성할 때 생성되는 것. 함수가 선언될 때의 Lexical Scope, 다시 말해 '함수가 선언될 때 어떤 변수들에 접근할 수 있었는가'에 대한 정보를 담고 있다. + - 언어적 특성(현상) + - 외부함수의 호출이 종료되었음에도 내부함수에서 외부함수의 변수에 접근할 수 있는 현상 + - 코드 패턴 + - 외부 함수의 변수에 접근하는 내부 함수를 리턴하는(외부에 전달하는) 코드 패턴 + - 함수 + - 외부 함수의 변수에 접근할 수 있는 내부 함수 자체를 지칭하기도. + - 클로저에 대한 이해를 돕는 아티클 + - [# What is a closure? Example use cases in JavaScript and React](https://dev.to/mattdclarke/what-is-a-closure-example-use-cases-in-javascript-and-react-2e6j) + - A closure is a function enclosing a reference (variable) to its outer scope. ... **The outer function returns an inner function that "closes" over the outer function variable `outerFuncVar`. This is why it is called a closure.** The `outerFunction`, which returns the `innerFunction`, can be called anywhere outside of its scope and the `innerFunction` will have access to, it can remember, the `outerFuncVar`. + - Lexical scoping uses **the location of where a variable is declared in the source code to determine where the variable is available in the source code. Scope is determined at compile time, more specifically lexing time, by the compiler of the JavaScript Engine used to process and execute the code.** The first stage of compilation involves lexing / parsing. Lexing is when the code is converted into tokens, which is part of the process of converting code into machine readable code + - 사용 시 이점 + - 전역 스코프의 사용을 막을 수 있다. + - 개발자가 원하는 정보만 원하는 방향으로 노출시킬 수 있다. + - 리액트에서의 활용: useState + ``` + const [state, setState] = useState() + // useState의 호출은 종료됐지만 내부함수인 setState에서는 외부함수인 useState의 값에 접근할 수 있다. + // setState로 값을 수정할 수 있지만 state값은 useState 내에 숨겨져 있다. + // 덕분에 전역 스코프가 오염되지 않고, 변수값의 접근 및 수정을 통제할 수 있다. + ``` + - 질문 + - '함수형 프로그래밍의 중요한 개념, 부수 효과가 없고 순수해야 한다는 목적을 달성하기 위해 적극적으로 사용되는 개념이다' + - '좋은 함수형 컴포넌트를 만들고, 나아가 함수형 프로그래밍의 패러다임을 이해하려면 클로저에 대해 반드시 알고 있어야 한다.' + - '클로저 사용을 적절한 스코프로 가둬두지 않는다면 성능에 악영향을 미친다' + +- ## 1.5 이벤트 루프와 비동기 통신의 이해 + - 동기와 비동기 + - 동기: A가 끝나야 B를 수행. 직렬. + - 비동기: A 끝나든 말든 B 수행. 병렬. + - 싱글 스레드의 장점 + - 동시성 문제를 고려하지 않아도 된다. + - 이벤트 루프 + - 자바스크립트 런타임 엔진에 존재하는 장치. 자바스크립트의 비동기 실행을 가능하게 한다. + - 자신만의 **단일 스레드**를 가진다. 따라서 **'수행해야할 작업이 있는지 확인하는 것'과 '코드 실행'은 동시에 일어날 수 없다.** + - 콜스택에 수행해야 할 작업이 있다면 자바스크립트 엔진을 이용해 실행한다. 콜스택이 비었다면 태스크 큐에서 대기 중인 작업을 실행한다. + - 이벤트 루프는 한 개 이상의 태스크 큐를 가진다. + - 마이크로 태스크 큐 + - 이벤트 루프는 한 개의 마이크로 태스크 큐를 가진다. + - 기존 태스크 큐보다 우선순위를 가진다. + - 태스크 큐에 포함되는 작업: setTimeout, setInternal, setImmediate + - 마이크로 태스크 큐에 포함되는 작업: process.nextTick, Promises, queueMicroTask, MutationObserver + - 태스크 큐를 실행하기에 앞서 마이크로 태스크 큐를 실행한다. 브라우저 렌더링 작업은 두 큐 사이에서 일어난다. + - 질문 + - '태스크 큐는 자료구조의 큐(queue)가 아니고 set 형태를 띠고 있다. 그 이유는 선택된 큐 중에서 실행 가능한 가장 오래된 태스크를 가져와야 하기 때문이다.' + - 태스크 큐, 마이크로 태스크 큐, 브라우저 렌더링 간 관계 + - '각 마이크로 태스크 큐 작업이 끝날 때마다 한 번씩 렌더링할 기회를 얻게 된다.' +- ## 1.6 리액트에서 자주 사용하는 자바스크립트 문법 + - 구조 분해 할당: 배열, 객체의 구조를 분해해서 선언 없이 바로 변수에 할당한다. + - 커스텀 훅에서 배열을 반환하는 것과 객체를 반환하는 것 간의 차이점 + - '객체 구조 분해 할당은 사용하는 쪽에서 원하는 이름으로 변경하는 것이 번거롭다.' + - *내심 궁금했었던 부분인데 이 문장을 읽고 수긍했다.* + - 계산된 속성 이름 방식 + ``` + const key = 'a' + const { [key]: a } = object + ``` + - Array 프로토타입 메서드 + - reduce + - map, filter의 사용과 트레이드 오프가 있다. + - map, filter: 가독성이 좋다. + - reduce: map, filter를 각각 사용하는 것보다 배열 순회 횟수를 줄일 수 있다. + - forEach + - return, break가 먹히지 않는다. + - forEach 내부의 콜백함수는 무조건 O(n)만큼 실행되므로 최적화 가능 여부를 꼼꼼히 따져볼 것. + - 질문 + - '또 객체 구조 분해 할당을 자주 쓰지 않는다면 꼭 써야 하는지 검토할 필요가 있다.' 87p + - 자주 안 쓰는거랑 무슨 상관? + - 객체 구조 분해 할당, 객체 전개 구문의 트랜스파일 이후에 번들링이 커지는 것까지 고려해서 최적화 해보신 분 있으신지 궁금 +- ## 1.7 타입스크립트 + - 타입 체크를 정적으로 런타임이 아닌 빌드타임에 수행할 수 있게 해준다. + - 타입스크립트의 타입 검사는 값의 형태에 초점을 맞춘다. + - 자바스크립트는 다른 언어에 비해 객체가 열려 있는 구조로 만들어져 있다. 이때문에 덕 타이핑으로 객체를 비교해야 하는 특징이 있다. + - 덕 타이핑이란? 어떤 객체가 필요한 변수와 메서드만 지니고 있다면 해당 타입에 속하도록 인정해주는 것 + - unknown + - top type + - 모든 타입을 할당할 수 있다 + - never + - bottom type + - 코드상으로 존재가 불가능한 타입 + - 타입 가드 + - 타입을 사용하는 쪽에선 타입을 최대한 좁히는 게 좋다. + - instance of, typeof + - try-catch문 에러 핸들링에서 활용하기 + - 제네릭 + - 인덱스 시그니처 + - 키에 원하는 타입을 부여할 수 있다. 동적인 객체를 정의할 때 유용하다. + - 그러나 객체의 키는 동적으로 선언되는 경우를 최대한 지양해야 하고, 객체의 타입도 필요에 따라 좁혀야 함. From 9078eca5be169dce4ed463d9794dfd8821dc4d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EB=8C=80=EC=9C=A4?= <59507527+sensecodevalue@users.noreply.github.com> Date: Mon, 18 Dec 2023 17:43:06 +0900 Subject: [PATCH 3/4] =?UTF-8?q?2=EC=9E=A5=20=EC=A0=95=EB=8C=80=EC=9C=A4=20?= =?UTF-8?q?(#15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\354\240\225\353\214\200\354\234\244.md" | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 "2\354\236\245/\354\240\225\353\214\200\354\234\244.md" diff --git "a/2\354\236\245/\354\240\225\353\214\200\354\234\244.md" "b/2\354\236\245/\354\240\225\353\214\200\354\234\244.md" new file mode 100644 index 0000000..6f396a0 --- /dev/null +++ "b/2\354\236\245/\354\240\225\353\214\200\354\234\244.md" @@ -0,0 +1,240 @@ +## JSX + +> jsx는 트렌스파일을 걷혀 자바스크립트 코드로 변환 과정을 걷힌다. + +- 목적 + - HTML를 js 내부에 표현 + - 트랜스파일러에서 다양한 속성을 가진 트리 구조를 토큰화 esm으로 변환이 주목적 + - JSX내부에 트리 구조로 표현하고 싶은 다양한 것들을 작성하고 트랜스파일링해서 esm으로 변환 +- jsx 컴포넌트 + - JSXElement (E) + - like HTML element + - OpeningE, ClosingE, SlefClosingE, Fragment + - JSXAttrobutes (At) + - JSX에 부여할 수 있는 속성을 의미 + - SpreadAt, At, AtValue(””, ’’, {}, JSXE) + - JSXChildren + - JSX는 트리구조를 나타낸다. 따라서 자식을 갖을 수 있다. + - Text, Element, Fragment, { 표현식 }이 자식일 수 있다. + - JSXStrings + +> React에서는 사용자 컴포넌트를 HTML 요소와 구분하기위해 대문자를 사용 +> 이는 JSX 문법아니다. + +### JSX 트랜스파일링 + +> @babel/plugin-transform-react-jsx 플러그인은 JSX구문을 js가 이해할 수 있는 형태로 변환한다. + +```jsx +const Foo = Hello ; + +// transfiling + +("use strict"); +var Foo = React.createElemen(A, { required: true }, "Hello"); + +// react 17 after +var _jsxRuntime = requrie("custom-jsx-library/jsx-runtime"); +var Foo = (0, _jsxRuntime)(A, { required: true, children: "Hello" }); +``` + +> JSX는 js에 트리구조를 만들 수 있다. +> HTML과 js가 섞여 가독성을 해친다는 의견이 있다. +> 그래서 항상 주의하고 적절한 lint를 사용해보자. + +## VDOM & fiber + +### DOM + +페이지에대한 인터페이스로 콘테츠와 구조에 대한 정보를 담고있다. + +랜더링 과정 + +HTML 다운 → HTML 파싱 **DOM 트리 생성** (css 다운로드) → css 파싱 **CSSOM 생성** → 표현되는 노드만 순회하며 CSS를 적용 → **레이아웃** (노드의 위치,좌표 설정) → **페인팅**(색 및 유효한 모습 그림) ⇒ **Render Tree 생성** + +### 가상 DOM (VDOM) + +> 브라우저가 웹페이지를 render하는 과정은 복잡 + 비용이 많이 든다. +> 사용자와의 상호작용을 하려하고 render 완료이후에도 변경이 되는 경우가 많아졌다. +> 리페인팅은 괜찮지만 **레이아웃은 리페인트를 동반하기에 문제가 되었다.** + +개발자들은 DOM을 관리하고 모든 변경을 추적하는 것보다는 **결과적으로 만들어지는 DOM을 사용하는** 방식을 생각하고 이는 브라우저입장에서도 도움을 준다. + +**VDOM은 빠르다?!** + +VDOM은 일반 DOM사용 보다 빠르다가 아닌 APP을 만들 정도로 충분히빠르다가 맞는말이다. + +충분한 속도에 여러 이점이 있음으로 채용한다가 맞는말인듯.. + +### 가상DOM 처리 = React FIber + +> 가상 DOM과 렌더링 과정 최적화해주는 Fiber +> 재조정자 = reconcier + +Fiber는 react의 작업 단위이며, component와 1:1 매칭되어 정보를 갖고 있는다. + +Fiber Reconcier는 가상DOM과 DOM 사이의 변경 사항을 수집하며, 변경 관련정보가진 Fiber를 기준으로 화면에 렌더링을 요청한다. + +(파이버 제조정자 = 비교 → 파이버정보 → 랜더링 요청) + +Fiber는 애니메이션, 레이아웃, 상호작용에 결과물을 만들며, 이를 통해 반응성 문제를 해결한다. (반응한다?!) + +- 작업을 분할하며 우선순위를 정한다. +- 이 작업들을 중지 다시작할 수 있다. +- 이전 작업 재사용, 폐기할 수 있다. + +이과정은 **비동기**이며, 작업들은 Stack구조이다. **즉, Fiber는 react 작업의 Stack 조정자이다.** + +> 왜 비동기냐를 묻는다면?! 구글의 검색창을 예로 들자 + +랜더의 작업 + +- render: **비동기 작업** 수행 및 작업 **우선순위 지정 및 중지 폐기** +- commit: DOM에 변경사항을 반영 = 반영은 동기 + +리엑트요소는 렌더링마다 재생성 되지만 Fiber는 대부분 재사용된다. + +### workInProgress Fiber 트리 + +리엑트는 Fiber 트리를 현재 트리, 작업중 트리 두개를 갖는다. + +**작업이 끝나면 리액트는 당순히 포인터만 변경해 작업중 → 현재로 변경해버린다.** + +> 더블 버퍼링 내부적처리를 미리 그린다음 변경하는 기법 + +리액트는 불완전한 트리를 보여주기보단 더블 버퍼링을 통해 완벽한 UI를 재공한다. + +**current 기준 상호작용 발생 → workInProgress에 새로운 정보를 빌드 → 교체** + +### 가상 DOM + +파이버는 비동기로 처리되는데 비동기적 처리되지만 DOM에 반영은 동기라서 불완전한 트리를 제공할 수 있다. 이를 **메모리상에서 먼저 수행**하는것이 가상 DOM이다. + +즉 가상돔은 빠르게 반영보다는 값으로 UI를 표현하고, 이 처리 흐름을 효율적으로 관리하기위한 것이다. + +## Class vs Function 컴포넌트 + +### Class Component Life Cycle + +> PureComponet: 얕은 비교를 수행하며, 결과가 다를 경우 리렌더링 +> Component: state가 업데이트되는데로 렌더링 + +- 마운트: 컴포넌트가 마운팅(생성) + - componentDidMount(): 마운트되고 즉시실행되며 state에 접근 변경가능 = API 호출에 사용 +- 업데이트: 컴포넌트 내용 변경되는 시점 + - componentDidUpdate(): 업데이트 직후 실행 + - shouldComponentUpdate(): state가 변경되면 리랜더링되지만 여기서 false를 반환하면 리랜더를 막는다. + - getSnapShotBeforeUpdate(): Dom이 업데이트되기 직전에 호출 (윈도우 조절, 스크롤 위치 조절등에 쓰인다.) + - getSnapShotBeforeUpdate → componentDidUpdate 순으로 호출 +- 언마운트: 컴포넌트가 존재하지 않는 시점 + - componentWillUnmount(): 사용되지 않기 직전에 실행되며 this.state 호출 못함 +- render(): 마운트, 업데이트 시점에 사용 UI 렌더링 + - 항상 순수해야하다 ⇒ side effect가 없으며, 항상 입력에 같은 값 반환 +- static getDerivedStateFromProps() + - render가 호출되기 직전에 호출된다. + +### Class Component Life Cycle - Error + +- getDerivedStateFromError() + - 자식컴포넌트에서 에러가 발생됬을 경우, 호출 + - state값을 반환한다. + - 하위컴포넌트를 error에 따라서 그릴지 말지를 결정할 용도로 제공되기때문이다. + - 추가적으로 어떠한 side-effect 발생시지마라 → 이건 componentDidCatch에서 + - render단계에서 실행됨으로 굳이 불필요하게 넣을 필요가 없다. +- componentDidCatch() + - getDerivedStateFromError에서 state결정이후 실행됨 + - side-effect를 넣어서 에러 로깅을 할 수 있다. + +> componentDidCatch는 production mode에서 다르게 동작함. +> dev에서는 window까지 전파된다. +> production mode에서는 componentDidCatch에 잡히지 않는것만 window로 전파됨 + +`const Foo = memo(() => {..})` 와같이 컴포넌트명을 추론할 수 없으면 error info를 정확하게 파악하기 힘들다. 그래서 기명함수 또는 displayName을 적는게 좋다. + +> react forwardRef? 를 쓸때 displayName명시햇던것이 이런이유인듯하다. + +### Class component의 한계 + +- 데이터 흐름 추적 어려움: 가독성이 않좋다. 여러 메서드에서 state 변경이 일어나 추적이 어렵다. +- 로직의 재사용 어려움 : HOC또는 Props로 넘겨주지만 복잡성이 증가, extends를 해도 상속흐름을 쫒아가야됨 +- 컴포넌트의 복잡성 : 여러 메서드드를 통해서 life cycle을 관리하는데 여기서 조금만 복잡도가 높아져도 크기가 비약적으로 상승해버린다. +- 코드 크기: 번들 크기 줄이는데 어려움이 있다. +- HMR 불리: instace가 다시 생성되서 초기값으로 돌아간다. 함수형은 클로저에 state를 저장함으로 유지한다. + +### Function vs Class + +- 생명주기 메서드의 부제 + - useEffect를 통한 life cycle 비슷하게 할 수있다. + - BUT useEffect는 side-effect를 state를 활용해 동기적으로 만드는 메커니즘임 +- 랜더링된 값 차이 + - class는 this를 기준으로 그림 + - function은 렌더링 순간의 props와 state를 기준으로 렌더링 + +## 렌더링은 어떻게? + +> 브라우저의 렌더링 HTML과 CSS를 기반으로 웹페이지에 UI를 그리는 과정 +> React의 렌더링 브라우저의 렌더링에 필요한 DOM 트리를 그리는 과정 + +- 리렌더링: 최초 렌더링 이후 발생하는 모든 렌더링 + - class : setState호출, render()를 실행한 경우, + - function: setState(useState)를 호출, dispatch(useReducer)호출 + - key의 변경 + - key는 형제요소(sibling)사이를 식별하는 값 + - props 변경 + - 부모컴포넌트의 리렌더링 + +### 렌더링 프로세스 + +**랜더** + +컴포넌트를 렌더링 및 변경사항 계산 + +- current - workingProgress 비교 (type, props, key) +- 변경 필요한 컴포넌트 체크 + +**커밋** + +변경사항을 DOM에 적용 + +이후, 리액트와 인스턴스를 가르키도록 내부 참조를 업데이트 이때 useLayoutEffect실행됨 + +> 리액트의 리랜더링 != DOM 업데이트 이런 경우, 커밋 단계가 생략될 수 있다. + +랜더와 커밋은 항상 동기로 이루어지면 이과정이 길어지면 성능저하가 온다. + +따라서 이를 타게하기위해 react-18에서 동시성 렌더링 도입되었다. `useTranslate` 였나?..이런거 + +> **동시성 렌더링은 렌더 단계를 비동기로 동작시켜 특정 렌더링의 우선순위를 낮춘다.** + +## 메모이제이션 + +### 의견1 : 필요한 곳에만 메모이제이션 추가하자 + +메모이제이션도 비용이든다. + +- 값을 비교하고 렌더링 및 재계산이 필요한지 확인하는 비용 +- 이전 결과물을 저장 및 다시 호출하는 비용 + +따라서 메모이제이션은 트레이드 오프가 있는 기법이며, 더 나은 성능을 위해 메모리를 점유하는것이다. + +> react에서도 useMemo없이 최적화를 하도록 권장한다. + +따라서 기능을 구현후 useEffect를 통한 리랜더링 확인 후 적용하는것 옳다!! + +### 의견2: 랜더링 비싸 냅다 메모이제이션하자! + +> memo를 하지않으면 + +- 렌더링 +- 컴포넌트 내부 복잡로직 재실행 +- 자식 컴포넌트에서 반복 + > + +메모이제이션 기법이 APP에서 어떠한 영향을 미치는지 판단하고 정량적이 평가가될 수 있을 때 유의미하다 생각함. + +추가적으로 + +- useCallback의 경우 말그대로 callback함수로 props많이 사용되고 +- useMemo또한 props로 넘어가거나 이를 활용한다면 사용해볼 여지가 있다. + +급하면 함 잡숴보고 판단하는게 가끔은 좋을때도 있다. From d2bea5b8f60ce94d85ac82612e9b978870c94676 Mon Sep 17 00:00:00 2001 From: Dongkyu Kim Date: Mon, 18 Dec 2023 18:10:07 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Create=20=EA=B9=80=EB=8F=99=EA=B7=9C.md=20(?= =?UTF-8?q?#14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\352\271\200\353\217\231\352\267\234.md" | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 "2\354\236\245/\352\271\200\353\217\231\352\267\234.md" diff --git "a/2\354\236\245/\352\271\200\353\217\231\352\267\234.md" "b/2\354\236\245/\352\271\200\353\217\231\352\267\234.md" new file mode 100644 index 0000000..1ab690d --- /dev/null +++ "b/2\354\236\245/\352\271\200\353\217\231\352\267\234.md" @@ -0,0 +1,129 @@ +# 리액트 핵심 요소 깊게 살펴보기 + +리액트에서 자주 언급되는 핵심 개념을 깊게 알아본다! + +## JSX + +JSX는 리액트가 등장하면서 페이스북에서 소개 된 구문 이지만, 리액트에 종속되지 않은 독자적인 문법이다. 자바스크립트 내부에서 표현하기 까다로웠던 XML 스타일 트리 구문을 작성하는데 많은 도움을 주는 문법이다. + +- `JSXElement` `JSXAttribute` `JSXChildren` `JSXString` 4가지 컴포넌트를 기반으로 구성되어 있다. + +## 가상 DOM과 리액트 파이버 + +가상DOM은 웹페이지에 대한 인터페이스로 브라우저가 웹페이지의 콘텐츠와 구조를 어떻게 보여줄지에 대한 정보를 담고있다. 가상 DOM은 DOM을 관리하는 브라우저 보다 무조건 빠른것이 아닌, 대부분의 상황에서 빠르다는 것 이다. + +리액트 파이버는 브라우저가 아닌 환경에서도 적용 될 수 있기 때문에 가상 DOM과 동일한 개념은 아니다. + +``` +최근에 million(https://million.dev/) 이라는 라이브러리를 보았는데, 이건 이런 최적화 성능을 70퍼센트 끌어올렸다고 자랑하더라고요! +``` + +#### 리액트 파이버란 + +리액트에서 관리하는 평범한 자바스크립트 객체이고, 애니메이션 레이아웃 등 사용자 인터렉션에 반응성 문제를 해결한다. 파이버의 모든 과정은 비동기로 일어난다. + +- 파이버 재조정자(fiber reconciler)는 가상 DOM과 실제 DOM을 비교해 변경 사항이 있으면 렌더링을 요청하는 역할을 한다. +- 재조정(reconciliation)은 가상DOM 실제DOM을 비교하는 작업(알고리즘) 이다. + +#### 스택 알고리즘 + +원래 스택에 렌더링에 필요한 작업을 쌓으며 동기적으로 진행되고, 자바스크립트의 싱글스레드 특성 상 동기작업은 중단 되지 못해 이 방식은 비효율적이 되어 버렸다. 이 문제를 해결하기 위해 파이버라는 개념을 도입했다. + +#### 리액트 파이버 구현 + +리액트 파이버는 하나의 작업단위로 구성되어 있으며, `finishedWork()` 함수로 작업을 마무리한다. **렌더 단계**와 **커밋 단계** 2가지로 나뉘어 브라우저 DOM에 가시적인 변경사항을 만들어낸다. + +- 렌더단계, 사용자에게 노출되지 않는 비동기작업들을 수행한다. +- 커밋단계, DOM에 실제 변경사항을 반영하기 위한 작업을 동기적으로 수행한다. + +#### 리액트 파이버 트리 + +리액트 파이버 트리에서는 현재의 모습을 담은 트리와 작업 상황을 담은 workInProgress 트리가 있다. 리액트 파이버의 작업이 끝나면 포인터만 변경하여 workInProgress 트리를 현재 트리로 바꿔버린다. 이 과정을 더블버퍼링 과정이라고 하며, 커밋 단계에서 수행된다. + +## 클래스형과 함수형 컴포넌트 + +초기 함수형 컴포넌트는 단순히 요소를 정적으로 렌더링 하는 것이 목표였지만, 16.8 업데이트 이후 달라졌다. + +#### 클래스형 컴포넌트 + +클래스형 컴포넌트를 사용하며 가장 많이 언급되는 것은 생명주기 이다. + +- Mount, 컴포넌트가 생성 되는 시점 +- Update, 이미 생성된 컴포넌트가 업데이트 되는 시점 +- Unmount, 컴포넌트가 더 이상 존재하지 않는 시점 + +#### 클래스형 컴포넌트의 Render() + +항상 순수해야하며 Side Effect가 없어야한다. render 함수 내부에서 setState를 호출해서는 안된다. + +#### Pure Component와 일반 Component + +shouldComponentUpdate 생명주기를 다룸에 있어서 차이가 있다. Pure Component는 얕은 비교만 진행하여 변경사항이 있을 경우 재 렌더링 시킨다. + +#### ErrorBoundary + +componentDidCatch는 개발모드와 프로덕션모드에서 다르게 동작한다. 개발모드에서는 에러가 발생하면 window까지 전파되고, 프로덕션모드에서는 잡히지 않는 에러만 전파된다. + +#### 클래스형 컴포넌트의 한계 + +- 데이터 흐름을 추적하기 어렵다 +- 애플리케이션 내부 로직의 재사용이 어렵다. +- 기능이 많아질수록 컴포넌트 크기가 커진다. +- 클래스는 함수에 비해 상대적으로 어렵다. + +#### 클래스형 VS 함수형 + +클래스형은 항상 this를 참조하기에 중간에 값이 변경되는 경우 변경 된 값이 렌더링되고, 함수형은 렌더링이 일어난 순간의 값을 가지고 사용한다. + +## 렌더링은 어떻게 일어나는가? + +리액트에도 렌더링이라는 과정이 있으며, 비용이 소모되기 때문에 렌더링 과정을 최소한으로 줄여야한다. + +### 리액트에 렌더링이 일어나는 이유 + +리액트에서 렌더링이 일어나는 타이밍은 아래와 같다. + +- 최초 렌더링 +- 클래스형 컴포넌트의 setState +- 클래스형 컴포넌트의 foreceUpdate +- 함수형 컴포넌트의 useState의 setter가 실행되는 경우 +- 함수형 컴포넌트의 useReducer의 두 번째 요소인 dispatch가 실행되는 경우 +- 컴포넌트의 key props가 변경 되는 경우 +- props가 변경되는 경우 +- 부모 컴포넌트가 리렌더링 되는 경우 + +### Key Props가 필요한 이유 + +리렌더링이 발생하는 동안 동일한 요소를 식별하는 식별값이고, 리액트 파이버의 현재와 workInProgress를 구분하는 값이다. 만약 키가 없다면, 단순한 파이버 내부의 index로만 비교하게 된다. + +### 상태관리 라이브러리 + +mobx나 redux 같은 라이브러리들은 각자의 방법으로 상태관리를 해주다가 위의 리렌더링 트리거를 통해 리렌더링을 발생 시킨다. + +### 렌더와 커밋 + +렌더 단계는 type,props,key 크게 3가지를 비교하여 변경이 필요한 컴포넌트인지 체크한다. 다음인 커밋단계에서 변경 사항을 실제 DOM에 적용한다. 하지만 이와같은 리액트 렌더링과정이 일어났다고 하여 무조건 DOM 업데이트가 일어나는 것은 아니다. + +React 18에서 비동기 렌더링을 통해 먼저 보여줄 수 있는 화면을 먼저 보여주는 동시성 렌더링이 업데이트 되었다. + +## 컴포넌트와 함수의 무거운 연산을 기억해 두는 메모이제이션 + +무조건 메모이제이션이 필요하다 라는 입장과 섣불리 해서는 안된다라는 2가지 입장이 있다. + +### 꼭 필요한 곳에만 메모이제이션을 추가 해야 한다. + +- 메모이제이션에도 비용이 들어간다. +- 간단한 작업의 경우 비용이 더 들어갈 수 있다. +- 리액트 공식문서에서는 useMemo를 사용하지 않고도 작동할 수 있는 코드를 작성하는것을 권하고 있다. + +### 렌더링 과정의 비용은 비싸다! 모두 메모이제이션 해버리자 + +- 자식컴포넌트가 많은 경우, memo를 때려버리는것이 이득이다 +- 프로젝트 규모가 커지는 경우 메모이제이션이 필요한 컴포넌트만 세심하게 볼 수 없다. +- 메모이제이션 되지 않았을 경우 발생하는 리렌더링(객체 재생성)의 비용이 더 크다. +- 사람의 실수로 인한 비용 문제도 있다. + +``` +글쓴이도 후자에 좀 더 이점이 크다고 말했는데 저도 비슷하게 생각합니다. +작업자가 작업을 하면서 생각을 덜 하게 해주는 부분도 꽤 크다고 생각합니다. 다들 이 부분에 대해서 어떻게 생각하시나요? +```