Web/techTalk

Virtual DOM 을 왜 사용하나요? (Feat. React reconciliation)

레에몽 2022. 7. 5. 15:57

React, Vue 등 Virtual DOM을 채택한 경우가 많이 있습니다. DOM 이 아니라, Virtual DOM을 선택해야 하고, 사용해야 하는 이유에 대해서 알아봅시다.

 

DOM (Document Object Model)

 Virtual DOM 을 알기 전에 실제 DOM에 대해서 간단하게만 알아봅시다. DOM 은 문서의 구조화된 표현을 제공하고 있으며, 웹페이지를 스크립트 또는 프로그래밍 언어들에서 사용될 수 있게 연결시켜주는 담당을 합니다.

 

간단하게 얘기하면 개발자들은 DOM 을 만들어내고, DOM 은 웹페이지를 구성하면서 사용자들에게 보여지는 역할을 담당합니다.

 

Browser Workflow (webkit)

 

Webkit 에서 Browser가 작동하는 과정을 도식화한 모습입니다. 간단하게 내용을 살펴보면

 

1. DOM Tree

  • 브라우저가 HTML을 전달 받으면, 브라우저 엔진이 파싱하고 DOM Node Tree를 생성합니다.

2. Attachment

  • Webkit 에서 Node의 Style을 처리하는 과정으로, 스타일 정보를 계산해서 객체 형태로 반환합니다.
  • 동기적으로 작업되며, 새로운 노드가 추가되는 경우 attach 메소드가 실행이 됩니다.

3. Render Tree

  • 외부 CSS 파일과 엘리먼트의 inline 스타일을 파싱해서 새로운 Render Tree를 생성합니다.

4. Reflow(Layout)

  • 각 Node들의 geometry를 반환하며, 어디에 나타나야 할 지 위치로 자리 잡습니다.
  • 여기서는 실제 위치를 계산해야 하기 때문에 "한 번의 연산자체만 해도 무겁습니다."

5. Paint

  • 각 Node에 색을 입히는 과정으로 실제로 유저가 보는 단계입니다.

 

 

https://junilhwang.github.io/TIL/Javascript/Design/Vanilla-JS-Virtual-DOM/#_1-reflow-repaint

reflow(layout) 과정이 시간이 오래걸린다는 사진입니다.

 

Virtual DOM

 DOM에 대해서 알아봤으니까, Virtual DOM에 대해서도 알아봅시다. Virtual DOM (VDOM)은 UI의 이상적인 또는 “가상”적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 “실제” DOM과 동기화하는 프로그래밍 개념

 

 개념만 보아서는 어떤 뜻인지 짐작하기 어렵습니다. 간단하게 얘기를 드리면, Virtual DOM은 유저와 DOM 사이의 하나의 버퍼라고 생각을 해주시면 됩니다.

 

 유저가 어떤 행동을 하게 되면 DOM에 직접적으로 update를 하지 않고, Virtual DOM에 우선적으로 update를 하고 Virtual DOM에서 DOM으로 업데이트를 한 번에 하게 됩니다.

 

 그렇다면 왜 이런 작업을 거쳐야 하는걸까요?

 

Virtual DOM을 왜 사용하지?

 저는 Virtual DOM이라는 개념을 처음 접했을 때 의문이 들었습니다. Virtual DOM을 사용해야 하는 이유가 무엇이지? 이미 기존에 있던 작업들의 성능은 점점 개선이 되고 있지 않나? 라는 생각을 했습니다.

 

 크롬을 기준으로 얘기를 해보면, 자바스크립트 엔진 V8은 C++ 기반으로 성능이 충분히 빠르고, 렌더링 엔진 blink도 layoutNG, renderNG, 여러 스레드를 사용하면서 성능을 개선하고 있는데 왜 굳이 Virtual DOM을 사용하지?

 

그래서 vanlia랑 여러 라이브러리를 비교하는 benchmark를 확인해봤습니다.

 

JS framework Benchmark

 

https://github.com/krausest/js-framework-benchmark

 

GitHub - krausest/js-framework-benchmark: A comparison of the perfomance of a few popular javascript frameworks

A comparison of the perfomance of a few popular javascript frameworks - GitHub - krausest/js-framework-benchmark: A comparison of the perfomance of a few popular javascript frameworks

github.com

 

 

해당 github에서 벤치마크를 한 결과는 다음과 같습니다. (Vanila vs React)

