React Virtual DOM
Virtual DOM이란?
공식 문서에서는 Virtual DOM을 이렇게 정의한다.
Virtual DOM (VDOM)은 UI의 이상적인 또는 “가상”적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 “실제” DOM과 동기화하는 프로그래밍 개념입니다. 이 과정을 재조정이라고 합니다.
이 접근방식이 React의 선언적 API를 가능하게 합니다. React에게 원하는 UI의 상태를 알려주면 DOM이 그 상태와 일치하도록 합니다. 이러한 방식은 앱 구축에 사용해야 하는 어트리뷰트 조작, 이벤트 처리, 수동 DOM 업데이트를 추상화합니다.
“virtual DOM”은 특정 기술이라기보다는 패턴에 가깝기 때문에 사람들마다 의미하는 바가 다릅니다. React의 세계에서 “virtual DOM”이라는 용어는 보통 사용자 인터페이스를 나타내는 객체이기 때문에 React elements와 연관됩니다. 그러나 React는 컴포넌트 트리에 대한 추가 정보를 포함하기 위해 “fibers”라는 내부 객체를 사용합니다. 또한 React에서 “virtual DOM” 구현의 일부로 간주할 수 있습니다.
공식 문서의 해석은 이해하기 조금 어렵다. 그렇기 때문에 조금 더 쉽게 설명을 해보자면 이렇게 할 수 있다.
Virtual DOM은 프로그래밍적 개념으로 실제 DOM과 반대되는 이상적이고 가상의 위치에 있다. 즉 프로그래밍으로 시스템 내부에 구현되어 있는 것으로 실제로 사람이 볼 수 있는 것은 아니다. 표현되는 UI는 메모리에 저장되고 ReactDOM과 같은 라이브러리에 의해 실제 DOM과 동기화된다. 미리 Virtual DOM에 UI를 구현하고 ReactDOM에 의해 실제 DOM과 동기화되는 과정을 재조정이라고 한다.
Virtual DOM의 등장
Virtual DOM은 2013년 Facebook에서 React를 만들면서 등장했다. Virtual DOM은 UI를 렌더링하는 과정에서 발생하는 비용을 줄이기 위해 고안된 개념이다.
DOM
DOM은 Document Object Model의 약자로 HTML 문서를 객체로 표현한 것이다. HTML 문서를 트리 구조로 표현하고, 각 노드는 객체로 표현된다. DOM을 사용함으로써 개발자들이 자바스크립트를 활용해서 콘텐츠들을 쉽게 조작할 수 있다. 또한 구조화된 형태는 특정 대상을 쉽게 선택할 수 있고 모든 코드가 한층 더 쉽게 동작할 수 있게 해준다.
자바스크립트 인터프리터는 HTML 문서상의 (<div>Hello World!</div>)
를 해석할 수 없다.
대신에 DOM으로 파싱된 트리에서 매핑된 div DOM을 찾아서 해석한다.
구조화된 DOM
DOM은 트리나 어떤 숲의 하나로 생각할 수 있다. Structure Model은 때때로 문서의 표현을 트리로 표현하는 것을 의미한다. 트리의 각각의 브랜치들은 노드에서 끝나게 된다. 그리고 각각의 노드는 노드에 추가 될 수 있고 발생한 이벤트의 트리거 역할을 하는 이벤트 리스너와 같은 객체를 포함하고 있다. DOM Structure의 중요한 속성은 Structural Isomorphism - 구조적 동형으로 두 개의 DOM 구현이 동일한 문서의 표현을 생성하는 것에 사용되는 경우 정확히 동일한 개체 및 관계를 사용하여 동일한 구조 모델을 생성하는 것을 말한다.
왜 Object Model일까?
일단 문서는 객체를 활용해서 모델링 되기 때문에 객체 모델이라고 부른다. 그리고 모델은 문서의 구조 뿐만 아니라 문서의 행동, 태그로 구성되어진 HTML과 같은 객체를 포함한다.
DOM의 예시
<html>
<head>
<title>DOM Example</title>
</head>
<body>
<div id="container">
<h1>DOM Example</h1>
<p>This is a DOM example</p>
</div>
</body>
</html>
이때 파싱되는 HTML 문서의 DOM 트리는 아래와 같이 구성된다.
리렌더링이 성능에 미치는 영향
DOM Operation은 매우 빠르고 가볍다. 하지만 사용자의 웹앱의 데이터가 변화되고 갱신될 때, 리렌더링은 매우 비용이 많이 드는 작업 중에 하나이다.
// DOMExample.tsx
export default function DOMExample(){
const update = () => {
const element = `
<div>
<h1>Form for State</h1>
<form>
<input type="text"/>
</form>
</div>
<div>현재 시각은 ${new Date().toLocaleTimeString()}</div>
`
return element
}
const startTimer = () => {
setInterval(update,1000)
}
useEffect(()=>{
const element = update()
document.getElementById('root').innerHTML = element
},[update])
return (
<div id="root"></div>
)
}
먼저 실제 DOM tree를 살펴보도록 하자.
⎿DOCTYPE : html
⎿HTML
⎿HEAD
⎿BODY
⎿DIV
⎿DIV : id = "root"
⎿H1
⎿#text : Form for State
⎿FORM
⎿INPUT type = "text"
⎿DIV
⎿#text : 현재 시각은 00:00:00
DOMExample.tsx는 form과 setInterval을 활용한 timer를 구현한 예제이다. 일단 form의 input의 내용은 state로 관리되지 않는 상황이다. 그렇기 때문에 만약 input에 내용을 입력한다면 입력한 내용은 1초 마다 실행되는 리렌더링으로 사라지게 된다.
이는 state 관리 측면에서도 누수가 발생하는 상황이고 또한 매번 DOM tree를 재구성하는 리렌더링이 발생해 비용이 극대화 되는 상황이 발생한다. 리렌더링을 최소화 하기 위한 방법으로는 useCallback, React.Memo, useMemo 등이 있다.
Virtual DOM의 역할
일단 앞서 Virtual DOM은 실제 DOM을 위한 이상적이고 가상의 객체라고 했다. 그말은 즉, 가상이기 때문에 실제 DOM 보다 훨씬 가볍게 동작하고 브라우저 메모리에 저장될 수 있다는 것이다. 하지만 대부분 잘못 생각하고 있는 점이 하나 있다. 바로 Virtual DOM이 실제 DOM보다 빠르다거나 실제 DOM을 대체할 수 있는 존재라는 것이다. Virtual DOM의 역할은 실제 DOM을 Support(보조)하는 것이다.
React에서 Virtual DOM 작동 방식
Virtual DOM을 이해하기 위해서는 두가지 중요한 개념을 이해해야한다. 바로 렌더링(Rendering)과 재조정(Reconciliation)이다. 리액트는 Virtual DOM을 트리로 표현하고 메모리에 저장한다. 그리고 업데이트가 발생했을 때, 리액트는 자동으로 새로운 Virtual DOM을 생성한다.
새로운 Virtual DOM을 생성하고 이전 Virtual DOM과 비교하여 변경된 부분만 실제 DOM에 반영한다. 이 과정을 재조정(Reconciliation)이라고 한다. 재조정 과정 후에는 리액트는 ReactDOM 라이브러리를 통해 실제 DOM을 업데이트한다. ReactDOM 라이브러리는 업데이트된 특정 노드를 타겟팅해서 실제 DOM를 Repainting한다.
우원 /
우원입니다.