웹 페이지나 웹 앱에 있는 HTML 요소들을 구조적으로 표현한 것이다.
DOM은 애플리케이션의 전체 UI를 나타내며 트리 데이터 구조로 표현된다. 여기에는 Web Document에 있는 각 UI 요소에 대한 노드가 포함된다. 웹 개발자가 JavaScript를 통해 콘텐츠를 수정할 수 있기 때문에 매우 유용하다. 또한 구조화된 형식으로 되어 있어 특정 대상을 선택할 수 있고, 모든 코드 작업이 훨씬 쉬워지기 때문에 많은 도움이 된다.
DOM은 트리 구조로 되어 있어서 이해하기 쉽지만, 노드의 수가 많아질 수록 속도가 느려지고 DOM 업데이트에 잦은 오류를 발생시킬 수 있다.
또한, 최근 모던 웹은 SPA(Single Page Application)을 사용한다. 하나의 웹 페이지를 어플리케이션처럼 구성하는 SPA에서는 HTML문서 자체가 하나이며, 여러 동적인 기능이 들어가기 때문에 안그래도 리소스가 모두 합쳐진 무거운 HTML문서를 지속적으로 재 랜더링 해줘야한다는 문제점이 발생하게 되었다.
JavaScript를 공부한적이 있다면 "getElementById()"또는 "getElementByClass()"메서드를 사용하여 DOM의 내용을 수정할 수 있는 것을 알고 있을 것이다.
document.getElementById('some-id').innerValue = 'updated value';
따라서 DOM을 업데이트하는 것은 콘텐츠 변경을 포함할 뿐만이 아니라 훨씬 더 많은 작업들이 요구된다. 또한 CSS를 다시 계산하고 레이아웃을 변경하려면 복잡한 알고리즘이 필요하며 성능에 영향을 미치게 된다.
이처럼 기존에는 화면의 변경사항을 DOM을 직접 조작하여 브라우저에 반영하였다. 하지만 이 방법의 가장 큰 단점은 DOM트리가 수정될 때마다 렌터 트리가 계속해서 실시간으로 갱신된다는 점이다. 즉, 화면에서 10개의 수정사항이 발생하면 수정하 때마다 새로운 랜터트리가 10번 수정되면서 새롭게 만들어지게 되는 것이다.
가상 DOM이란 DOM을 가볍게 만든 JavaScript 표현이라고 할 수 있고 주로 React, Vue.js그리고 ELm에 사용된다. 가상 DOM은 실제로 스크린에 랜더링하는 것이 아니기 때문에 DOM을 직접 업데이트하는것 보다 상대적으로 빠르다.
가상 DOM은 실제 DOM에서 처리하는 방식이 아닌 Virtual DOM과 메모리에서 미리 처리하고 저장한 후 실제 DOM과 동기화 하는 프로그래밍 개념이다. 해당 DOM을 컴포넌트 단위로 쪼개어 HTML컴포넌트 조림품 처럼 다루는 개념인 것이다.
👉 결론적으로 DOM을 반복적으로 직접 조작하면 그 만큼 브라우저가 렌더링을 자주하게 되고 그 만큼 PC자원을 많이 소모하게 되는 문제를 해결하기 위한 기술이다.
그렇다면 도대체 어떠한 부분으로 인해 DOM이느려질까?
이 부분을 이해하기 위해서는 브라우저가 어떻게 작동하는지 알아볼 필요가 있다.
브라우저가 HTML을 전달 받으면 브라우저 렌더 엔진이 이를 파싱하고 DOM노드(Node)로 이루어진 트리를 만든다. 각 노드는 각 HTML 엘리먼트들과 연관되어 있다.
그리고 외부 CSS파일과 각 엘리먼트의 inline 스타일을 파싱한다. 스타일 정보를 사용하여 DOM트리에 따라 새로운 트리, 렌더 트리를 만든다.
Webkit에서는 노드의 스타일을 처리하는 과정을 'attachment'라고 부른다. DOM 트리의 모든 노드들은 'attach'라는 메소드가 있다. 이 메소드는 스타일 정보를 계싼해서 객체 형태로 반환한다.
이 과정은 동기적으로 작업이 진행되며 DOM트리에 새로운 노드가 추가되면 그 노드의 attach메소드가 실행된다.
렌더 트리를 만드는 과정에선 각 요소들의 스타일이 계산되며, 또 이 계산되는 과정에서 다른 요소들의 스타일 속성들을 참조한다.
렌더 트리가 다 만들어지고 나면 레이아웃 과정을 거치게 된다. 각 도드들은 스크린의 좌표가 주어지고, 정확히 어디에 나타나야할지 위치가 주어지게 된다.
그 다음 작업은 렌더링 된 요소들에 색을 입히는 과정이다. 트리의 각 노드들을 거쳐가면서 Paint()메소드를 호출한다. 그러고 나면 스크린에 원하는 정보가 나타나게 되는 것이다.
위에서 DOM을 조작했을 때 어떤 작업이 이뤄지는지 알아보았다. DOM에 변화가 생기면 렌더트리를 재생성하고(모든 요소들의 스타일이 다시 계산되고) 레이아웃을 만들고, 페인팅하는 과정이 다시 반복되는 것이다.
복잡한 SPA에서는 DOM조작이 많이 발생하게 된다. 그 뜻은 그 변화를 적용하기 위해 브러우저가 많은 연산을 해야한다는 소리가 되는 것이고, 전체적인 프로세스를 비효율적으로 만들게 된다.
하지만, Virtual DOM은 뷰에 변화가 있다면 그 변화는 실제 DOM에 적용되기 전에 가상의 DOM에 먼저 적용시키고 그 최종적인 결과를 실제 DOM으로 전달해주게 된다. 이로써 브라우저 내에서 발생하는 연산의 양을 줄이면서 성능 개선이 되는 것이다.
DOM조작의 실제 문제는 각 조작이 레이아웃의 변화, 트리 변화와 렌더링을 일으킨다는 것이다. 그래서 30개의 노드를 하나하나 수정하면 그 뜻은 30번의 레이아웃 계산과 30번의 리렌더링을 초래 한다는 것이다.
Virtual DOM은 DOM 차원에서의 더블 버퍼링이랑 다름이 없는 것이다. 변화가 일어나면 그걸 오프라인 DOM트리에 적용시키고 이 DOM트리는 렌더링 도 되지 않기 때문에 연산 비용이 줄어들게 된다. 연산이 끝나고 나면 그 최종적인 변화를 실제 DOM에 던저주게 하여 딱 한번 레이아웃을 계산하고 리렌더하게 되는 것이다.
레이아웃 계산과 리렌더링의 규모는 커지겠지만 한번만 실행됨으로써 연산의 횟수를 획기적으로 줄일 수 있게 되는 것이다.
사실, 이 과정은 Virtual DOM이 없이도 이뤄질 수 있다. 그냥, 변화가 있을 때 변화를 묶어서 DOM fragment에 적용한 다음에 기존 DOM에 던저주기만 하면 된다.
그러면, Virtual DOM이 해결하려고 하는 건 무엇이냐? 그 DOM fragment를 관리하는 과정을 수동으로 하나하나 작업할 필요 없이 자동화하고 추상화하는 것이다. 그 뿐만아니라, 기존에서 수행하고 있는 작업에서 기존 값 중 어떤게 바뀌었고 어떤게 바뀌지 않았는지 계속 파악하고 있어야 하는데, 이것도 Virtual DOM이 자동으로 알아내준다.
마지막으로 DOM관리를 Virtual DOM이 하도록 함으로써 컴포넌트가 DOM 조작을 요청할 때 다른 컴포넌트들과 상호작용을 하지 않아도 되고 특정 DOM을 조작할 것 이라던지, 이미 조작했다던지에 대한 정보를 공유할 필요가 없다. 즉, 각 변화들의 동기화 작업을 거치지 않으면서 모든 작업을 하나로 묶어줄 수 있다는 것이다.
How to Browsers work
reference
정아마추어 코딩블로그
브라우저 렌더링 과정 알아보기