티스토리 뷰

DOM이란?

https://hahagarden.tistory.com/76

 

DOM과 브라우저 동작원리

DOM이란 DOM은 Document Object Model 문서객체모델의 약자이다. 웹페이지의 구조를 나타낸 text일뿐인 HTML파일이 DOM을 통해 태그 하나하나 생명력을 얻는다. DOM은 연결다리이다. DOM은 HTML, XML문서의 프로

hahagarden.tistory.com

 

브라우저의 렌더링

브라우저의 렌더링 과정

서버에서 HTML, CSS, JavaScript, 이미지파일 등을 응답을 보내주면 브라우저의 렌더링 엔진이 DOM트리와 CSSOM트리를 만들고 렌더트리로 병합한다. 그리고 그 후에 position이나 size 등 CSS가 계산되고 화면의 좌표에 맞춰서 배치가 되고 색칠되고 그려진다. 

DOM트리를 만드는 과정 자체는 느리지 않다. DOM을 재생성하는 것은 크게 문제가 되지 않는다. 그러나 DOM이 만들어지고 렌더트리가 만들어지면, 그 이후의 브라우저 작업이 오래걸린다. 많은 연산을 하고 시간을 잡아 먹어서 비용이 드는 작업이다. 페이지가 느려지고 다량의 DOM요소들이 변경된다면 무거워질 것이다. 렌더트리 이후의 작업을 최소화하기 위해서 DOM을 자주 업데이트하지 않으려는 방법들이 생겼다.

 

요소가 변경될 때 DOM에 무슨 일이?

const $ul = document.getElementById("ul");

["Apple", "Banana", "Orange"].forEach((text) => {
  const $li = document.createElement("li");
  const textNode = document.createTextNode(text);
  $li.appendChild(textNode);
  $ul.appendChild(li);
});

ul 리스트에 Apple, Banana, Orange에 추가하려고 자바스크립트에서 DOM을 조작하고 있다.
이 때 Apple을 추가할 때 DOM이 변경되고, Banana를 추가할 때 또 변경되고, Orange를 추가할 때 한번 더 변경된다.
li를 추가할 때마다 DOM이 변경되는 것이다.
DOM이 변경되면 렌더트리가 재생성되고 렌더트리를 기반으로 브라우저는 다시 CSS를 계산하고 layout을 배치하고 페인트를 할 것이다.
브라우저가 부담되는 작업을 3번을 한다. 

이것을 보완하기 위해 다음의 코드를 통해 Apple, Banana, Orange를 하나의 요소($container)로 묶고 그 요소를 추가할 수 있다.

const $ul = document.getElementById("ul");
const $container = document.createElement("div");

["Apple", "Banana", "Orange"].forEach((text) => {
  const $li = document.createElement("li");
  const textNode = document.createTextNode(text);
  $li.appendChild(textNode);
  $container.appendChild(li);
});

$ul.appendChild($container);

Apple, Banana, Orange 를 $container에 담고 DOM요소인 $ul에 자식노드로 추가한다.
이 경우에는 마지막줄에서 한 번만 DOM이 변경되기 때문에 브라우저에 부담되는 작업은 1번만 발생한다.

그렇지만 이것은 불필요한 div($container)요소를 DOM에 추가했다. 이는 바람직하지 않다. 다음의 내용을 보자.

DocumentFragment

const $ul = document.getElementById("ul");
const $fragment = document.createDocumentFragment();

["Apple", "Banana", "Orange"].forEach((text) => {
  const $li = document.createElement("li");
  const textNode = document.createTextNode(text);
  $li.appendChild(textNode);
  $fragment.appendChild(li);
});

$ul.appendChild($fragment);

DocumentFragment 노드를 통해서 해결할 수 있다. 기존 DOM과 별도로 존재하여 이전 예시의 $container역할을 할 수 있다.
다른 점은, 마지막 줄의 ul에 자식노드로 추가될 때 DocumentFragment 자신은 사라지고 자식노드들만 DOM에 추가한다.
불필요한 요소가 DOM에 추가되지 않는 것이다.
이전 예시의 문제점을 해결하고 DOM도 한번만 변경되므로 무거운 작업을 한 번만 할 수 있는 효율적인 방법이다.

 

