[React] Virtual DOM(VDOM)과 Diffing 알고리즘

2022. 7. 12. 17:09·Framework | Library/React.js
728x90

DOM

DOM은 The Document Object Model의 약어로 직역하자면 문서 객체 모델이다.

즉, DOM은 HTML을 객체로 표현한 것이며 외부(JavaScript)와 접속할 인터페이스이다.

html로 작성된 코드는 HTML 파서에 의해서 DOM이라는 객체가 모인 트리로 변환된다.(DOM 트리)

Virtual DOM

React 공식 홈페이지에서 Virtual DOM은 다음과 같이 표현하고 있다.

UI로 표현될 객체를 가상 메모리에 저장하고 라이브러리에 의해 실제 DOM으로 동기화 하는 개념

여기서 UI로 표현될 객체는 DOM을 말하며 라이브러리는 VDOM을 랜더링해주는 ReactDOM같은 것을 말한다.

Virtual DOM의 이점

React에서 state나 props가 갱신되면 render() 함수가 호출되어 새로운 엘리먼트(VDOM) 트리를 반환한다.

이때 효과적으로 UI를 갱신하기 위해서 기존의 VDOM(Old virtual DOM)과 새로운 VDOM(New virtual DOM)의 차이점을 찾아내어 변경된 부분만 새롭게 랜더링 한다.

즉, Virtual DOM은 DOM이 변경될 때마다 전체 DOM을 Reflow 하는 것이 아니라 가상의 DOM을 이용하여 한번만 Reflow를 수행함으로 부하를 줄여 빠르게 그릴 수 있다.

React의 Diffing Algorithm

기존의 DOM 트리를 새로운 트리로 변환하기 위하여 최소한의 연산을 하기 위해서 최신 알고리즘을 사용하여도 n개의 노드가 있을 때 O(n^3)의 복잡도를 가진다.

React는 두 가지 가정을 기반으로 O(n) 복잡도의 heuristic 알고리즘을 구현 하였다.

다음은 두 가지 가정을 기반으로 알고리즘이 구현 되었다.

1. Two elements of differnt types will produce different trees.
 : 서로 다른 타입을 가진 두 엘리먼트는 다른 트리를 만들어 낸다.
2. The developer can hit at which child elements may be stable across different renders with a key prop.
 : 개발자가 key prop를 통해 자식 엘리먼트의 변경 여부를 표시할 수 있다.

아래는 위 가정을 기반으로 구현된 알고리즘 내용이다.

1. Element의 타입이 다른 경우

  • Virtual DOM Tree의 Root 엘리먼트(old vs new)의 타입이 다르면 DOM Tree를 버리고 새로운 트리를 구축한다.
  • 엘리먼트가 제거되면 해당 엘리먼트와 하위 모든 엘리먼트의 state도 사라진다.
  • componentWillUnmount() : DOM 파괴될 때 실행.
  • componentWillMount() : DOM 삽입되기 전에 실행.
  • componentDidMount() : DOM 삽입된 후에 실행.
<!-- div와 span은 다르기 때문에 div는 제거된 후 span과 그 하위 엘리먼트가 추가됨 -->
<div>
  <Counter />
</div>

<span>
  <Counter />
</span>

2. DOM의 Element의 타입이 같은 경우

  • 두 React DOM Element의 타입이 같은 경우, Attribute(속성)을 확인하여 동일한 내역은 유지하고 변경된 속성만 갱신한다.
  • 해당 처리는 하위의 자식 노드들에게 재귀적으로 처리한다.
  • style 갱신도 변경 사항만 갱신 한다.
<!-- Attribute 갱신 -->
<div className="before" title="stuff" />

<div className="after" title="stuff" />


<!-- Style 갱신 // 결과적으로 color만 갱신, fontWeight는 유지 -->
<div style={{color: 'red', fontWeight: 'bold'}} />

<div style={{color: 'green', fontWeight: 'bold'}} />

