수민·2023년 1월 25일
0

code

목록 보기
41/47

React Diffing Algorithm

React가 기존 가상 DOM과 변경된 새로운 가상 DOM을 비교할 때, React 입장에서는 변경된 새로운 가상 DOM 트리에 부합하도록 기존의 UI를 효율적으로 갱신하는 방법을 알아낼 필요가 있었습니다. 즉 하나의 트리를 다른 트리로 변형을 시키는 가장 작은 조작 방식을 알아내야만 했는데, 알아낸 조작 방식 알고리즘은 O(n^3)의 복잡도를 가지고 있었습니다.

만약 이 알고리즘을 그대로 React에 적용한다면 1000개의 엘리먼트를 실제 화면에 표시하기 위해 10억(1000^3)번의 비교 연산을 해야만 합니다. 사실 이것은 너무 비싼 연산이기 때문에 React는 두 가지의 가정을 가지고 시간 복잡도 O(n)의 새로운 휴리스틱 알고리즘(Heuristic Algorithm)을 구현해냅니다.

두 가지 가정은 이것입니다.

각기 서로 다른 두 요소는 다른 트리를 구축할 것이다.
개발자가 제공하는 key 프로퍼티를 가지고, 여러 번 렌더링을 거쳐도 변경되지 말아야 하는 자식 요소가 무엇인지 알아낼 수 있을 것이다.
실제 이 두 가정은 거의 모든 실제 사용 사례에 들어맞게 됩니다. 여기서 React는 비교 알고리즘(Diffing Algorithm)을 사용합니다.

React가 DOM 트리를 탐색하는 방법

React는 기존의 가상 DOM 트리와 새롭게 변경된 가상 DOM 트리를 비교할 때, 트리의 레벨 순서대로 순회하는 방식으로 탐색합니다. 즉 같은 레벨(위치)끼리 비교한다는 뜻입니다. 이는 너비 우선 탐색(BFS)의 일종이라고 볼 수 있습니다.

[그림] React의 DOM 트리 순회 방식

React는 이런 식으로 동일 선상에 있는 노드를 파악한 뒤 다음 자식 세대의 노드를 순차적으로 파악해 나갑니다.

다른 타입의 DOM 엘리먼트인 경우
그런데 DOM 트리는 각 HTML 태그마다 각각의 규칙이 있어 그 아래 들어가는 자식 태그가 한정적이라는 특징이 있습니다. (예를 들어

    태그 밑에는
  • 태그만 와야 한다던가,

    태그 안에

    태그를 또 쓰지 못하는 것입니다.) 자식 태그의 부모 태그 또한 정해져 있다는 특징이 있기 때문에, 부모 태그가 달라진다면 React는 이전 트리를 버리고 새로운 트리를 구축해버립니다.

      
      
      <div>
    	<Counter />
    </div>
    
    //부모 태그가 div에서 span으로 바뀝니다.
    <span>
    	<Counter />
    </span>
      
    이렇게 부모 태그가 바뀌어버리면, React는 기존의 트리를 버리고 새로운 트리를 구축하기 때문에 이전의 DOM 노드들은 전부 파괴됩니다. 부모 노드였던
    으로 바뀌어버리면 자식 노드인 는 완전히 해제됩니다. 즉 이전
    태그 속 는 파괴되고 태그 속 새로운 가 다시 실행됩니다. 새로운 컴포넌트가 실행되면서 기존의 컴포넌트는 완전히 해제(Unmount)되어버리기 때문에 가 갖고 있던 기존의 state 또한 파괴됩니다.

    같은 타입의 DOM 엘리먼트인 경우
    반대로 타입이 바뀌지 않는다면 React는 최대한 렌더링을 하지 않는 방향으로 최소한의 변경 사항만 업데이트합니다. 이것이 가능한 이유는 앞서 React가 실제 DOM이 아닌 가상 DOM을 사용해 조작하기 때문입니다. 업데이트 할 내용이 생기면 virtual DOM 내부의 프로퍼티만 수정한 뒤, 모든 노드에 걸친 업데이트가 끝나면 그때 단 한번 실제 DOM으로의 렌더링을 시도합니다.

    
    <div className="before" title="stuff" />
    
    //기존의 엘리먼트가 태그는 바뀌지 않은 채 className만 바뀌었습니다.
    <div className="after" title="stuff" />

    React는 두 요소를 비교했을 때 className만 수정되고 있다는 것을 알게 됩니다. className before와 after는 각자 이런 스타일을 갖고 있다고 해보겠습니다.

      
      //className이 before인 컴포넌트
    <div style={{color: 'red', fontWeight: 'bold"}} title="stuff" />
    
    //className이 after인 컴포넌트
    <div style={{color: 'green', fontWeight: 'bold"}} title="stuff" />
                                                                   
                                               ```
                                  
                                                                   
                                                                   두 엘리먼트를 비교했을 때 React는 정확히 color 스타일만 바뀌고 있음을 눈치챕니다. 따라서 React는 color 스타일만 수정하고 fontWeight 및 다른 요소는 수정하지 않습니다. 이렇게 하나의 DOM 노드를 처리한 뒤 React는 뒤이어서 해당 노드들 밑의 자식들을 순차적으로 동시에 순회하면서 차이가 발견될 때마다 변경합니다. 이를 재귀적으로 처리한다고 표현합니다.
    
    자식 엘리먼트의 재귀적 처리
    예를 들면 이렇게 자식 엘리먼트가 변경이 된다고 가정하겠습니다.
                                                                   
                                                                   
    
profile
헬창목표

0개의 댓글