렌더링 엔진 동작
브라우저가 화면을 그리는 렌더링 엔진 동작 과정
- 파싱 단계: DOM 트리와 CSSOM 트리를 만든다.
- 렌더 트리 단계: DOM과 CSSOM을 묶어서 렌더 트리를 만든다.
- 레이아웃 단계: 렌더 트리에서 각 노드가 어디에 어떻게 그려져야 하는지 모양을 계산한다.
- 페인트 단계: 계산한대로 화면에 엘리먼트들을 배치한다.
성능 최적화
성능 최적화는 기본적으로 적은 비용과 빠른 작업 속도가 중요하다.
DOM을 조작할 때 비용이 드므로 성능을 올리고 싶다면 렌더링 엔진 동작 과정에서 레이아웃과 페인트 단계에서 드는 연산을 줄이는 것이 중요하다.
연산비용을 줄이기 위해서는 변경된 내용이 생길 때 마다 렌더 트리 전체를 뒤지기 보다는 변경된 내용만 연산을 하도록 작업한다. (더티 비트 시스템)
*더티 비트 시스템 👉 변경된 내용이 생길때마다 하나하나 수정하는 것이 아니라, 변경할 내용을 기록했다가 최대한 한방에 처리!
이런 방식을 이용하여 리액트는 메모리 상에 가상 DOM을 두고, 변경된 내용이 있으면 가상 DOM에 올려두었다가 실제 DOM에 변경된 것만 한 번에 렌더링될 수 있도록 처리한다.
(메모리에 있는 가상 DOM에서 그려지는 것은 실제로 그려지는 것이 아니므로, 실제 DOM보다는 연산 비용이 줄어든다)
*BUT 가상 DOM이 무조건 빠른 것은 아니다, 사이트 구조에 따라서 가상 DOM을 쓰는 것 보다 실제 DOM을 쓰는 것이 훨씬 성능이 좋을수도 있고, 속이 터지게 느릴 수도 있다.
package.json
'yarn init -y
' 프로젝트 초기화하면 package.json 파일이 생성된다.
package.json 은 프로젝트 정보를 정의하고 의존하는 패키지 버전 정보를 명시해두는 파일이다.
webpack.config.js
Webpack 번들러의 설정을 이곳에서 설정해준다.
// webpack.config.js
const path = require("path");
module.exports = {
mode: "development", /* 개발환경(dev)/배포환경(prod) */
entry: {
main: "./src/index.js", /* 파일의 시작점 명시 */
},
output: {
/* 번들된 파일의 이름과 경로를 설정 */
filename: "[name].bundle.js",
path: path.resolve(__dirname, "build"),
},
/* resolve - webpack의 동작 정의 */
resolve: { extensions: ["*", ".js", ".jsx"] }, /* import 될 수 있는 파일의 확장자명 명시 */
stats: { children: true },
};
- mode: 개발환경 또는 배포환경에서 진행할 것인지 명시 (development/production)
- entry:
- main - 파일이 시작할 때 보여질 파일. 파일의 시작점을 명시
- output:
- filename, path - 번들된 파일의 이름과 경로를 설정
- resolve: webpack의 동작 정의
- extensions - import 될 수 있는 파일의 확장자명을 명시
Babel
babel은 컴파일러로, 간단하게 말하면 번역기이다.
ES6+에서 작성한 자바스크립트와 JSX는 브라우저가 지원해줘야 실행할 수 있는데, 지원하지 않는다면 코드가 동작하지 않는다. 따라서 Babel은 최신 문법으로 작성한 코드들도 이하 버전의 브라우저에서 알아들을 수 있도록 번역해주는 역할을 한다.
.babelrc
webpack.config.js 와 비슷하게 바벨 설정을 이곳에서 한다.
- presets: 무엇으로 변환할지 명시
// .babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
리액트 버전에 따른 돔 렌더링방식
// index.js
import React from "react";
import ReactDOM from "react-dom";
// react v18
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<div>안녕 여러분! :)</div>);
// react v17
// 첫번째 인자에 화면에 렌더링할 내용, 두번째 인자에 렌더링될 위치
ReactDOM.render(<div>안녕 여러분! :)</div>, document.getElementById('root'));
CRA 없이 리액트 프로젝트를 세팅하는 이유
- 기본적으로 CRA에 포함되어있는 수많은 패키지가 필요 없는 경우
👉 BUT, 패키지 하나 작업할 때 마다 webpack/babel을 만져야하는 번거로움이 있다
- 호스트 환경이 최신이 아닌 경우
👉 ex. 키오스크 프로젝트 진행 - 자바스크립트 엔진이 es3 만 돌아간다면 폴리필 설정을 별도로 해주어야 한다. 이 설정을 CRA에서 하기에는 많이 번거롭다.
함수형 컴포넌트
무조건 하나의 엘리먼트 또는 null 값을 반환해야 한다.
function IAmComponent(props) {
return (<p>I am component! :) Functional component!</p>);
}
컴포넌트 import 시 불러오는 이름의 차이점
함수형 컴포넌트를 다른 곳에서 import 할 때 보면 다음과 같이 2개의 방식이 있다.
import One from "./MyComponents";
import { Two } from "./MyComponents";
이 두가지의 차이는 컴포넌트를 export 할 때 그냥 하는것과 export default 하는 것의 차이다.
//MyComponents.js
const One = () => {
return (
<div>
<h1>One</h1>
</div>
);
};
const Two = () => {
return (
<div>
<h1>Two</h1>
</div>
);
};
export default One;
export { Two };
export default
- export default 는 한 파일 당 하나의 컴포넌트만 할 수 있다.
// 이런식으로 하면 오류
export default One;
export default Two;
- export default 로 선언한 경우, 컴포넌트의 원래 이름과 달라져도 무방하다.
//MyComponents.js
const One = () => {
return (
<div>
<h1>One</h1>
</div>
);
};
export default One;
// App.js
import OneOne from "./MyComponents";
function App() {
return (
<div className="App">
<OneOne />
</div>
);
}
export default App;
- export 로 내보냈는데 컴포넌트 이름을 변경해서 불러오고 싶은 경우 (별칭 지정)
// App.js
import { Two as TwoTwo } from "./MyComponents";
function App() {
return (
<div className="App">
<TwoTwo />
</div>
);
}
export default App;
함수형 컴포넌트 상태관리
컴포넌트의 데이터 관리를 '상태관리' 라고 한다.
컴포넌트가 가지고 있는 데이터는 크게 2개가 있다. 👉 props 와 state.
- props: 부모한테 받아온 데이터이다.
- state: 컴포넌트가 가지고 있는 데이터이다. (내거)
이 두 객체들은 렌더링 결과물에 영향을 주는 데이터를 가지고 있으므로 직접 수정을 하면 안된다.❎
아래 보기와 같이 컴포넌트의 재렌더링 문제 때문에 a=1 과 같이 직접 새로운 값을 할당하면 안된다는 의미이다.
⭐ 컴포넌트가 업데이트 되는 경우 (컴포넌트 라이프 사이클)
1. state 가 변할 때
2. props 가 변할 때
3. 부모가 재렌더링 됐을 때
4. 강제 업데이트
ReactDOM
ReactDOM.createRoot(some Element).render();
render() 는 루트 DOM 노드(<div id="root"></div>
) 에 리액트 엘리먼트를 전달하는 역할을 한다.
리액트 엘리먼트는 기본적으로 불변 객체이다. (속성, 자식 등 엘리먼트의 내용을 변경할 수 없다.)
render() 는 엘리멘트에 변화가 필요한 경우 필요한 부분만 업데이트 해주는 역할을 한다.
명령어
nvm으로 node 설정하기
nvm -v # nvm이 잘 설치되었나 보기 위해, 버전 정보를 확인해봅니다.
nvm install 16.14.0 # 현재 LTS 버전으로 설치해봅시다!
nvm use 16.14.0 # 설치한 노드를 사용해봅시다.
node로 yarn 설치하기
node -v # node 버전을 확인해봅니다.
npm -v # npm은 노드의 패키지 매니저예요. 이걸 통해서 누군가 만들어 둔 패키지를 설치하고 사용할 수 있어요!
npm install -g yarn # -g는 글로벌로 설치하겠다!고 알려주는 옵션입니다. yarn을 글로벌로 설치해봅시다!
yarn으로 CRA 설치하기
yarn -v # yarn 버전을 확인해봅니다.
# global은 글로벌 설치 옵션입니다.
# yarn은 npm과 명령어가 조금 다른데요, install 대신 add를 씁니다.
# 이런 명령어의 차이는 공식문서를 통해 확인해도 좋고, yarn --help로 확인해도 좋습니다.
# yarn으로 create-react-app을 설치합니다.
yarn add create-react-app global
CRA 프로젝트 만들기
yarn create react-app 프로젝트폴더이름
프로젝트 초기화
yarn init -y
Webpack 설치
yarn add -D webpack webpack-cli
HTML/CSS Webpack 패키지 설치
# webpack으로 html 파일을 묶어주기 위해 필요한 패키지 설치
yarn add -D html-webpack-plugin clean-webpack-plugin
# webpack으로 css 파일을 묶어주기 위해 필요한 패키지 설치
yarn add -D mini-css-extract-plugin css-loader sass-loader file-loader
Babel 설치
# 바벨을 쓸 때 필요한 라이브러리는 내가 어떤 걸 해볼지에 따라 차이가 날 수 있어요.(사실 웹팩도, 다른 패키지들도 모두 그렇죠!)
# 이번에는 정말 꼭 필요한 것들만 설치해서 써봅니다. 저는 아래처럼 구성해볼거예요.
# @babel/core : 바벨 코어 라이브러리
# babel-loader : webpack에 babel 적용하기 위한 라이브러리
# @babel/preset-env : es6를 es5로 컴파일링 해주는 라이브러리
# @babel/preset-react : JSX를 자바스크립트 코드로 변환해주는 라이브러리
yarn add -D @babel/core babel-loader @babel/preset-env @babel/preset-react
React 설치
yarn add react react-dom
webpack-dev-server 설치
yarn add webpack-dev-server