Virtual DOM 동작 원리와 이해 (with 브라우저의 렌더링 과정)
Virtual DOM?
1. Virtual DOM이란?
→ "DOM을 추상화한 가상의 객체"
DOM을 추상화한 가상의 객체라고 표현해봤습니다. (개인이 내린 정의)
그러면 우선 저 문장을 이해하기 위해서 DOM이란 뭔지 알아야합니다.
1.1 DOM(Document Object Model)
DOM은 문서 객체 모델이라고 하는데 결국은 브라우저에서 다룰 HTML 문서를 파싱하여 "문서의 구성요소들을 객체로 구조화하여 나타낸 것"입니다.
DOM은 HTML Elements, Attributes, CSS styles, Events, Methods 등을 제어할 수 있는 표준 인터페이스를 제공합니다.
단순하고 실용적인 측면에서 다시 정의하면 "웹 페이지를 구성하는 요소를 구조화해서 나타낸 객체고 이 객체를 이용해서 웹 페이지 구성요소를 제어할 수 있다"라고 이해하면 된다.
그러면 Virtual DOM은 왜 이 DOM을 추상화했고, 가상의 객체라고 표현했을까요?
그건 Virtual DOM이 어떤 문제를 해결하기 위한 기술인지를 파악해보면 이해하기 쉽습니다.
2. 어떤 문제를 해결하기 위한 기술인가?
- DOM 조작에 의한 렌더링이 비효율적인 문제
- SPA(Single Page Application)특징으로 DOM 복잡도 증가에 따른 최적화 및 유지 보수가 더 어려워지는 문제
결론적으로 DOM을 반복적으로 직접 조작하면 그 만큼 브라우저가 렌더링을 자주하게 되고, 그 만큼 PC 자원을 많이 소모하게되는 문제를 해결하기 위한 기술입니다.
조금 더 상세하게 설명해보겠습니다.
document.getElementById("hello").innerHTML = 'hello world!'
document.body.style.background = 'blue'
위와 같이 자바스크립트로 DOM에 접근하여(메서드를 이용해서) 내용을 바꾸거나 스타일을 바꿀 수 있습니다.
예전의 jQuery와 같은 라이브러리도 위와 같이 DOM의 메서드를 이용해서 DOM을 바꾸었고 그럴 때마다 DOM이 변경된 것을 인지한 브라우저 엔진은 렌더링을 다시 했습니다.
2.1 간단히 짚고 넘어가는 브라우저의 렌더링 방법
앞서 DOM을 반복 조작하면 렌더링도 자주한다고 얘기했는데 그건 브라우저의 렌더링 방식이 그렇게 되어있기 때문입니다.
위 그림은 브라우저 렌더링과 관련하여 많이 본 그림일 것입니다.
간단하게 설명하면 과정은 아래와 같습니다.
- HTML을 파싱하여 DOM 객체를 생성하고, CSS를 파싱하여 스타일 규칙을 만듭니다.
- 이 두개를 합쳐서 실제로 웹 브라우저에 보여져야할 요소를 표현한 "렌더 트리" 라는 것을 만듭니다.
- 이 렌더 트리를 기준으로 레이아웃을 배치하고 색을 칠하는 등의 작업을 합니다.
이 과정에서 문제가 되는 경우는 현대의 웹처럼 변경해야할 대상도 많고 변경도 많은 경우입니다.
프로그래밍에 의해 DOM을 변경해야하고 변경할 구성 요소가 100개면 위의 과정을 100번을 하는 비효율적인 작업을 해왔습니다.
정확히는 DOM을 변경하는게 문제가 아니고 렌더링을 여러번 하는게 문제입니다.
그렇게까지 비효율은 아니야...
브라우저라고 진짜 위에서 설명한대로 무식하게 100번 렌더링하지는 않습니다.
이해를 돕기 위한 예시고 사실은 DOM을 제어하는 메서드에 따라서 다르지만 어느정도 합쳐서 배치로 작업을 할 수 있습니다. (Batched DOM Update)
그냥 이해를 돕기 위해 100개가 변경되면 20번 렌더링이 된다고 가정합니다.
그래도 여전히 비효율인 것은 맞습니다.
그럼 어떻게 해결했을까요?
3. 어떻게 해결했는가? (동작 원리)
이제 아까 설명한 추상화한 가상의 객체를 설명할 시간입니다.
해결 방법은 바로 Virtual DOM이라는 DOM을 추상화한 가상의 객체를 메모리에 만들어 놓는 것입니다.
Virtual DOM은 DOM과 유사한 역할을 담당할 객체입니다.
즉, 변경 사항을 DOM에 직접 수정하는게 아니라 중간 단계로 Virtual DOM을 수정하고 Virtual DOM을 통해서 DOM을 수정하게 했습니다.
실질적인 방법은 Virtual DOM에 변경 내역을 한 번에 모으고(버퍼링) 실제 DOM과 변경된 Virtual DOM의 차이를 판단한 후, 구성요소의 변경이 부분만 찾아 변경하고 그에 따른 렌더링을 한 번만 하는 것으로 해결했습니다.
* DOM과 Virtual DOM에서 변경된 부분을 비교하는 알고리즘에 대해서는 설명하지 않겠습니다.
만약 어떤 게시판에서 다음 페이지를 눌러 리스트 10개(<ul>
태그안의 <li>
태그)를 변경하는데 DOM에 접근하여 <li>
10번을 바꾸지 않고 Virtual DOM을 통해 리스트 10개(<li>
태그)를 변경하고 Virtual DOM과 실제 DOM을 비교하여 변경된 부분(<ol>
통채로)을 1번만에 변경하여 렌더링도 1번만 일어나게 하는 것입니다.
추상화했다는 얘기는 DOM을 제어하는 API를 직접 호출하지않고 Virtual DOM을 제어하는 것을 React나 Vue같은 프레임워크가 알아서 하도록 추상화했다는 얘기가 사실 더 정확합니다. (실질적인 DOM관리를 Virtual DOM이 하게됩니다.)
그 뿐만 아니라 가상의 객체라는 것도 실제 DOM과 유사한 역할을 한다고 생각하여 가상이라고 붙여봤습니다.
4. 주의사항과 한계
- 0.1초마다 화면에 데이터가 변경된다면? Virtual DOM으로 0.5초씩 모아가지고 렌더링을 적게할 수 있을까? → 안된다. 동시에 변경되는 것에 한해서만 렌더링된다.
- React나 Vue등을 이용해서 Virtual DOM을 쓰면 무조건 빠른가? → 아니다. 똑같이 최적화를 해야한다. (슬라이드를 옮기거나 무한 스크롤등의 움직임이 있을 때는 Virtual DOM을 이용해서 반복 렌더링을 하지 않도록 해줘야한다.)
- Virtual DOM은 메모리에 존재한다. DOM에 준하는 무거운 객체(Virtual DOM)가 메모리에 상주(?)하고 있기 때문에 메모리의 사용이 많이 늘어날 수 밖에 없다.
- Virtual DOM을 조작하는 것도 엄청나게 많은 컴포넌트를 조작하게 된다면 오버헤드가 생기기 마련이다. Virtual DOM 제어가 DOM 직접 제어에 비해 상대적으로 비용이 적게 들 뿐이다.
참고 사이트
https://d2.naver.com/helloworld/59361
https://www.w3schools.com/js/js_htmldom.asp
http://blog.drakejin.me/React-VirtualDOM-And-Repaint-Reflow/