React (2) Virtual DOM

이종호·2022년 7월 24일
0

React

목록 보기
2/7
post-thumbnail

영상링크

브라우저의 동작

브라우저 렌더링 과정

  1. DOM 트리 생성
    ㄴ 렌더 엔진이 HTML을 파싱하여 DOM 노드로 이루어진 트리 생성

  2. render 트리 생성
    ㄴ css파일과 inline 스타일을 파싱하여 앞서 만든 DOM트리와 결합한다. (DOM+CSSOM)

  3. Layout (reflow)
    ㄴ 뷰포트 내 생성된 render 트리의 각 노드들이 스크린 좌표에 따라 위치 결정

  4. Paint (repaint)
    ㄴ 3번까지의 작업으로 생성된 Layout을 갖고 실제로 그려냄

DOM 조작의 비효율성

위의 렌더링 4가지의 작업 도중, DOM에 변화가 발생하면 render tree가 그때마다 재생성 됨을 알고가자.
이 뜻은 변화가 발생하면 모든 요소들의 스타일을 다시 계산하고, layout, repaint가 다시 반복된다는 뜻이다.

인스타그램의 좋아요 클릭, 장바구니 목록에서 특정 아이템 삭제, 유튜브 댓글 남기기 등 이러한 과정을 수행하면 virtual DOM이 아닌 DOM에서는 전체 노드들이 처음부터 그려진다.

Virtual DOM의 등장

Virtual DOM의 등장

DOM조작의 비효율성으로 인해 DOM조작이 불필요해 보이며 시간이 많이 드는 소모적인 과정으로 여겨졌다.

심지어 최근에는 SPA(Single Page Aplication)에서 페이지를 서버가 아닌 브러유저에서 관리하여 DOM 트리를 즉각적으로 변경해야만 하는 일이 많아졌다.

이러한 문제점을 갖고 등장하게 되었다.

Virtual DOM의 동작 원리

Virtual DOM은 HTML DOM의 추상화 버전이다.
실제 DOM 객체와 같은 속성을 갖고있지만 api는 갖고있지 않다.

데이터가 변경되면 전체 UI는 가상 DOM에 렌더링 된다.
그런 후 이전 가상 DOM에 있던 내용과 업데이트 후의 내용을 비교하여 바뀐 부분만 실제 DOM에 적용시킨다.

즉, 가상 DOM에 변경사항이 반영되면 원본 DOM에 필요한 변화만 반영되어 전체 DOM을 바꾸지 않고도 필요한 부분의 UI만 업데이트를 적용할수 있다.

그렇다면 가상DOM은 어떻게 생겼길래 이렇게 사용할수 있을까?

<ul id='items'>
	<li>Item 1</li>
    <li>Item 1</li>
</ul>    

이러한 html이 있을때,

let domNode = {
	tagName: 'ul',
  	attribute: { id: 'items' },
  	children: [
      {
      	tagName: 'li',
        textContent: 'Item 1',
      },
      {
        tagName: 'li',
        textContent: 'Item 2',
      },
    ],
};

사실 이런 자바스크립트 객체로 표현된다.
이렇듯 Virtual DOM(가상DOM)은 자바스크립트 객체를 활용하고 있다. 또한 자바스크립트 이므로 실제 DOM이 아닌 메모리 상에서 동작하여 훨씬 더 빠르게 동작한다.

가상 DOM의 특징을 정리하면,

  1. 자바스크립트의 객체로 표현된다.
  2. 메모리 상에서 동작한다.
  3. 실제로 렌더 되지 않아 연산 비용을 줄인다.

로 정리가 된다.

실제 가상 DOM이 하는 일은 DOM fragment의 변화를 묶어 적용한 다음 기존 DOM에 던져주는 과정을 자동화 및 추상화 한것이다.

React의 Virtual DOM

SPA에서 가장 많이 쓰이는 대표적인 라이브러리. React에서는 가상 DOM을 다음과 같이 이야기한다.

UI의 가상적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 실제 DOM과 동기화 (재조정)

JSX

위의 개념을 실제 React 코드로 작성해보면,

function WoowaComponent() {
	return (
    	<div className='tech-course'>
        	<h1>Hello Crews!</h1>
      	</div>
    );
}

와 같은 자바스크립트 확장문법 jsx를

function WoowaComponent() {
	return React.createElement('div',{
    	className: 'tech-course'
    }, React.createElement('h1', null, 'Hello Crews!'));
}

