React는 2단계를 거쳐 화면에 UI를 렌더링 한다.
위 두가지 Phase에 대해 자세히 알아보자
리액트가 변화된 상태나 props에 따라 어떤 UI가 변경되어야 할지를 결정하는 단계
Render Phase는 컴포넌트의 변경사항을 계산하고 준비하는 단계이다. 이 단계에서는 실제 DOM을 수정하지 않고, 변경이 필요한 부분을 파악한다.
// Component
function App() {
return (
<div id="main">
<p>Hello</p>
</div>
)
}
위 컴포넌트를 호출하게 되면 아래와 같은 객체 값이 반환된다.
아래의 객체를 React Element라고 부른다.
// React Element
{
type: 'div',
key: null,
ref: null,
props: {
id: 'main',
children: {
type: 'p',
key: null,
ref: null,
props: {
children: 'Hello',
},
// ...
},
},
// ...
};
React Element는 컴포넌트가 렌더링하고자 하는 UI에 대한 모든 정보를 다 포함하고 있는 객체를 말한다.
이 단계에서는 화면에 렌더링 되어야하는 모든 컴포넌트를 호출해서 얻은 React Element들을 모아서 Virtual DOM이라는 트리 형태로 구조화시킨다.
React Element라고 부르는 객체 값의 모임
- 자바스크립트 형태의 값이고 객체 형태를 가진다.
- 실제 DOM이 아니라 값으로 UI를 표현한 가상의 DOM 즉 복제판이라고 볼 수 있다.
- 연산을 많이 소요하지 않아 생성, 제거, 수정에 있어 자유롭다.
이렇게 Virtual DOM을 만들게 되면 Render Phase가 종료된다.
정리해보면, Render Phase에서는 실제로 DOM을 업데이트하지 않고, 변경사항을 가상 DOM에서 계산하여 비교한다는 것을 알 수 있다.
이 단계는 순수하게 계산과정이기 때문에 성능에 영향을 주지 않도록 중단되거나 다시 실행될 수 있으며, React 18에서 도입된 Concurrent Mode를 통해 비동기적으로 처리될 수도 있다.
실제로 변화된 UI를 DOM에 반영하는 단계
이 단계에서는 Render Phase에서 계산한 Virtual DOM의 계산 결과를 가지고 Actual DOM(진짜 브라우저가 렌더링하게 될 DOM)에 반영시킨다. 즉, Virtual DOM을 Actual DOM에 반영한다고 볼 수 있다.
또한 이 과정에서는 useEffect와 같은 사이드 이펙트가 발생하는 훅들이 실행된다.
요약하자면 Render Phase는 변화된 UI를 결정하는 계산 과정이고, Commit Phase는 계산된 결과를 반영하는 단계이다.
React는 DOM 수정을 최소화 하기 위해, 즉 대부분의 상황에 충분히 빠른 업데이트를 보장하기 위해 이러한 렌더링 프로세스를 거친다.
정리
업데이트 발생 시, 동시에 발생한 업데이트를 모아 한번만 DOM을 수정한다. 그래서 대부분의 상황에 충분히 빠른 속도로 화면 업데이트가 이루어진다. 이러한 과정을 Reconciliation(재 조정)이라 한다.
하지만 이는 대부분의 상황에 충분히 빠른 속도를 제공할 뿐, 항상 최고의 속도를 보장해주지는 못한다. 그 이유는 Virtual DOM을 생성하고 비교하는데에도 연산이 소요되기 때문이다.
Render Phase가 완료되면 리액트는 즉시 Commit Phase를 실행하지 않고, 다른 높은 우선순위 작업이 있다면 먼저 처리한 후 나중에 Commit Phase를 실행할 수 있다.
이러한 단계적 진행을 통해 React는 동기화가 필요한 작업을 효율적으로 관리하여 사용자 경험을 개선한다.
Render Phase에서 모든 변경 사항이 Fiber Tree에 준비된 상태에서 Commit Phase로 넘어가므로, Render와 Commit 단계의 일관성이 유지된다.
이렇게 두 단계는 순차적으로 작동하여, UI가 정확하게 동기화되고 불필요한 재렌더링을 방지한다.