D5ng

React

리액트 렌더링 과정에 대해 어디까지 알고 있나요?

당신이 알고 있는 렌더링은 무엇인가요? 이번에 제대로 공부하는 시간을 가져봅시다.

DH
DongHyun Lee·FE Developer
9 min read·July 14, 2024
thumnnail

작가 pikisuperstar 출처 Freepik

이 글을 읽으면 배울 수 있는 것

  • React에서 렌더링의 의미
  • React가 컴포넌트를 언제, 왜 렌더링 하는지
  • 화면에 컴포넌트를 표시하는 단계
  • 렌더링이 항상 DOM 업데이트를 하지 않는 이유

렌더링 프로세스

React에서 렌더링이란 컴포넌트가 props와 state를 통해 UI를 어떻게 구성할지 컴포넌트에게 요청하는 작업을 말한다. 일반적으로 말하는 렌더링은, 화면에 바로 나타나는 것처럼 표현되고, React에서는 화면에 나타내기전 어떤 과정을 시작하는 듯한 어조이다. 따라서, 주의해야 할 점은 React에서 렌더링은 DOM 업데이트가 아니다. 즉, 렌더링은 실질적 화면 업데이트가 아니라는 점이다.

React가 컴포넌트를 화면에 표시하는 과정은 총 3단계가 있다.

  1. Rendering Trigger
  2. Component Rendering
  3. 리액트가 DOM에 Commit

Rendering Trigger

Trigger가 되는 조건은 컴포넌트의 state가 Object.is() 메서드를 사용해 state 값이 불변을 지켜 변경되었을 때다. state가 변경이되면 리 렌더링이 일어나는데, 리 렌더링은 상태 값에 의존해 새로운 데이터로 새로운 화면을 그린다. 따라서 React가 데이터가 변경되었다는 것을 알아야 하며 그 과정이 Trigger 과정이다. 반드시 상태 값을 Set 함수로 업데이트해야 하는 이유다. React가 렌더링 될 것인지 되지 않을 것인지 판단하기 위해서다.

다음은 컴포넌트가 리렌더링 되는 조건이다.

  • 부모 컴포넌트가 리렌더링 되었을 때
  • state 값이 변경되었을 때
  • context API의 state 변경되었을 때

set State의 함수는 Queue에 순차적으로 등록된다. 이 과정은 Rendering 과정에서 다시 활용된다.

Component Rendering

렌더링을 Trigger 한 후 React는 컴포넌트를 호출하여 화면에 표시할 내용을 파악한다. 이 단계에서 Reconciliation(재조정)이 일어난다.

  • 초기 렌더링에서 React는 루트 컴포넌트를 호출한다.
  • 이후 렌더링에서 React는 state 업데이트가 일어나 렌더링을 Trigger 한 컴포넌트를 호출한다.

Reconciliation

Reconciliation(재조정)이란 렌더링 프로세스에서 컴포넌트를 실행해 앞으로 그려질 DOM과 그려지기 전 DOM을 비교해 달라진 부분을 찾아 변경이 필요한 컴포넌트를 체크하는 단계다. 여기서 사용하는 알고리즘이 diffing 알고리즘이다.
다음과 같이 세 가지 중 하나라도 변경된 것이 있으면 변경이 필요한 컴포넌트로 체크해둔다.

  • type
  • props
  • key

처음 렌더링할 경우 React는 가상 DOM을 그려놓는다. 그리고 Trigger가 발생하면, 새롭게 가상 DOM을 그린다. 그리고 Diffing 알고리즘을 통해 위 과정을 거쳐 어떤 부분을 업데이트할지 결정하는 것이다. 왜 이런 과정을 거칠까? 굳이 리 렌더링 되지 않아도 되는 부분은 제외하고 변경되어야 하는 부분만 변경하기 위해서이다. 즉, 효율성을 위함이다.

web 혹은 native 환경에서의 DOM 조작은 비용이 비싸다. 하지만, 가상 DOM을 비교하거나 조작하는 일은 실제 DOM을 조작하는 것에 비해 가격이 저렴하다. 가상 DOM은 JavaScript로 조작할 수 있는 가벼운 메모리이기 때문이다. 또한, 모든 DOM을 새로 그리게 될 경우 focus가 없어질 수 있고, select 요소에서 선택했던 option 값이 사라졌을 수도 있으며 스크롤 정보도 없어질 수 있다.

Batching

Batching(일괄 처리)은 리액트가 더 나은 성능울 위해 여러 상태 업데이트를 단일 리 렌더링으로 그룹화하는 것을 말한다. Trigger 단계에서 set State의 함수는 Queue에 순차적으로 들어간다고 했다. 이제 Diffing 알고리즘으로 어떤 DOM이 변경되어야 하는지 알았기 때문에, 변경되어야 할 DOM의 state 값을 Queue에 들어갔던 함수를 실행하면서 계산한다.

브라우저의 렌더링은 reflow와 repaint 과정의 비용이 비싸다. 하지만 리액트는 Batching을 통해서 단일 리랜더링으로 그룹화했기 때문에 state가 업데이트될 때마다 리랜더링이 되지 않는다는 성능적 이점이 있다. 즉 단일 리 렌더링으로 그룹화했기 때문에 reflow, repaint를 한 번만 일어나도록 했다.

React 18 버전부터는 Promise, setTimeout, EventHandler 등도 함께 일괄 처리된다. Automatic batching for fewer renders in React 18에서 확인할 수 있다. 이 과정을 통해 리렌더링은 setState가 호출될 때마다 실행되는 것이 아니라, 마지막에 한번만 실행 되도록 했다.

Commit

커밋 단계를 거쳐서 DOM을 업데이트하고 나면, React는 요청된 DOM 노드와 컴포넌트 인스턴스를 가리키도록 모든 변경사항들을 업데이트한다. 그리고나서 componentDidMount 와 componentDidUpdate 클래스 생명주기 메소드 또는 useLayoutEffect 훅을 동기적으로 실행한다. 여기서도 DOM에 마운트 된다는 뜻이지 paint 된다는 뜻이 아니다. 이 단계는 항상 일관적 화면 업데이트를 위해 동기적으로 실행된다. 동기적으로 실행된다는 것은 콜 스택을 비우지 않고, DOM 조작을 Batching 처리 한다는 뜻이다. Commit 단계가 끝나면 브라우저의 렌더링이 발생한다.

여기서 알 수 있는 중요한 사실은 리액트의 렌더링이 일어난다고 해서 무조건 DOM 업데이트가 일어나는 것은 아니라는 것이다. 렌더링을 수행했으나 커밋 단계까지 갈 필요가 없다면, 즉 변경 사항을 계산했는데 아무런 변경 사항이 감지되지 않는다면 이 커밋 단계는 생략될 수 있다. 리액트의 렌더링은 꼭 가시적인 변경이 일어나지 않아도 발생할 수 있다.

React가 상태를 기반으로 효율적으로 화면을 그려내기 위한 동작은 아래와 같다.

  • setState의 Object.is() 메서드로 상태 값 비교
  • Reconciliation의 가상 DOM으로 부분 리렌더링
  • Batching(일괄 처리)를 통해 여러 번이 아닌 한 번만 리렌더링

Reference