뭔가 이상하다고 생각이 드시나요? Virtual DOM을 사용하는 React가 오히려 속도가 느려서 "성능의 병목"을 담당하고 있습니다.

 

해당 benchmark로는 단순하게 생각해보면 React가 안좋아보입니다. 그 이유는 benchmark 내에서 "한 행동을 실행"하는 데에 초점을 두는 부분이 많기 때문입니다.

 

Virtual DOM의 특징은 아까 글에 적었던 대로, 작업 사항을 바로 DOM에 작업하는 것이 아니라 Virtual DOM에 작업하고 "DOM update를 단 한 번"만 작업합니다. 따라서 특정 행동만을 하는 것은 오히려 두 단계(변경 -> virtual dom -> dom)를 거쳐야 하는 상황이니 성능이 저하될 수 밖에 없는 것이죠.

 

Virtual DOM을 사용함으로써 얻는 행동은 DOM Browser work에서 보았듯이 성능의 저하를 유발시킬 수 있는 reflow를 최소한 하는 것입니다. 굳이 같은 계산을 2번, 3번 ... 할 필요가 없다는 것이 가장 큰 장점입니다.

 

새로운 Tree를 만들어보자

그리고 저희는 DOM Tree, Render Tree 등 Tree 형태로 만들어내는데, React 공식홈페이지에서 여럿 봤듯이 실제 DOM만 이용하게 되면 하나의 Tree를 가지고 다른 Tree로 변환하기 위한 알고리즘은 n개의 Element가 있는 Tree에 대해서 O(n^3)의 시간복잡도가 요구됩니다.

 

근데 여기서 의문점이 하나 더 생겼습니다. O(n^3)이라는 시간복잡도 자체가 큰 것은 맞지만, 실제 작동할 때에도 유의미한 차이를 만들어 낼 정도로 n이 충분히 큰가?라는 생각을 했습니다.

 

한 페이지에 Element가 몇 개가 존재할까요? 페이지마다 너무 다르겠지만 저는 많아야 몇 백개로 생각했었습니다.

 

100개 정도의 Element가 있다면 100 * 100 * 100 = 1백만 정도로, 컴퓨터가 감당하기에는 충분히 빠른 시간 내에 할 수 있는 연산량이라고 생각했습니다.

 

lighthouse DOM 개수 경고

그래서 Lighthouse를 기준으로 잡아봤습니다. Lighthouse에서는 1500개의 DOM Element를 넘어가지 말라고 권장하고 있습니다. 1500개가 아니더라도 1000개정도만 되어도 1000 * 1000 * 1000 = 10억 정도로 컴퓨터가 빠르게 연산하기에는 무리일정도의 연산량이 생기기 시작합니다.

 

따라서 실제 DOM을 업데이트 해버린다면 Reflow 등 여러 연산과 Tree를 만들어내기에는 너무나도 많은 연산을 할 확률이 커진다는 것을 알 수 있습니다.

 

그렇다면 React에서는 해당 연산을 어떻게 더 최적화 했을까요?

 

React Virtual DOM

 그래서 React에서는 Virtual DOM을 활용하면서도, 두 가정을 기반으로 새로운 Tree를 만드는 데에 O(n)정도의 시간복잡도를 가지게 만들었습니다.

 

1. 서로 다른 타입의 두 Element는 서로 다른 트리를 만듭니다.

div 태그가 section 태그로 바뀌는 상황이 있다면 내부 속성이 그대로여도 새로운 Tree를 만든다는 뜻입니다.

2. key prop을 통해 어떤 자식 Element가 변경되지 않아야 하는지 표시합니다.

react를 사용하다보면 map 고차함수를 적을 때 key prop을 적어달라고 log가 찍힙니다. key prop을 토대로 변경해야 하는 자식과 변경하지 않아도 되는 자식을 쉽게 구별해냅니다.

 

그렇게 React는 변화해야 하는 부분만 다시 만들어내는 reconciliation 과정을 통해 Rendering을 더 최적화 시킵니다.

 

React Reconciliation

React에서는 위에서 언급했듯이 바뀌는 부분만 Re-Rendering 됩니다.

해당 빨간색으로 박스친 부분만 다시 바뀌게 됩니다.

 

이러한 이유로 React가 Virtual DOM이라는 기능을 채택했다고 생각합니다. 실제로 React는 Fiber Architecture을 사용하면서 더블 버퍼의 형식과 우선순위를 이용해서 Rendering을 더 최적화 하고 있습니다. Fiber Architecture가 궁금하시다면 기존 포스트 글을 확인해 주세요.