DOM / Virtual DOM

Kyle·2022년 8월 3일
0
post-thumbnail

참고

DOM | PoiemaWeb
DOM과 Virtual DOM
Virtual DOM 동작 원리와 이해 (with 브라우저의 렌더링 과정)
NAVER D2
Virtual DOM
DOM과 Virtual DOM

DOM ( Domcument Object Model )

돔이란?

  • Document object model, 웹문서를 로드한후 파싱하여 웹문서를 브라우저가 이해할 수 있는 구조로 구성하여 메모리에 적재하는 것

  • DOM은 웹문서의 모든 요소와 요소의 어트리뷰트, 텍스트를 각각의 객체로만들고 이를 트리구조로 구성한 것을 의미한다.

  • DOM은 js를 통해 동적으로 변경할 수 있으며, 변경된 DOM은 렌더링에 반영된다.

  • DOM은 DOM API로 프로퍼티와 메소드를 갖는 자바스크립트 객체를 통해 DOM에 접근하고 수정할 수 있는 방법을 제공한다.

돔의 기능

  • HMTL 문서에 대한 모델 구성
    • 브라우저는 HTML 문서를 로드한 후에 해당 문서에 대한 모델을 메모리에 생성한다. 이때 모델은 객체의 트리로 구성되는데 이를 DOM tree 라고 한다.
  • HTML 문서 내의 각 요소에 접근 / 수정
    • DOM은 모델 내의 각 객체에 접근하고 수정할 수 있는 프로퍼티와 메소드를 제공한다. DOM이 수정되면 브라우저를 통해 사용자가 보게 될 내용 또한 변경된다.

DOM Tree

DOM Tree의 종류

  • 문서 노드 (Document)

    • 트리의 최상위 존재, 각 노드에 접근하기위해서 문서노드를 통해야함, 시작점(entry point)
  • 요소 노드 (Element)

    • html 요소, html 요소는 중첩에 의해 부자관계를 가지며 이 부자관계를 통해 정보를 구조화한다. 요소노드는 문서의 구조를 서술한다.
    • 어트리뷰트/텍스트 노드에 접근하기위해 먼저 요소노드를 찾아 접근해야함
    • 모든 요소노드는 요소별 특성을 표현하기위해 HTMLElement 객체를 상속한 객체로 구성된다.
  • 어트리뷰트 노드 (Attribute)

    • HTML 요소의 어트리뷰트를 표현, 이 노드는 요소의 일부로 표현됨. 해당 요소 노드를 찾아 접근하면 어트리뷰트를 참조, 수정할 수 있다.
  • 텍스트 노드 (Text)

    • HTML 요소의 텍스트를 표현
    • 요소 노드의 자식, 자신의 자식노드를 가질 수 없음.
    • DOM tree의 최종단
      개발자도구의 Properties를 통해 DOM tree 를 확인할수있다.
      개발자도구의 Properties를 통해 DOM tree 를 확인할수있다.

DOM Manipulation(조작)

  • DOM을 통해 웹페이지를 조작하기위해서 는 다음과같은 수순으로 진행한다.
    • 조작하고자하는 요소를 선택 또는 탐색한다.
    • 선택된 요소의 콘텐츠 또는 어트리뷰트를 조작한다.
<html>
  <head>
  </head>
  <body>
    <div class="dailyfunding-wrapper">
      <p class="daily-intern">뚱이</p>
    </div>
  </body>
</html>
  • daily-intern 을 단풍이로 바꾸고, 두번째에 소금이를 추가하는 작업
    1. daily-intern 의 첫번째 요소 탐색
    2. 해당 내용을 단풍이로 바꾸기
    3. p태그를 하나 더 만들어서 소금이 Element 추가하기
const dailyIntern = document.getElementByClassName("daily-intern")[0];
dailyIntern.textContent = "단풍이";

const Intern = document.getElementByClassName("dailyfunding-wrapper")[0];
const Salt = document.createElement("p");
Salt.classList.add("daily-intern");
Salt.textContent = "소금이";
Intern.appendChild(Salt);

이러한 작업은 프로젝트의 규모가 커지고, 코드가 복잡해지면 이러한 작업을 수행하기가 어려워진다.

