[React] 리액트 기초 동작 원리(Feat. Virtual DOM : 가상돔이란?)
리액트란?
자바스크립트 UI 라이브러리이다.
좀더 친절하게 요약하면,
"상태값이 변경되거나, 부모가 재렌더링 될 때마다 UI를 자동으로 업데이트해주는 JS 라이브러리" 이다.
그리고 이를 위해
"가상DOM 을 통해 변경된 부분만 효율적으로 업데이트" 해주는 구조를 채택했다.
사실 리액트 자체는 이게 다다.
자칫하면 잊기 쉬운 리액트의 알몸을 간략하게 정리해둔다.
# 리액트는 자바스크립트 UI 라이브러리다.
1. 리액트는 컴포넌트의 1) 상태값이 변경되거나 부모가 재렌더링되면, 2) UI를 자동 업데이트 해주는 라이브러리다.
* 즉, state, redux store 등의 상태값이 변경되거나, 부모 컴포넌트가 재렌더링 되면,
* 리액트가 해당 컴포넌트 함수를 자동으로 재호출하여 재렌더링 해준다.
2. 이 때, 가상DOM(Virtual DOM)을 통해 변경된 부분의 UI만 효율적으로 업데이트한다.
* 가상DOM은 실제DOM을 분석하여 만든 Javascrip 객체이다.
* 컴포넌트의 상태값이 변경되면, 새로운 가상DOM 객체를 만들고, 이전 가상DOM 객체와 비교한다.
* 최종적으로 바뀐 부분이 있을 경우, 해당 부분만 실제 DOM에 반영하여 UI를 업데이트 한다.
# 가상DOM(VirtualDom)이란?
- 가상DOM이란 실제DOM의 구조를 분석하여, 아래와 같은 형태로 메모리에 저장하고 관리하는 객체라고 볼 수 있다.
- 렌더링시마다 새로운 가상DOM을 생성하여, 상태값 변경 이전/이후 달라진 부분을 비교하는 매커니즘을 사용한다.
{
type: 'div',
props: {
children: [
{
type: button,
props: {
children: "버튼입니다",
onClick: ()=>{}
}
},
{
type: input,
props: {children: "인풋입니다."}
},
{
type: CountButton,
props: {children: "리액트컴포넌트입니다"}
}
]
}
}
# 리액트 UI 업데이트 단계
- 리액트는 아래의 2단계를 통해 상태값 변경에 따른 UI 업데이트를 진행한다.
1) 렌더 단계(Render Phase. diffing 이라고도 한다. )
- 리액트는 렌더링 할 때마다 매번 새로운 가상 DOM 을 만들고,
- 이전 가상DOM과 비교하여 바뀐 부분을 탐색하고,
- 실제 DOM에 반영할 부분을 결정한다.
2) 커밋 단계(Commit Phase)
- 렌더 단계를 거쳐 바꾸기로 결정된 부분만 실제 DOM에 반영한다.
- 브라우저는 변경된 실제 DOM을 화면에 paint 한다.
* 이 때가 didMount, didUpdate가 완료되어 useEffect가 호출되는 시점이다.
위의 과정을 재조정(Reconcilation) 이라고 부른다.
# 리액트를 알몸으로 사용하는 방법
1) 리액트 라이브러리 불러오기
- 리액트는 JS 라이브러리이므로, 당연히 <script> 태그를 통해 불러와서 사용할 수 있다.
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
- react.development.js 파일을 로드하면 React 객체 생성된다.
- react-dom.development.js 파일을 로드하면 ReactDOM 객체가 생성된다.
2) 리액트 컴포넌트 생성하기
- React 객체의 createElement(태그, 속성, 자식) 메서드로 리액트 요소를 생성한다.
// 함수 컴포넌트
function CountButton() {
// useState 사용
const [count, setCount] = React.useState(0);
// 리액트 요소 생성
return React.createElement(
'button', // button 태그 생성
{
onClick: () => {
// 클릭 이벤트 핸들러 설정
setCount(count + 1);
}
},
count // children 설정
);
}
3) 리액트 컴포넌트를 화면에 렌더링하기
- ReactDOM 객체의 render(컴포넌트, 컨테이너) 메서드로 컴포넌트를 렌더링한다.
const domContainer = document.getElementById('root');
ReactDOM.render(React.createElement(CountButton), domContainer);
// 전체 코드
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- 리액트 라이브러리 로드 -->
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
</head>
<body>
<!-- 컨테이너 -->
<div id="root"></div>
<script>
// 리액트 함수 컴포넌트
function CountButton() {
// useState 사용
const [count, setCount] = React.useState(0);
return React.createElement(
'button', // button 태그 생성
{
onClick: () => {
// 클릭 이벤트 핸들러 설정
setCount(count + 1);
}
},
count // children 설정
);
}
// div#root 태그에 리액트 컴포넌트 렌더링하기
const domContainer = document.getElementById('root');
ReactDOM.render(React.createElement(CountButton), domContainer);
</script>
</body>
</html>
실제 리액트로 개발을 할 때는 JSX로 코딩하고,
여기 저기서 모듈들을 export, import 해서 불러오는데,
사실 이는 모두 바벨과 웹팩의 은총 덕분이다.
1) Babel 이 JSX를 React.createElement() 로 변환해준다.
2) WebPack이 JS, CSS 파일을 번들링하여 모듈화해준다.
참고로, npx create-react-app 등을 사용하면 Babel, Webpack 등이 기본적으로 세팅되어 있다.