본문 바로가기

[React] Virtual DOM

[React] Virtual DOM
*  Virtual DOM이 나오게 된 배경
*  Virtual DOM의 개념
*  React가 DOM 트리를 탐색하는 방법
*  DOM 엘리먼트의 타입이 같을 때와 다를 때의 React 동작 방식

 

Virtual DOM이 나오게 된 배경

 

Virtual DOMReal DOM의 가벼운 사본과 같다. Real DOM은 Browser에서 생성되는 Document Object Model을 가리키는 용어이며, Virtual DOM과 구분하기 위해 이 용어를 사용한다.

 

Real DOM (DOM)

DOM은 Document Object Model의 약자로, 뜻을 그대로 풀자면 문서 객체 모델을 의미한다.

  • 여기서 문서 객체란 브라우저가 JavaScript와 같은 스크립팅 언어가 <html>, <head>, <body>와 같은 태그들에 접근하고 조작할 수 있도록 문서를 트리 구조로 객체화한 것을 의미한다.
  • 다시 말하자면, DOM은 브라우저가 HTML 문서를 조작할 수 있도록 트리 구조화한 객체 모델이다.
  • 트리 구조로 DOM 객체가 이뤄져 있기 때문에 JavaScript는 쉽게 DOM 객체에 접근할 수 있고, DOM 객체를 조작할 수 있다.

 

 

따라서 이미지의 왼쪽과 같은 HTML 문서가 있을 때 이를 브라우저에서 읽는다면 이미지의 오른쪽과 같은 DOM이 생성된다. 그리고 querySelector, addEventListener와 같은 DOM API를 통해 문서의 요소들을 조작할 수 있다.

 

DOM의 조작 속도가 느려지는 이유

DOM은 계층적 형태의 트리 구조로 구성되어 있다.

  • 자료구조 중에서도 특히 트리는 “데이터 저장"의 의미보다는 “저장된 데이터를 더 효과적으로 탐색”하기 위해 사용되므로, 빠른 자료 탐색 성능이 장점인 자료구조라고 볼 수 있다.
  • 그렇기 때문에 그런 트리 구조로 된 DOM은 JavaScript와 같은 스크립팅 언어가 접근하고 탐색하는 속도가 빠르기 때문에 변경 및 업데이트 속도 또한 빠르다고 할 수 있다.

 

그러나 DOM이 변경되고 업데이트가 된다는 것은 결국 브라우저의 렌더링 엔진 또한 리플로우(Reflow)한다는 것을 의미한다.

 

브라우저의 렌더링 과정

 

브라우저는 렌더링 과정에서 DOM 트리와 CSSOM 트리를 토대로 Render 트리를 생성하고 각 요소가 배치될 공간을 계산(Layout)한 뒤 이를 화면에 그려(Paint)낸다.

 

 

만약 DOM이 변경된다면 업데이트된 요소와 그에 해당하는 자식 요소들에 의해 DOM 트리를 재구축하게 된다.

  • 그 과정에서 이에 대한 레이아웃 재연산을 수행하는 리플로우, 그리고 이를 화면에 그려내는 Repaint 과정을 거치게 된다.
  • 실제 DOM을 조작하는 경우, 변화가 필요하지 않은 요소도 변경될 수 있다.
  • 이때 변화가 필요 없는 부분도 변경되면서 잦은 리플로우 발생으로 인해 성능을 떨어뜨리는 문제를 야기하게 된다.
  • JavaScript를 통한 DOM 조작이 많아질수록 이에 대한 브라우저의 리플로우가 발생하므로 DOM 업데이트 비용이 커질 수 있다.

 

[예시]

6개의 콘텐츠가 있는 화면에서 단 1개의 콘텐츠만 변경되어야 하는 경우, 나머지 5개의 콘텐츠는 신경 쓰지 않고 해당 콘텐츠만 변경되어야 하지만, 실제 DOM을 조작하는 경우 해당 콘텐츠만 변경될 뿐만 아니라 나머지 콘텐츠도 다시 그리게 될 수 있다.

 

위의 예시는 브라우저의 성능에 크게 문제를 끼치지 않겠지만, 대부분의 모던 웹에서는 많은 양의 DOM 조작이 이루어지기 때문에 리플로우로 인한 비효율적인 업데이트가 빈번하게 발생할 가능성이 있다.  이러한 경우 극단적인 예로 프레임 드롭(Frame Drop)과 같은 치명적인 UX 문제가 발생할 수 있다.

  • 프레임 드롭은 웹 애플리케이션에서 프레임 레이트(Frame Rate)가 떨어져 화면이 버벅대거나 부드럽게 표시되지 않는 것이다.
  • 이는 사용자 경험을 저하시키고, 애플리케이션의 성능을 떨어뜨릴 수 있다.
  • 특히 DOM 조작이 빈번하게 일어날 때 더욱 발생할 가능성이 높다.
    • 프로그래밍 언어로 조작하는 DOM은 애플리케이션 UI의 상태가 변경될 때마다 해당 변경 사항을 나타내기 위해 업데이트가 된다.
    • 이런 DOM을 조작하는 정도가 잦다면, DOM의 렌더링은 브라우저의 파워에 의존하기 때문에 DOM의 조작 속도는 느려지게 된다.

 

결국 바뀐 부분만 비교해서 그 부분만 렌더링할 수는 없을까?“라는 아이디어를 기반으로 React의 Virtual DOM이 등장하게 되었다.

 


 