getElementByClassName 메소드를 통해 특정 class 를 탐색하지만, 코드가 복잡해진다면 특정 class를 찾아 업데이트하는 쿼리비용은 매우 비싸진다.

이외에도 innerText를 사용하여 문서의 큰 부분을 한번에 바꾸는 방법도 있다

const Intern = document.getElementByClassName("dailyfunding-wrapper")[0];
Intern.innerText = `
	<p class="daily-intern">단풍이</p>
	<p class="daily-intern">소금이</p>
`;
  • 돔 조작은 웹페이지를 수정하는데 유용하지만 문제점도 존재한다.

    • 자바스크립트를 사용하여 <div>태그의 색상을 업데이트한다고 가정헀을때 해당 DOM node 객체에 접근하여 색상 속성을 업데이트하면 된다. 이때는 트리의 나머지 노드에 영향을 끼치지않는다.
    • 트리에서 하나의 노드를 추가하거나 제거한다면, 전체 트리를 다시 정렬해야 할 수 있고, 시간과 브라우저리소스가 필요하다.
    • 예를들어 5가지 리스트 (<li></li>) 가 추가된다고하면, 하나의 리스트마다 새 노드가 DOM에 추가되어, 트리가 매번 업데이트되는 문제점이 발생한다.
    • 하나의 노드추가나 삭제를 하여 웹페이지 전체의 레이아웃에 영향을 받는 경우 웹페이지 일부 또는 전체가 리렌더링된다. 이런 경우를 Reflow라고 한다. 과도한 Reflow는 성능에 영향을 준다.
  • 위에 다룬것처럼 DOM은 DOM을 생성할때마다, Render Tree의 생성부터 layout, painting까지 의 작업을 다시 해야한다. DOM자체는 빠를지라도, 브라우저가 작업해야하는 작업이 많아지면 이는 성능 저하로 연결된다.

  • 이에 대한 대안으로 React에서는 Virtual DOM을 사용한다.

  • Vitual DOM에 대해 들어가기 앞서 브라우저 동작 방식을 먼저 간단하게 살펴보자.

브라우저 동작 방식

  1. HTML을 파싱하여 DOM 객체를 생성한다. (DOM Tree 생성)
    • 브라우저가 HTML파일을 렌더엔진을 통해 HTML 요소를 분석하여 DOM Tree를 생성한다.
  2. CSS를 파싱하여 스타일 규칙을 생성한다.
  3. HTML과 CSS를 합쳐서 실제 웹 브라우저에 보여져야할 요소를 표현한 Render Tree를 생성한다.
    • Render Tree생성을 위해서 Render 객체의 시각적 속성을 계산해야한다.
    • DOM Tree의 모든 노드에는 계산된 스타일 정보를 가져오고 Render 객체를 반환하는 attach 메서드가 있다.
  4. Layout ( Reflow )
    • 렌더트리 생성 후에 이 프로세스를 통해 Render Tree의 모든 노드에는 화면에 표시되어야할 좌표가 제공된다.
    • 생성된 DOM 노드의 레이아웃 수치가 변경 될 때 영향을 받는 모든 노드의 수치를 다시 계산하여 렌더트리를 재생성하는 과정을 Reflow라고 한다. (width, 등)
  5. Painting(페인팅) , (Repaint)
    • 각 노드의 paint() 메소드가 호출되어 화면에 표시된다.
    • Reflow 과정이 끝난 후에는 재생성된 렌더트리를 다시 그리는 과정이 이루어지는데 이를 Repaint라고 한다. 이 과정에는 레이아웃 수치변경과 관련이 없는 요소들이 해당된다 (background-color, outline 등)
  • Reflow가 발생하면 반드시 Repaint 가 발생하지만, Repaint는 단독으로 발생할 수 있다.
    • Reflow는 레이아웃 변화로 일부 또는 전체를 다시 구성하고 그리지만, 만약 레이아웃 변경에 관련 없는 요소의 변화는 Reflow가 발생하지 않고 Repaint만 발생한다.

