- 구 가이드 : https://ko.legacy.reactjs.org/docs/reconciliation.html
- 신 가이드 : https://react.dev/learn/preserving-and-resetting-state
동기
- 하나의 트리를 다른 트리로 변환하기 위한 최소한의 연산은 O(n^3)
- 리액트에서는 다음 두가지의 가정하에 O(n)을 가지는 휴리스틱 알고리즘을 구현함
- 서로 다른 타입의 두 엘리먼트는 서로 다른 트리를 만들어낸다.
- 개발자가
key
prop을 통해 여러 렌더링 사이에서 어떤 자식 엘리먼트가 변경되지 않아야할지 표시해줄 수 있다.
비교 알고리즘 (Diffing Algorithm)
리액트에서 두 개의 트리를 비교할 때 두 엘리먼트의 루트 엘리먼트부터 비교한다. 이후의 동작은 루트 엘리먼트의 타입에 따라 달라진다.
엘리먼트의 타입이 다른 경우
- 두 루트 엘리먼트의 타입이 다르면, React는 이전 트리를 버리고 새로운 트리를 구축한다. ex) <a>
에서 <img>
로, <Article>
에서 <Comment>
로 바뀌는 경우를 말한다. - 트리를 버릴 때 이전 DOM 노드들은 모두 파괴된다. 컴포넌트 인스턴스는 componentWillUnmount()
를 실행한다. 새로운 트리가 만들어질 때 새로운 DOM 노드들이 DOM에 삽입된다. 그에 따라 컴포넌트 인스턴스는 UNSAFE_componentWillMount()
가 실행되고 componentDidMount()
가 실행된다. 이전 트리와 연관된 모든 state는 사라진다. - 루트 엘리먼트 아래의 모든 컴포넌트도 언마운트 되고 그 state도 사라진다. 아래와 같은 변경이 이루어질때, 이전 <Counter/>
는 사라지고 새로 다시 마운트 될 것이다.
1
2
3
4
5
6
7
<div>
<Counter />
</div>
<span>
<Counter />
</span>
DOM 엘리먼트의 타입이 같은 경우
- 같은 타입의 두 React DOM 엘리먼트를 비교할 때, React는 동일한 속성은 유지하고 변경된 속성들만 갱신한다. - style이 갱신될 때는 변경된 속성만 변경한다. - style=
-> style=
일때, fontWeight는 건들지 않고, color만 수정한다.
같은 타입의 컴포넌트 엘리먼트
- 컴포넌트가 갱신되면 인스턴스는 동일하게 유지되어 렌더링 간 state가 유지된다. React는 새로운 엘리먼트의 내용을 반영하기 위해 현재 컴포넌트 인스턴스의 props를 갱신한다. 이때 해당 인스턴스의 componentDidUpdate
를 호출한다. - 이후 render() 메서드가 호출되고 diffing 알고리즘은 재귀적으로 자식에 대해 처리한다.
자식에 대한 재귀적 처리
- DOM 노드의 자식들을 재귀적으로 처리할 때, 리액트는 기본적으로 동시에 두 리스트를 순회하고 차이점이 있으면 변경을 생성한다. - 예를 들어 리스트 끝에 엘리먼트를 추가하면 두 트리 사이의 변경은 잘 작동한다.
1
2
3
4
5
6
7
8
9
10
<ul>
<li>first</li>
<li>second</li>
</ul>
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
- 하지만 리스트의 맨 앞에 엘리먼트를 추가하면 성능은 좋지 않다. 모든 엘리먼트를 다시 생성하기 때문이다. ```html
- Duke
- Villanova
- Connecticut
- Duke
- Villanova
1
2
3
4
5
6
7
8
9
10
11
12
13
- 이러한 문제를 해결하기 위해 리액트는 `key` 속성을 제공한다. 자식들이 같은 key를 가지고 있으면 React는 key를 통해 기존 트리와 이후 트리의 자식들이 일치하는지 확인한다.
```html
<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>
리액트는 이제
2014
key를 가진 엘리먼트가 추가되었고, 2015, 2016 key를 가진 엘리먼트는 그저 이동만 하면 되는 것을 알 수 있다. 이때 사용하는 key는 형제들 사이에서 유일하면 된다.최후의 경우 배열의 인덱스를 key로 사용할 수 있다. 하지만 이 경우 배열이 재배열되면 컴포넌트의 state가 꼬일 수 있다. 컴포넌트 인스턴스는 key를 기반으로 갱신되고 재사용되기 때문에, 인덱스를 key로 사용하면 항목의 순서가 바뀌었을 때 key 또한 바뀌기 때문이다. 예시 개선된 예시
#review