가상돔(VirtualDOM) deep dive

김규빈·2023년 2월 16일
2
post-thumbnail

가상돔 원리... 복잡해 보이는데?

SPA 웹 기술에 근간 가상돔 근데 이거 어떻게 만들었지?

그냥.. 갑자기 궁금해졌다.
가상돔 만들 생각은 왜 했으며(why, how), 어느 단계에서 가상돔과 비교 후 적용하는지.
diff 알고리즘은 구체적으로 어떻게 걸러내는건지. Virtual DOM에는 실제 DOM의 정보 중 어디까지 가지고 있을지.... 하나씩 알아보자

1. 렌더링 과정

우선 먼저 렌더링 과정부터.
1. html, css file 파싱 후 DOM tree & CSSOM tree 생성
2. Dom tree + CSSOM tree = render tree 생성
3. 렌더 트리에 적용된 element의 위치 계산 reflow 과정 (위치)
4. 작업한 렌더링 트리를 화면의 픽셀로 변환하면서 paint (색칠)
5. 각각 paint된 레이어를 합성하면서 렌더링 완료

여기서 3, 4 과정은 비용이 비싼 작업이다.

과거에 웹 형태는 Dom에 요소가 추가가 되면 렌더링 과정을 지우고 처음부터 거친 뒤 화면에 뿌려졌을 것.
SPA가 아닌 구조(리소스가 통으로)와 변경을 통해 매번 렌더링을 거친다면 브라우저에겐 매우 큰 부담과 성능 저하 요소였을것이다. 이걸 스크립트단에서 해결 할 순 없을까? => 가상돔 만들 생각은 왜 했으며(why)의 대답

2. 비교해서 뭐가 바뀐지 보자

html에 변화가 있을 때, 전과 비교하여 변경된 내용만 찾아서 DOM에 반영하자. 이를 통해 브라우저 내에서 발생하는 연산의 양(정확히는 렌더링 과정)을 줄이면서 성능이 개선되는 것 이다.
우선 진짜 돔의 노드 구조를 갖고 있는 가상돔을 메모리에 올리고, 변화가 생길 경우 가상돔에서 렌더링 과정을 거치고 비교 후 변경 된 부분만 진짜 돔에 반영 => 뭐하러 가상돔에 렌더링, 비교, 반영 과정을 거치나요 그냥 리얼 돔에 바로 반영하면 되는거 아닌가요? (가상돔은 dom의 내용을 추상화 한 자바스크립트 객체, dom 객체를 가지고 있지만 DOM이 갖고 있는 api는 갖고 있지 않기 때문에 비교적 가벼움, 이러한 처리는 실제 DOM이 아닌 메모리 상에서 동작하기 때문에 훨씬 더 빠르게 동작하고, Virtual DOM tree는 실제 렌더링이 되지 않기 때문에 연산비용 훨씬 적다. => Virtual DOM에는 실제 DOM의 정보 중 어디까지 가지고 있을지 의 대답)

현대의 프론트엔드 기술은 시간이 지날수록 DOM의 조작에 대한 복잡도가 나날이 증가하고 있다. DOM의 변화가 많아 질수록 가상돔의 장점 활용은 더욱 필요한셈.

3. 바뀐지 어떻게 알까?

가상돔의 개념은 알았다. 그러면 가상돔에서 바뀐(업데이트)가 된 부분은 어떻게 알까?
가상돔이 업데이트 되면 React는 가상돔이 바뀌기 전 가상돔의 구조를 스냅샷하고 그 스냅샷 한 구조와 업데이트 된 구조를 비교하여 바뀐 노드를 찾아낸다. 이 과정을 Diff 알고리즘이라고 한다.
element의 속성 값만 변한 경우에는 속성 값만 업데이트 하고, 해당 엘리먼트에 태그나 컴포넌트가 변경된 경우라면 해당 노드를 포함한 하위의 모든 노드들을 언마운트(제거)한 뒤 업데이트된 노드로 대체한다.
Diff 알고리즘은 A상태에서 B상태로 변경하는 가장 합리적인 방법을 찾는 알고리즘인데, SPA의 경우 state와 props등 상태값 변경에 따른 영향을 미치는 노드가 하위로 뻗어있다. 이 문제에 대한 일반적인 비교 방법으론 하위 노드를 모두 탐색하여 O(n^3)의 복잡성을 갖게 된다.
하지만 리액트에선 두가지의 조건으로 O(n)복잡도로 계산을 할 수 있다.

1. 서로 다른 타입을 가진 두 엘리먼트는 서로 다른 트리를 만들어냅니다.
2. 개발자가 key prop를 사용해 자식 엘리먼트의 변경 여부를 표시할 수 있습니다.
A.
<div><SometingComponent /></div>
B.
<p><SometingComponent /></p>

A->B의 root 엘리먼트는 타입이 달라졌기 때문에 Dom트리를 제거하고 곧 바로 새로운 트리를 다시 구성
<ul>
  { myListArr.map(list => 
	<li key={list.id}>list</li>
}
</ul>

리액트에서 이런 식으로 맵핑을 많이 할텐데 맵핑시 주는 key값을 이용하여 두 트리의 변경점을 찾아낸다

=> diff 알고리즘은 구체적으로 어떻게 걸러내는건지 대답

4. 비교 후 적용

React의 render 결과는 실제 DOM 노드가 아니다. render는 자바스크립트 객체로 반환되며, render를 통해 생성된 자바스크립트 객체를 virtual DOM이라 부르는 것. 리액트의 render가 끝나고 이후 변경 사항을 갖고 다시 render를 통해 업데이트가 될때 가상돔에서 비교 후 적용 한다. => 어느 단계에서 가상돔과 비교 후 적용하는지의 대답

const Component = React.createClass({
  render: function() {
    if (this.props.first) {
      return (
        <div className="first">
          <span>A Span</span>
        </div>
      );
    } else {
      return (
        <div className="second">
          <p>A Paragraph</p>
        </div>
      );
    }
  }
});

만약 위에 예시에서 this.props.first를 true에서 false로 변경하고 unmount시킨다면 Dom은

1. <div className="first"><span>A Span</span></div> 노드를 생성
2. className="first"를 className="second"로 교체
3. <span>A Span</span><p>A Paragraph</p>로 교체
4. 변경한 <div className="second"><p>A Paragraph</p></div> 를 삭제

의 과정을 거쳐 unmount가 될 것.

출처
[10분 테코톡] 🥁 지그의 Virtual DOM
https://velopert.com/3236
https://medium.com/@deathmood/how-to-write-your-own-virtual-dom-ee74acc13060
https://junilhwang.github.io/TIL/Javascript/Design/Vanilla-JS-Virtual-DOM/#_2-%E1%84%89%E1%85%B3%E1%84%90%E1%85%A1%E1%84%8B%E1%85%B5%E1%86%AF

profile
FrontEnd Developer

0개의 댓글