DOM을 생성할때마다 위의 과정들을 모두 다시해야한다.
DOM을 생성할때마다 위의 과정들을 모두 다시해야한다.
이 일련의 과정들은 DOM을 생성할때마다 Render Tree부터 페인팅까지 모두 다시 해야하며 브라우저의 성능저하로 이어진다. 이는 아까 다뤘던 DOM의 문제점이 된다.

  • 이를 해결하기위해 Virtual DOM 이라는것을 사용하게된다.
  • Virtual DOM이란 DOM과 유사한 역할을 담당하는 객체이며, 추상화한 가상의 객체를 메모리에 만들어 놓는 것이다.
  • Virtual DOM은 변경 내역을 한 번에 모으고(버퍼링) 실제 DOM과 변경된 Virtual DOM의 차이를 판단 후, 구성요소의 변경된 부분만 찾아 변경하여 렌더링을 한번만 수행한다.

Virtual DOM

Virtual DOM 이란?

  • DOM을 추상화한 가상의 객체
  • DOM을 반복조작하면 렌더링이 자주일어난다
  • why? → 브라우저의 렌더링 방식이 그렇기 때문임

Vitual DOM이 동작하는 방식

  1. 데이터 업데이트시 전체 UI를 Virtual DOM에 리렌더링함.
  2. 이전 Virtual DOM에 있던 내용과 현재의 내용을 비교함.
  3. 바뀐 부분만 실제 DOM에 적용
  4. 여러번의 변경사항이 있더라도 모든 변경사항을 하나로 그룹화하기 때문에 한번만 수행된다.

Virtual DOM을 사용하는 이유

  • 위의 장점들은 사실상 DOM의 모든 수정사항을 직접 그룹화 한다음 DOM에 넘겨도 가능한 방법이다.
  • Virtual DOM은 이러한 DOM 관리를 자동화하고 추상화하여 직접할 필요가 없게 해준다.
  • 만약 직접 DOM을 그룹화하여 넘길떄는 DOM Tree를 reload하지 않기 위해 변경한 부분과 변경되지않는 부분을 직접 추적해야하지만 Virtual DOM을 쓰므로써 이 행위들을 자동화할 수 있다.
  • Virtual DOM을 쓰게되면 DOM의 조작 자체를 포기하게되지만, DOM을 수정하는 모든 부분간의 동기화를 피할 수 있다.
  • 단, 가상돔이 항상 더 빠르다고 할수는 없다. 가상돔은 개발자가 작업을 보다 쉽게 할 수 있도록 도와주는 것이지, 가상돔에서 더 빠르게 접근할 수 있는 무언가를 제공해 주는 것이 아니다.

virtual DOM의 특징

  • DOM의 기능을하면서 더 가벼움
  • in-memory에 존재해서 실제 렌더되지 않음
  • JS 객체로 이루어진 tree data structure
  • React와 같은 UI 라이브러리에 널리 쓰임

예시

  • HTML code를 객체화한 예시
<html>
  <head>
  </head>
  <body>
    <div class="dailyfunding-wrapper">
      <p class="daily-intern">뚱이</p>
    </div>
  </body>
</html>
const vdom = {
	tagName: 'html',
	childern : [
		{ tagName: 'head' },
		{
			tagName: 'body',
			children: [
				{
					tagName: 'div',
					attributes: { 'class': 'dailyfunding-wrapper' },
					children: [
						{
							tagName: 'p',
							attributes: { 'class': 'daily-intern' },
							textContent : '뚱이'
						}
					]
				}
			]
		}
	]
}
  • Virtual DOM 은 작업하려는 일부분만 작업할 수 있다.
  • 자유롭게 일부분만 업데이트 할 수 있다는 장점이 존재함.
const intern_list = {
	tagName: 'div',
	attributes: { 'class': 'dailyfunding-wrapper' },
	children: [
		{
			tagName: 'p',
			attributes: { 'class': 'daily-intern' },
			textContent: '뚱이'
		}
	]
}
const new_intern_list = {
	tagName: 'div',
	attributes: { 'class': 'dailyfunding-wrapper' },
	children: [
		{
			tagName: 'p',
			attributes: { 'class': 'daily-intern' },
			textContent: '단풍이'
		},
		{
			tagName: 'p',
			attributes: { 'class': 'daily-intern' },
			textContent: '소금이'
		}
	]
}
  • Virtual DOM은 intern_list 객체와 new_intern_list 객체를 비교하여 변경점을 찾는다.
  • 변경점을 발견하면 해당 부분만 실제 DOM에 반영한다.

Reconciliation (재조정)

