리액트 렌더링의 2단계: Render Phase와 Commit Phase의 차이점

크리스가 리액트 코드를 디버깅하다가 고개를 갸웃거린다.
"분명 console.log가 찍혔는데, 왜 화면은 그대로지? 그리고 왜 콘솔은 두 번씩 찍히는 거야?"
많은 개발자가 "렌더링(Rendering)"이라는 단어를 "화면을 그리는 것(Painting)"과 혼동하곤 한다. 하지만 리액트의 세계에서 렌더링은 화면 업데이트만을 의미하지 않는다. 심지어 렌더링은 일어났지만, 화면은 1픽셀도 변하지 않을 수도 있다.
리액트가 컴포넌트를 호출하고 화면에 반영하기까지의 과정을 정확히 이해해야 불필요한 재렌더링을 막고, useEffect가 언제 실행되는지 예측할 수 있다. 그 비밀인 Render Phase와 Commit Phase를 파헤쳐 본다.
리액트의 주문 시스템
리액트의 렌더링 과정을 식당 주문에 비유해보자.
중요한 건, 주문서를 쓴다고 해서(Render Phase) 무조건 요리가 나오는 것(Commit Phase)은 아니다라는 점이다. 주문 내용이 이전과 똑같다면 주방장은 요리를 시작하지 않는다.
1. Render Phase (렌더 단계)
이 단계는 "계산"의 영역이다. 리액트는 컴포넌트 함수를 호출하여 리턴된 JSX(Virtual DOM)를 이전 렌더링 결과와 비교한다. 이것을 재조정(Reconciliation)이라고 한다.
// Render Phase 예시
function UserProfile({ name }: { name: string }) {
// 1. 컴포넌트 본문 실행 (Render Phase)
console.log('Render Phase: 계산 중...');
// 2. JSX 반환 (이 객체를 이전 것과 비교함)
return <div>{name}</div>;
}왜 콘솔이 두 번 찍힐까? (Strict Mode)
개발 모드에서 console.log가 두 번 찍히는 이유는 리액트의 Strict Mode 때문이다. 리액트는 "Render Phase가 순수(Pure)한가?"를 검증하기 위해 컴포넌트를 의도적으로 두 번 호출해본다. 두 번 실행했을 때 결과가 다르다면 사이드 이펙트가 있다는 뜻이니 고치라는 신호다.
2. Commit Phase (커밋 단계)
이 단계는 "적용"의 영역이다. Render Phase에서 계산된 "변경 사항"을 실제 DOM에 반영한다.
// Commit Phase 이후 실행
useEffect(() => {
console.log('Commit Phase 완료 후: 이제 사이드 이펙트 실행');
}, []);전체 흐름도
크리스가 버튼을 눌러 상태를 변경했을 때의 흐름을 따라가 보자.
만약 상태를 setCount(0)으로 똑같은 값으로 업데이트했다면?
Render Phase에서 비교해 보니 변경 사항이 없다. 리액트는 Commit Phase를 생략한다. 즉, DOM 조작 비용을 아끼는 것이다.
실전 코드: 단계별 실행 순서 확인
아래 코드를 실행하면 콘솔에는 어떤 순서로 출력될까?
import React, { useState, useEffect, useLayoutEffect } from 'react';
function CycleTest() {
console.log('1. Render Phase');
const [count, setCount] = useState(0);
useLayoutEffect(() => {
console.log('2. Commit Phase (DOM 업데이트 직후, Paint 전)');
}, [count]);
useEffect(() => {
console.log('3. Commit Phase (Paint 후)');
}, [count]);
return (
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
);
}결과는 1 -> 2 -> 3 순서다.
특히 useLayoutEffect는 브라우저가 화면을 그리기 전에 실행되므로, 깜빡임 없이 DOM 레이아웃을 측정하거나 변경해야 할 때 사용한다. (대부분은 useEffect로 충분하다.)
핵심 정리
이제 렌더링의 겉과 속을 알았으니, 리액트가 화면을 전환하는 방법인 라우팅에 대해 알아볼 차례다.
"React Router와 History API: SPA는 어떻게 페이지를 전환할까?" 편에서 계속된다.