Virtual Dom

❗️ DOM(Document Object Model)이란?

  • 브라우저가 HTML 코드를 해석해서 요소들을 트리 형태로 구조화 것
  • HTML을 자바스크립트가 이해할 수 있도록 객체로 변환
  • DOM은 JS를 사용해서 화면에 콘텐츠를 추가/수정/삭제하거나 이벤트를 처리할 수 있도록 인터페이스 제공

❗️ Virtual DOM 등장 배경

브라우저가 렌더링 되는 과정을 간단하게 알아보자.

  1. HTML과 CSS를 파싱하고 파싱된 결과인 DOM과 CSSOM 트리 생성
  2. DOM과 CSSOM 트리를 결합하여 Render 트리를 생성
  3. 각 노드들의 실제 위치를 계산하는 layout 과정을 진행
  4. 화면에 요소들을 그리는 painting 과정을 진행

 

예전에는 SSR 방식을 많이 사용했다.

하지만 SPA가 등장하면서 CSR 방식이 사용되고,  DOM 트리를 즉각적으로 변경을 많이하게 되면서 최적화가 필요해졌다.

DOM이 변경될 때마다 위의 과정들을 반복하는데, 이는 많은 연산과 비용을 필요로 하기 때문에 프로그램 성능을 저하시킨다.

(오타 하나 수정해도 위의 과정 반복함)

 

위의 문제를 해결하기 위해 Virtual DOM이 등장했다!

 

Virtual DOM이란 기존 DOM의 가벼운 버전으로,

DOM 트리를 복제한 자바스크립트 객체이고 메모리 상에만 존재하는 가상의 DOM이다.

class, style 등의 속성은 동일하게 가지지만, 화면에 직접 변화를 주는 getElementById와 같은 DOM Api는 가지지 않는다.


❗️ Virtual DOM 동작 원리

브라우저는 실제 DOM Tree를 생성한다.

이 때, DOM을 가벼운 버전으로 복사하여 Virtual DOM을 생성한다.

DOM 노드에 변화가 생기면 새로운 Virtual DOM이 생성된다.

 

🤔 변화가 생길 때마다 Virtual DOM을 생성하면 비효율적인거 아니야?

DOM 노드를 조작하는 것이 비효율적인 이유는 DOM 트리를 업데이트 할 때 발생하는게 아니라 렌더링 하는 과정에서 발생하는 것이다.

Virtual DOM은 렌더링하지 않고 메모리 상에서 트리를 변경한다.

 

Virtual DOM 내부 구현을 살펴보면

diff(previous:VTree, current:VTree) -> PatchObject

이전 상태와 현재 상태를 받아와서 변경된 부분(PatchObject)만 생성한다.

 

patch(rootNode:DOMNode, patches:PatchObject) -> DOMNode newRootNode

rootNode와 변경된 부분을 이용해서, 변경된 부분만 렌더링 한다.

 

이렇게 변경된 부분을 DOM에 직접 수정하지 않고,

virtual dom을 한 번 거친 다음에 수정 사항을 한 한 번에 DOM에 적용함으로써 렌더링 횟수를 줄일 수 있다.


❗️Virtual DOM

Virtual DOM은 DOM 트리를 복제하여 메모리에 저장시킨 자바스크립트 객체이다.

왼쪽의 코드를 Virtual DOM으로 나타내면 오른쪽 코드와 같다.

 

Virtual DOM의 특징

  • 메모리 상에서 동작

    Virtual DOM은 메모리 상에서 동작하기 때문에 훨씬 빠르게 동작한다.
    브라우저에서 DOM을 조작하면 렌더링과 리플로우 같은 비용이 발생하지만,
    virtual DOM은 자바스크립트 객체로써 브라우저에서 직접 수정되지 않기 때문에 위와 같은 비용이 발생하지 않는다.

  • Batch Update

    변화가 30개 일어났다고 해서 레이아웃을 30개 새로 만드는 것이 아니라 모든 변화를 하나로 묶어서 한 번만 실행시킨다.

 

결국 Virtual DOM이 하는 것은

변화를 묶어서 적용한 다음 기존 DOM에 적용시키는 것이다.


❗️React에서의 Virtual DOM 

Virtual DOM(VDOM)은 UI의 이상적인 또는 "가상"적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 "실제" DOM과 동기화 하는 프로그래밍 개념입니다. 이 과정을 재조정이라고 합니다.

 

const element = <h1 title='foo'> Hello </h1>

const element = {
	type : "hi",
    props: {
    	title: "foo",
        children: "Hello",
    },
};

 

위의 JSX 코드를 아래와 같은 객체로 변환한다.

type은 dom node의 태그이름을 나타내고, props는 JSX에 포함된 모든 속성들을 나타낸다.

 

객체를 활용하여 virtual dom 구성하는데, 변경 전/후 virtual dom을 둘 다 유지하고 변화된 부분만을 DOM에 적용한다.

 

🤔 어떻게 변화된 부분만 감지할까?

type을 이용해서 변화된 부분을 찾아낸다.

 

1. 변경 전 type === 변경 후 type

=> 변경된 속성만 갱신한다.

 

2. 변경 전 type != 변경 후 type

=> 이전 트리를 삭제하고 새로운 트리를 만든다.

 

예시를 하나 봐보자.

ex) li 태그 엘리먼트가 첫번째에 추가됨(다른 태그들 변화X)

리액트는 자식노드를 전부 새로 그리게 되는데 이는 성능 이슈를 유발한다.

이를 Key prop을 사용해서 해결할 수 있다.

 

key값으로는 변경되지 않는 유일한 값을 사용해야 한다. 

만약 유일한 값이 아닌 배열의 index를 key값으로 사용한다면 

 

key=0이였던 이전 노드의 값을 새로 추가된 노드의 값으로 전달하는 문제가 발생하게 된다.


❗️React vs Vue 

React와 Vue에서는 동일하게 Virtual DOM을 사용한다.

이전 상태와 변경 상태를 비교하여 변경된 부분만 실제 DOM에 적용하는 방식은 동일하다.

하지만 분명 차이점이 있을 거라는 생각이 들어서 찾아보니 상태 변화를 추적하는 방식이 다르다고 한다.

 

- React

React에서는 상태를 불변적으로 취급한다.

따라서 상태를 변경하기 위해서는 setState와 같은 메서드를 통해 상태를 변경해야한다.

 

- Vue

Vue에서는 상태 비교에 Proxy 객체를 사용한다.

Proxy 객체 : 특정 객체를 감싸 프로퍼티 읽기/쓰기와 같은 객체에 행해지는 작업을 중간에서 가로채는 객체

 

개발자가 직접 Proxy 객체를 작성할 필요는 없고, Vue 내부에서 처리해준다.

Vue의 데이터는 data, ref를 통해 선언되고,

자동으로 반응형으로 만들어지기 때문에 상태 변경을 위해 별도의 메서드 호출을 하지 않아도 된다.


추가로 angular와 svelte에서는 virtual dom을 사용하지 않는다.

angular는 virtual dom을 사용하지 않고 incremental dom을 사용하고

svelte는 컴파일러인데 컴파일시에 모든 과정을 미리 처리한다.

 

자세한 내용은 다음에 알아보쟈..!

 

'WEB' 카테고리의 다른 글

Web Worker  (0) 2024.09.21
타입스크립트를 사용하는 이유  (0) 2024.09.06
Polling/Long-Polling/SSE/Web Socket  (0) 2024.08.27
Nginx  (0) 2024.08.21
SEO 최적화  (0) 2024.08.20