3. 같은 타입의 Component Element

  • 컴포넌트가 갱신되면 인스턴스는 동일하게 유지되어 랜더링 간 state가 유지됨.
  • 새로운 내용 반영을 위해서 props를 갱신.
  • componentWillReceiveProps() : props 갱신 전 호출
  • render() 메소드 호출 후 이전과 비교하여 재귀적으로 처리

4. 반복적인 Element 처리

  • DOM 노드의 자식들을 반복적으로 처리할 때, 기본적으로 두 리스트(old vs new)를 순차적으로 비교하고 차이점이 있으면 변경한다.
  • 순회 비교를 하기 때문에 Element 리스트의 앞에 추가하는 것보다 뒤에 추가하는 것이 성능상으로 좋다.
<!-- 문제 X -->
<ul>
  <li>first</li>
  <li>second</li>
</ul>

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>


<!-- 성능상 문제 발생할 수 있음 -->
<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

5. Keys

  • 4번에서 발생한 문제를 해결하기 위해서 key 속성을 지원함
  • React는 key 속성을 통하여 두 트리(old vs new)를 비교하여 일치하는지 확인
  • 차이점이 발생하면 추가함
  • 일반적으로 React에서 반복문을 통하여 Element를 생성할 때 key 속성을 요구한다.
  • 하지만 위의 Case 외에 반복적으로 Element를 사용하고 상태에 따라서 Element가 추가/삭제될 수 있는 경우 key 속성을 사용하는 것이 성능상으로 좋다.
  • 반복되는 Element의 key 속성에 index 사용을 권장하지 않는 이유는 반복되는 Element의 항목들이 재배열 되는 경우 비효율적으로 동작하기 때문이다. (재배열 되면 index가 바뀌고 그에 따라 key도 변할 수 있기 때문)
<!-- key 속성으로 4번의 성능 문제 해결 -->
<ul>
  <li key="2022">Duke</li>
  <li key="2023">Villanova</li>
</ul>

<ul>
  <li key="2021">Connecticut</li>
  <li key="2022">Duke</li>
  <li key="2023">Villanova</li>
</ul>

6. 고려 사항

  • 상태에 따라 두 컴포넌트가 교체 될 때, 두 컴포넌트의 결과물이 비슷한 경우 하나의 컴포넌트로 만들 것을 권장.
  • key는 변하지 않고, 예상 가능하며, 유일해야함. (5번 참고)

'Framework | Library > React.js' 카테고리의 다른 글

[React] 이벤트 핸들러에 인자 전달하기  (0) 2022.07.12
[React] event handling 하는 법  (0) 2022.07.12
[React] React hooks - useReducer  (0) 2022.07.12
[React] props  (0) 2022.07.11
'Framework | Library/React.js' 카테고리의 다른 글
  • [React] 이벤트 핸들러에 인자 전달하기
  • [React] event handling 하는 법
  • [React] React hooks - useReducer
  • [React] props
arajo
arajo
  • arajo
    아라 메모장
    arajo
  • 전체
    오늘
    어제
    • 분류 전체보기 (509)
      • Language (298)
        • HTML (55)
        • CSS (11)
        • JavaScript (70)
        • TypeScript (8)
        • Python (33)
        • Java (119)
        • C (0)
        • C# (2)
      • Programming (92)
        • Programming (14)
        • Web (51)
        • Apache (1)
        • MySQL (23)
        • AWS (3)
      • Framework | Library (26)
        • Framework | Library (3)
        • Vue.js (2)
        • React.js (5)
        • React Native (4)
        • Node.js (1)
        • Ajax (1)
        • Bootstrap (8)
        • Spring (1)
        • Flutter (1)
      • etc (2)
      • 휴식 (19)
        • 책 (13)
        • 일기 (5)
        • 게임 일기 (1)
      • A (71)
        • 공부 (18)
        • 기타 (6)
        • 일 (47)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    object
    react
    MySQL
    TypeScript
    파이썬
    제어문
    next.js
    객체
    자바스크립트
    변수
    array
    HTML
    Java
    CSS
    web
    JavaScript
    리액트
    Python
    event
    타입스크립트
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
arajo
[React] Virtual DOM(VDOM)과 Diffing 알고리즘
상단으로

티스토리툴바