개발(Development)/React(리액트)

[React] 리액트 기초 동작 원리(Feat. Virtual DOM : 가상돔이란?)

카레유 2022. 8. 6. 17:13

리액트란?

자바스크립트 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 등이 기본적으로 세팅되어 있다.