Virtual DOM 이 어떻게 변경 되는 부분만 알아낼수있을까에 대한 답

Reconciliation 과정을 통해 서로 다른 두 DOM Tree를 비교하여 하나의 실제로 반영될 DOM Tree를 만들어낸다.

  • Reconciliation 두가지 가정

    1. 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다.
      • 엘리먼트가 다르면 비교하지 않는다.
    2. 개발자가 key를 이용하여, 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할 지 표시해 줄 수 있다. (key를 통해 비교함)
  • 부모노드의 타입이 다르면, 자식 노드는 비교하지 않는다.

    • React는 DOM 트리를 level-by-level 방식으로 탐색한다.
      너비 우선 탐색(BFS) 의 일종으로 볼수있다.
      너비 우선 탐색(BFS) 의 일종으로 볼수있다.
      깊게 탐색하기전에 넓게 탐색하는 것
  • 엘리먼트 타입이 다른 경우

<div>
	<Counter />
</div>
<span>
	<Counter />
</span>

위의 노드가 아래로 바뀌면 자식 노드인 컴포넌트는 완전히 해제되고 state 또한 파괴된다. 부모 엘리먼트 타입이 다를 경우 처음부터 렌더하는것이 효율적이다. 이는 display: block 규칙이 무너지기 때문에 렌더링 결과물이 어차피 달라지게 되기 떄문이다.

  • DOM 엘리먼트 타입이 같은 경우
    같은 타입의 React DOM element를 비교할 때, React는 두 element의 속성을 확인하여, 동일한 내역은 유지하고 변경된 속성들만 갱신한다.
    <div className="before" title="세미나" />
    <div className="after" title="세미나" />
    React는 현재 DOM 노드 상에서 className 만 수정한다.
  • 자식에 대한 재귀적 처리
    • 자식 엘리먼트의 순서가 다른 경우 아래와 같이 비효율적으로 수행하게 된다.
      <ul>
        <li>first</li>
        <li>second</li>
       <ul>
      <ul>
          <li>zero</li> {/* first가 zero 로 변경, 새로 렌더링 */}
          <li>first</li> {/* second가 first 로 변경, 새로 렌더링 */}
          <li>second</li> {/* 추가된 항목, 새로 렌더링 */}
       <ul>
  • 위의 문제는 key를 통해 해결 할 수 있다.
<ul>
  <li key="a">first</li>
  <li key="b">second</li>
</ul>
<ul>
  <li key="c">zero</li> {/* 추가, 새로 렌더링 */}
  <li key="a">first</li> {/* 이전의 key와 동일, 순서만 변경. */}
  <li key="b">second</li> {/* 이전의 key와 동일, 순서만 변경. */}
</ul>

Reconciliation Process
Reconciliation Process

Component render() — updating the Virtual DOM, running the diffing algorithm and finally updating the DOM
How Virtual-DOM and diffing works in React

Reconciliation에 대한 알고리즘 설명을 자세하게 다룬 포스팅

결론

  • React는 Virtual DOM을 이용하여 렌더를 한 번으로 줄여 DOM의 단점을 보완하여 효율적으로 HTML을 제어한다.
  • 위에서 다뤘던내용 중 무조건 리액트 Virtual DOM을 사용한다고해서 항상 성능이 향상된다고 할 수는 없다. Virtual DOM은 개발자가 작업을 보다 쉽게 할 수 있도록 도와주는것이지, Virtual DOM에서 더 빠르게 접근할 수 있는 무언가를 제공해주는것이 아니다. 가상돔은 목적을 위한 수단일 뿐이다.
  • React의 핵심 개발자들도 vanilla js(프레임워크를 이용하지 않은 순수 개발) 가 Virtual DOM에서 작업하는 것보다 항상 더 빠르다고 말하는 부분과 일맥상통하는 부분이다.
  • 하지만 바닐라 자바스크립트 등 프레임워크를 사용하지 않은 순수 개발은 정해진 규격이 없기 때문에 개발하는 사람들에 따라 중구난방식 코드가 작성된다.
  • 리액트를 사용하므로써 데이터의 흐름과 구성을 일관되게 해준다는 장점이 있다. (강제성 부여)
profile
깔끔하게 코딩하고싶어요

0개의 댓글