DOM의 문제점

DOM을 한 번만 변경시킬 수 있는 방법은 있다. 그리고 과거의 정적인 웹페이지는 자주 DOM조작을 할 일이 없어 충분했다.
하지만 현대의 웹이 크고 복잡해지며 DOM tree가 거대해졌다. document, html노드에서 시작해서 수많은 가지가 뻗어있다. 페이스북의 포스트 한 개를 표현할 때 사용하는 <div>는 약 100개이다.
DOM을 변경하려면 수많은 가지를 거치며 변경하고자 하는 노드를 탐색하고, 처음부터 다시 렌더링을 한다.
이렇게 큰 웹페이지의 댓글이나 좋아요 등 실시간으로 변하는 요소들을 모두 DOM조작하여 업데이트하려면 성능이 떨어질 것이다. 또한 한 컨텐츠당 수십개 이상의 요소를 가지고 있는데 DOM을 적게 변경시키기 위해 이 많은 요소들에 대해서 어떤 요소가 바뀌면 어떻게 묶고, 어떤 것은 바뀌지 않도록 수동으로 작업을 해주는 것은 굉장히 비효율적일 것이다.

 

그래서 사용하는 Virtual DOM

가상 DOM은 실제 DOM tree의 노드를 탐색하는 것이 아니라, 실제 DOM을 복사한 사본을 메모리에 만들고 가상 DOM이 전, 후에 달라진 것이 있는지 틀린그림찾기를 하고 달라진 것들을 모아 한번에 실제 DOM을 변경한다.
복사한 사본은 자바스크립트 객체형태로 저장하기 때문에 가상 DOM에 접근하고 수정하는 것은 가볍고 빠른 작업이다.(DOM이 아니라 자바스크립트를 조작하는 것이다.) 가상 DOM에서 달라진 요소를 찾으면 그것들만 묶어서 실제 DOM에 반영한다. DOM이 한 번만 변경되는 것이다. 또한 전체 트리를 다시 그리는 것이 아닌 변경된 요소들만 업데이트된다.

Virtual DOM은 DOM 변경이 필요한 요소들에 대해서만 한 번만 일어나게 해주며 브라우저의 계산, 스타일링 과정을 줄일 수 있도록 하여 DOM 조작을 효율적으로 할 수 있도록 한다.

Virtual DOM을 사용하는 것이 항상 좋을까?

Virtual DOM이 DOM보다 무조건 빠를까? 아니다. 정보제공만 하는 웹페이지라면 잦은 인터랙션이 발생하지 않기 때문에 DOM을 사용하는 것이 성능이 좋다. Virtual DOM도 최종적으로 DOM을 조작하는 것이기 때문에 바로 DOM을 조작하는 것에 중간단계가 추가된 것이 된다. 과정이 늘어났으니 더 빠를리 없다.

하지만 댓글, 좋아요, 장바구니 등 컨텐츠가 자주 바뀌며 사용자와 인터랙션을 많이 하는 웹 페이지는 브라우저에 부담이 되는 작은 DOM조작을 여러 번 하지 않도록 Virtual DOM이 대안이 된다. 이런 Virtual DOM을 쓰는 라이브러리 또는 프레임워크는 React와 Vue가 있다. 
Virtual DOM을 사용하지 않고 더 빠르게 DOM을 조작할 수 있는 프레임워크인 Svelte도 있다. Svelte에 대해서는 추후 알아보겠다.

 

Reference

https://velopert.com/3236

https://callmedevmomo.medium.com/virtual-dom-react-%ED%95%B5%EC%8B%AC%EC%A0%95%EB%A6%AC-bfbfcecc4fbb

https://www.youtube.com/watch?v=PN_WmsgbQCo&t=137s

https://www.youtube.com/watch?v=6rDBqVHSbgM 

반응형
댓글