[React - 기초] Reconciliation

2022. 3. 21. 12:26·React/패스트캠퍼스
반응형

Reconcilation?

React를 사용할 때, `render() 함수는 React 엘리먼트 트리를 만드는 것이다.' 라고 생각할 수 있다. 

state나 props가 갱신되면 render() 함수는 새로운 React 엘리먼트 트리를 반환한다. 

 

이때 React가 가장 효과적으로 UI를 갱신해서 트리를 만드는지 알아보자! 

 

여기서 사용된 것이 Reconciliation (재조정) 알고리즘이다. 

 

하나의 트리를 가지고 다른 트리로 변환하기 위한 최소한의 연산 수를 구하는 알고리즘 문제를 풀기 위한 

일반적인 해결책들이 있다. 하지만 이런 알고리즘도 n개의 엘리먼트가 있는 트리에 대해 O(n3)의 복잡도를 가진다. 

1000개의 엘리먼트를 그리기 위해서 10억 번의 비교 연산을 수행해야 한다는 뜻이다.

 

그래서 React는 두 가지 가정을 기반으로 O(n) 복잡도의 휴리스틱 알고리즘을 구현했다. 

1. 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다. 

2. 개발자가 key prop을 통해, 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야 할 지 표시해 줄 수 있다. 

 

비교 알고리즘 (Diffing Algorithm)

두 개의 트리를 비교할 때, React는 루트(root) 엘리먼트부터 비교한다. 그후 루트 엘리먼트의 타입에 따라 달라진다.

 

엘리먼트 타입이 다른 경우 

루트 엘리먼트의 타입이 다르다면, React는 이전 트리를 버리고 완전히 새로운 트리를 구축한다. 

 

트리를 버릴 때 이전 DOM 노드들은 모두 파괴된다. 컴포넌트 인스턴스는 componentWillUnmount()가 실행된다. 

새로운 트리가 만들어질 때, 새로운 DOM 노드들이 DOM에 삽입된다.

 

그에 따라 컴포넌트 인스턴스는 UNSAFE_componentWillMount()가 실행되고 componentDidMount()가 이어 실행된다.

이전 트리와 관련된 모든 state도 사라진다. 

<div>
  <Counter />
</div>

<span>
  <Counter />
</span>

<div> 태그가 <span> 태그로 바뀌었다면 <Counter>는 사라지고, 다시 마운트가 된다. 

 

이처럼 루트 엘리먼트 아래의 모든 컴포넌트도 언마운트되고 그 state도 사라진다. 

 

※ UNSAFE_componentWillMount ? 

레거시이며, 새로 작성하는 코드에서는 componentWillMount를 사용하면 된다. 

 

 

엘리먼트 타입이 같은 경우 

루트 엘리먼트의 타입이 같다면, React는 두 엘리먼트의 속성을 확인하여, 동일한 내역은 유지하고 변경된 속성은 

갱신한다. 

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

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

두 엘리먼트를 비교한다면, React는 DOM 노드 상에서 className만 수정할 것이다. 

 

style이 갱신될 때, React는 변경된 속성만 갱신한다. 

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

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

React는 fontWeight는 수정하지 않고 color 속성만 수정한다. 

 

DOM 노드의 처리가 끝나면, React는 이어서 해당 노드의 자식들을 재귀적으로 처리한다. 

 

같은 타입의 컴포넌트 엘리먼트 

컴포넌트가 갱신되면 인스턴스는 동일하게 유지되어 렌더링 간 state가 유지된다. 

React는 새로운 엘리먼트의 내용을 반영하기 위해 현재 컴포넌트 인스턴스의 props를 갱신한다. 이때 

인스턴스의 UNSAFE_componentWillReceiveProps(), UNSAFE_componentWillUpdate(), componentDidUpdate를 

호출한다. 

 

다음으로 render() 메서드가 호출되고 비교 알고리즘이 이전 결과와 새로운 결과를 재귀적으로 처리한다. 

 

※ UNSAFE_componentWillReceiveProps(), UNSAFE_componentWillUpdate() ? 

레거시이며, 새로 작성하는 코드에서는 componentWillReceiveProps(), componentWillUpdate()를 사용하면 된다. 

 

자식에 대한 재귀적 처리 

DOM 노드의 자식들을 재귀적으로 처리할 때, React는 기본적으로 동시에 두 리스트를 순회하고 차이점이 있으면 

변경을 생성한다. 

 

예를들어, 자식의 끝에 엘리먼트를 추가하면, 두 트리 사이의 변경은 잘 적용될 것이다. 

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

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

React는 두 트리에서 <li>first</li>가 일치하는 것을 확인하고, <li>second</li>가 일치하는 것을 확인한다. 

그리고 마지막으로 <li>third</li>를 트리에 추가한다. 

 

하지만 단순하게 위와 같이만 구현하면, 리스트의 맨 앞에 엘리먼트를 추가하는 경우 성능이 좋지않다. 

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

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

<li>Duke</li>와 <li>Villanova</li> 종속 트리를 그대로 유지하지 않고 모든 자식을 변경한다. 

이런 비효율은 문제가 될 수 있다고 판단해서 Key 속성을 사용하였다. 

 

Keys

자식들이 key를 가지고 있다면, React는 key를 통해 기존 트리와 이후 트리의 자식들이 일치하는지 확인한다. 

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

비효율적이였던 위 예시에 key를 추가하였는데, 이제 React는 '2014' key를 가진 엘리먼트를 새로 추가하고, 

'2015'와 '2016' key를 가진 엘리먼트는 이동만 하면 된다. 

반응형
저작자표시 비영리 변경금지 (새창열림)

'React > 패스트캠퍼스' 카테고리의 다른 글

[React - 기초] PropTypes  (2) 2022.03.19
[React - 기초] Render Props  (1) 2022.03.18
[React - 기초] Portals  (2) 2022.03.17
[React - 기초] Memoization  (2) 2022.03.15
[React - 기초] Hooks  (0) 2022.03.10
'React/패스트캠퍼스' 카테고리의 다른 글
  • [React - 기초] PropTypes
  • [React - 기초] Render Props
  • [React - 기초] Portals
  • [React - 기초] Memoization
잉여개발자
잉여개발자
풀스택 개발자를 목표로 잉여롭게 개발 공부도 하면서 다양한 취미 생활도 즐기고 있는 잉여 개발자입니다.
  • 잉여개발자
    잉여로운 개발일지
    잉여개발자
    • 분류 전체보기 (789)
      • 개발정보 (36)
      • 개발환경 (7)
      • 개발생활 (19)
      • React (141)
        • 이론 (23)
        • 기능 (12)
        • 실험실 (88)
        • 버그 (6)
        • 패스트캠퍼스 (9)
        • Npm (3)
      • React Native (28)
        • 공통 (6)
        • TypeScript (3)
        • JavaScript (18)
        • 버그 (1)
      • Next.js (30)
        • 이론 (13)
        • 실험실 (13)
        • 버그 (3)
      • Web (35)
      • 알고리즘 (202)
        • 풀이 힌트 (39)
      • JavaScript (47)
      • TypeScript (29)
        • 기초 (27)
        • 실험실 (2)
      • Node.js (13)
        • 이론 (0)
        • 기능 (3)
        • 실험실 (9)
        • 버그 (1)
      • 도커 (4)
      • CCNA (22)
        • 이론 (4)
        • 문제 (18)
      • 취미생활 (167)
        • 잉여로운 칵테일 (2)
        • 잉여의 식물키우기 (130)
        • 잉여로운 여행기 (11)
        • 잉여의 제2외국어 (21)
        • 잉여로운 책장 (2)
      • Java (1)
        • Java의 정석 (1)
      • 꿀팁 공유 (3)
  • 태그

    typescript
    CCNA
    리얼학습일기
    타입스크립트
    네이버 부스트캠프
    식물
    다이소
    리얼클래스
    타일러영어
    next.js
    리액트
    Node.js
    영어회화
    ReactNative
    영어독학
    바질 키우기
    javascript
    redux
    네트워크
    알고리즘
    Babel
    webpack
    바질
    ChatGPT
    덤프
    react
    자바스크립트
    프로그래머스
    Docker
    CSS
  • hELLO· Designed By정상우.v4.10.1
잉여개발자
[React - 기초] Reconciliation
상단으로

티스토리툴바