Virtual DOM이란?

 

 

React에는 Virtual DOM이라고 하는 가상의 DOM 객체가 있다.

  • 이 가상의 DOM 객체는 실제 DOM 객체에 대응하는, 사본같은 개념이다.
  • 상대적으로 무거운 DOM에 비하여 React의 가상 DOM 객체는 자바스크립트 객체로 이루어져 있기 때문에 실제 DOM 객체와 동일한 속성을 가지고 있음에도 “훨씬 가벼운 사본”이라고 표현할 수 있다. 
  • 실제 DOM에 비해 가상 DOM의 조작 속도가 훨신 빠르다.

 

React는 실제 DOM 객체에 접근하여 조작하는 대신, 이 가상의 DOM 객체에 접근하여 변화 전과 변화 후를 비교하고 바뀐 부분만 적용할 수 있게 된다. 

  • 이는 React가 UI의 상태를 추적하고 변화가 일어난 요소들을 빠르게 업데이트하기 위함이다. 
  • 다만 가상 DOM 객체는 말 그대로 가상이기 때문에 비교를 위해 사용되며, 실제 DOM 객체처럼 화면에 표시되는 내용을 직접 변경하는 것은 아니다.

 

가상 DOM은 실제 DOM과 동기화되어, 상태가 변경될 때마다 가상 DOM을 새로 생성하여 이전 상태와 비교한다.

  • Virtual DOM은 가상의 UI 요소를 메모리에 유지시키고, 그 유지시킨 가상의 UI 요소를 ReactDOM과 같은 라이브러리를 통해 Real DOM과 동기화시키는 역할만 한다. (실제 브라우저의 화면에 표시되는 내용을 변경하는 것은 Real DOM이다.)
  • 그리고 변경이 필요한 부분만 실제 DOM에 반영하여 업데이트하므로, 전체 UI를 다시 그리지 않아도 된다.
  • 이를 통해 화면 업데이트 시간과 비용을 절약할 수 있다.

 

Virtual DOM의 형태

가상 DOM은 추상화된 자바스크립트 객체의 형태를 가지고 있다.

 

예 ) DOM 트리 

 

 

이 DOM 트리는 다음과 같이 JavaScript 객체로도 표현할 수 있다.

 

const vDom = {
	tagName: "html",
	children: [
		{ tagName: "head" },
		{ tagName: "body",
			children: [
				tagName: "ul",
				attributes: { "class": "list"},  //class라는 예약어가 있기 때문에 string으로
				children: [
					{
						tagName: "li",
						attributes: { "class": "list_item" },
						textContent: "List item"
					}
				]
			]
		}
	]
}

 

이것을 가상 DOM이라고 생각해 보자.

  • 실제 DOM과 마찬가지로 가상 DOM 또한 HTML 문서 객체를 기반으로 한다.
  • 또한 추상화만 되었을 뿐 평범한 자바스크립트 객체이므로 실제 DOM을 건드리지 않고도 필요한 만큼 자유롭게 조작할 수 있다.
  • 가상 DOM은 리액트에서 컴포넌트의 상태나 속성이 변경될 때마다 새로 생성되며, 리액트는 이전 가상 DOM과 새로운 가상 DOM을 비교하여 변경된 부분만 실제 DOM에 반영한다.

 

Virtual DOM의 동작 과정

 

 

다음과 같은 동영상 사이트에서 특정 썸네일 클릭 시, 모달 플레이어가 생성되는 경우를 가정해보자.

  • 리액트는 상태를 변경하는 작업(e.g. 이벤트)이 일어났을 때, 가상 DOM에 저장된 이전 상태와 변경된 현재 상태를 비교한다.
  • 이 비교 과정에서 React는 Diffing 알고리즘을 사용하여 변경된 부분을 감지한다.
  • 따라서 React에서 상태를 변경하는 경우에는 Diffing 알고리즘에서 이를 감지할 수 있도록 직접 할당이 아닌 setState와 같은 메서드를 활용해 상태를 변경한다.

 

 

그리고 가상 DOM과 변경된 새로운 가상 DOM을 비교하여 변경이 필요한 부분만 실제 DOM에 반영하여 업데이트한다.

  • 이것을 Reconciliation, 즉 재조정이라고 한다.
  • 이 과정에서 여러 개의 상태 변화가 있을 경우 이를 일일이 수행하지 않고 일괄적으로 한 번에 업데이트(Batch Update)한다.
  • 이를 통해 성능을 최적화하고 불필요한 리렌더링을 최소화할 수 있다.

 

Virtual DOM은 빠르다?

가상 DOM은 일반적으로 실제 DOM을 직접 조작하는 것보다 빠르다는 것이 맞지만, 모든 경우에 그렇지는 않다.

  • 때로는 직접 DOM을 조작하는 것이 더 빠를 수 있다.
  • 실제 DOM 조작이 느려지는 대부분의 경우는 DOM 조작이 발생할 때마다 브라우저에서 다시 레이아웃을 계산하고, 페이지를 다시 그리기 때문이다.
  • 이러한 렌더링 과정에서 발생하는 비용이 크기 때문에, 가상 DOM을 활용하여 실제 DOM 조작을 최소화하는 것이 성능 최적화의 핵심이라고 볼 수 있다.

 

결론 : 작성하는 코드가 렌더링에 어떤 영향을 미칠지를 고려하여 최적화 작업에도 관심을 기울여야 한다.

728x90
⬆︎