코드 변환기인 바벨(Babel)은 앞선 JSX를 react.createElement()호출로 컴파일한다.
그리고 컴파일된 코드는 다시 객체로 표현된다.

const element = {
	type: 'div',
  	props: {
      className: 'tech-course',
      children: [
        {
          type: 'h1',
          children: 'Hello Crews!',
        }
      ]
   }
};

ReactDOM.render(element, document.getElementById('Woowa-root'));

이러한 객체 표현법은 앞서 말한 가상DOM의 표현법이다.
즉, React Elements는 DOM요소의 가상 버전으로 가볍고, 상태를 갖지 않으며, 불변성을 갖는다.
불변성 덕분에 비교하고 업데이트 하는게 쉬워진다.

그리고 이러한 컴포넌트는 ReactDOM.render()메서드를 거쳐 비로소 실제 DOM요소가 된다.

하지만 앞서말한 불변성. react element는 변경이 불가능해서 한번 요소를 만들었다면 데이터가 변했다고 해서 그 자식이나 속성을 맘대로 변경할 수 없다.

즉, react element는 렌더 되는 순간의 UI를 스냅샷 형태로 보여준다고 볼수 있다.

이렇게 되면 UI를 업데이트 할 수 있는 방법은 새로운 요소를 만들어 ReactDom.render()로 전송하는것 밖에 방법이 없다.

diffing 알고리즘

하지만 이 때 React는 가상 DOM을 이용한다.
모든 React DOM 객체는 그에 대응하는 가상 DOM 객체가 있다.

그리고 가상 DOM객체는 그 DOM 객체에 하나하나 매핑된다. 데이터가 업데이트 되면 바뀐 데이터를 바탕으로 React.createElement()를 통해 JSX element를 렌더링한다.

이때, 각각의 모든 가상 DOM 객체가 업데이트 된다.
그리고 가상 DOM이 업데이트 되면 React는 가상 DOM을 업데이트 이전에 가상 DOM 스냅샷과 비교하여 정확히 어떤 가상DOM이 바뀌었는지 검사한다. 이러한 과정을 Diffing알고리즘이라고 한다.

Diffing
Virtual DOM이 업데이트 되면 React는 virtual DOM을 업데이트 이전의 Virtual DOM 스냅샷과 비교하여 정확히 어떤 virtual DOM이 바뀌었는지 검사한다.

위 사진과 같이 element의 속성 값만 변한경우에 속성 값만 업데이트하고,
element의 태그 또는 컴포넌트가 변경된 경우 해당 노드를 포함한 하위 모든 노드를 unmount 후 새로운 virtual DOM으로 대체하는것 이다.

그리고 위의 과정을 거쳐 딱 한번 실제 DOM에 Diffing 알고리즘 결과를 업데이트한다.

React의 Virtual DOM

코드 예시와 함께 어떻게 업데이트가 이루어지는지 알아보자.

return (
	<div className='App'>
      <header className='App-header'>
        <div>귀엽게 생긴 친구</div>
        <hr />
        <div className='content'>
        	<img src={baedali} alt='baedali' />
        </div>
        <button onClick={addCharacter}>+</button>
      </header>
  	</div>
);

버튼을 누르면 그림의 캐릭터가 하나씩 추가되는 컴포넌트 이다. 이 컴포넌트의 기능 요소는 다음과 같다.

  1. 버튼을 누른다.
  2. 캐릭터가 추가된다.
  3. 전체 virtual DOM이 업데이트 된다.
  4. virtual DOM을 업데이트 이전의 시점과 비교한다.
const content = {
	type: 'div',
  	attributes: { class: 'content' },
    children: [
        {
          type: 'img',
          attributes: { src: 'baedali' },
        }
	]
};
const copy = {
	type: 'div',
  	attributes: { class: 'content' },
    children: [
        {
          type: 'img',
          attributes: { src: 'baedali' },
        },
      	{
          type: 'img',
          attributes: { src: 'baedali' },
        }
	]
};
  1. 실제로 바뀐 부분만 real DOM에서 바꾼다.

  1. realDOM에서의 변화가 스크린에 그려진다.

vitrual DOM이 무조건 DOM보다 빠를까?

아니다.
정보 제공만 하는 웹페이지라면 인터랙션이 발생하지 않기 때문에 일반 DOM의 성능이 더 좋을수도 있다.

반대로, SPA로 제작된 큰 규모의 웹 페이지에서는 Virtual DOM을 사용해서 브라우저의 연산양을 줄여 성능을 개선할 수 있다.

profile
Frontend

0개의 댓글