<aside> 🔗

GitHub 예시 코드

</aside>

들어가는 말


컴포넌트를 개발할 때 우리는 흔히 “리렌더링”이라는 말을 쓴다.

예를 들어, “부모가 리렌더링 되면 자식도 리렌더링 된다.”, “상태가 바뀌면 리렌더링 된다.”, “리렌더링을 최소화해야 한다.”, “useMemo/useEffect로 리렌더링 비용을 줄인다.” 같은 표현들이다.

내가 알기로, 리액트의 컴포넌트는 함수가 재실행되고 그 연산 결과로 생성된 가상 DOM이 이전과 달라질 때 리렌더링이 일어난다.

그렇다면, 리렌더링은 “DOM을 화면에 다시 그려내는 행위”를 의미하는 걸까?

만약 그렇다면, 부모 DOM이 다시 그려질 때 자식 DOM도 함께 다시 그려지는 걸까? 또, 컴포넌트의 상태가 바뀌면 리액트는 DOM을 실제로 다시 그려내는 걸까? 그렇다면 리렌더링을 최소화한다는 말은 DOM을 다시 그려내는 횟수를 줄이자는 뜻일까? useMemo나 useEffect를 사용하면 DOM을 다시 그려내는 비용이 줄어드는 걸까?

이번 글에서는 이런 질문들을 정리하면서, 내가 평소 막연히 알고 있던 “리렌더링”이라는 단어의 의미를 좀 더 명확히 짚어보려 한다.

컴포넌트의 함수의 실행 과정


일단 리액트 컴포넌트 함수가 실행되는 단계들을 살펴보자.

실행 단계 (Render Phase)

컴포넌트 함수가 실행되어, JSX 평가 결과를 바탕으로 새 Fiber 트리를 준비하는 단계이다.

React.memo, 동일 props/state, 변경 없는 context면 재실행이 스킵될 수 있다.

동시성에 의해 중단, 재시작, 폐기가 가능하고 개발 환경의 StrictMode 에서는 이중 호출될 수 있다.

비교 및 조정 단계 (Reconciliation Phase)

방금 만든 트리를 이전 트리와 비교(diff) 해서 무엇을 바꿀지를 계산한다. 바꿀 게 없으면 이후의 커밋 단계가 생략될 수도 있다.

커밋 단계 (Commit Phase)

실제 DOM 트리에 반영하는 단계. 동기적으로 다음 하위 단계를 거친다.

  1. Before-mutation: 클래스의 getSnapshotBeforeUpdate 가 호출되고, 기존 ref 연결이 해제된다.
  2. Mutation: DOM을 변경하고, 새로운 ref 연결을 한다.
  3. Layout effects: useLayoutEffect 훅이 실행된다.