재조정(Reconciliation)은 React가 이전 렌더의 엘리먼트 트리와 새 트리를 비교(Diffing)해, 실제 DOM(Document Object Model)에 필요한 최소한의 변경만 반영하는 과정이다. 두 트리를 완벽하게 비교하면 O(n³)이라, React는 두 가지 가정을 두어 O(n)으로 줄인다.
- 타입이 다르면 다른 트리다.
<div>가<section>으로,Counter가Timer로 바뀌면 아래 트리를 모두 언마운트하고 새로 마운트한다. state도 사라진다 key가 형제 사이의 정체성을 알려 준다. key가 같으면 같은 요소로 보고 이동만 처리한다
flowchart TD
N["같은 위치의 이전 엘리먼트와 새 엘리먼트"] --> T{"타입이 같은가"}
T -- 아니오 --> R["아래 트리를 언마운트하고 새로 마운트 (state 사라짐)"]
T -- 예 --> K{"key가 같은가"}
K -- 아니오 --> R
K -- 예 --> U["DOM 노드·state 유지, 바뀐 props만 반영"]
규칙에서 나오는 실무 감각
- 타입과 트리 위치가 같으면 DOM 노드와 컴포넌트 state가 유지되고, 바뀐 props만 반영된다
- 리스트 key에 배열 index를 쓰면 앞에 항목을 넣을 때 엉뚱한 항목의 state가 따라간다. 데이터의 고유 ID를 쓴다
- key는 리스트 밖에서도 쓸 수 있다.
<Profile key={userId} />처럼 key를 바꾸면 컴포넌트를 일부러 초기화할 수 있다(Effect가 필요 없는 경우) - 컴포넌트를 다른 컴포넌트 안에서 정의하지 않는다. 렌더마다 새 함수 = 새 타입이라 매번 리마운트된다
- 같은 자리에서 조건부로 다른 타입을 번갈아 그리면 state가 날아간다.
{isOpen ? <Modal /> : null}도 닫으면 Modal의 state가 초기화된다 - 리렌더를 디버깅할 때는 "어느 트리 위치에 어떤 타입이 있나"로 생각한다.
React.memo는 이 알고리즘 안에서 비교를 건너뛰게 할 뿐 알고리즘을 바꾸지 않는다(memo·useMemo·useCallback, 상태 내리기와 컴포지션으로 리렌더 줄이기)
비교 작업을 쪼개 중단·재개하는 엔진은 Fiber 아키텍처. 정확성보다 속도를 택한 휴리스틱이라는 점에서 빅오 표기법 관점으로도 읽을 수 있다.
출처: React Reconciliation: 컴포넌트 뒤에 숨겨진 엔진 · React(구 문서) - Reconciliation · React - Preserving and Resetting State