개발/Javascript & Typescript

[React] 리액트의 Render Phase와 Commit Phase 완벽 정리

devhooney 2025. 7. 7. 08:30
728x90

리액트를 쓰다 보면 "렌더링"이라는 말을 자주 접하게 된다. 하지만 리액트의 렌더링 과정은 단순히 return <div> 하는 걸로 끝나는 게 아니다. React의 내부 렌더링은 두 단계, Render Phase와 Commit Phase로 나뉘며, 이 둘을 명확히 이해해야만 예상치 못한 사이드 이펙트나 성능 문제를 피할 수 있다.

이번 포스팅에서는 이 두 단계가 각각 무엇을 하는지, 왜 나뉘어 있는지, 개발 시 주의할 점은 무엇인지까지 깔끔하게 정리해본다.

 

 


 

 

 

🎬 리액트 렌더링 프로세스의 큰 그림


리액트의 렌더링은 다음과 같이 이루어진다:
- Render Phase (렌더 단계): 어떤 UI를 보여줄지 계산하는 단계
- Commit Phase (커밋 단계): 계산된 결과를 DOM에 실제 반영하는 단계

이 둘을 분리한 이유는 성능 최적화, 병렬 처리(Concurrent Rendering), 예측 가능한 사이드 이펙트 실행 등을 가능하게 하기 위해서다.

 

 

 

 


 

 

 

 

728x90

 

 

 

🔁 1. Render Phase — 계산만 하는 순수한 단계


✅ 이 단계에서 하는 일
JSX를 분석해서 React Element Tree 생성

상태(state), props, context 등을 기반으로 새로운 UI를 계산

이 과정에서 DOM은 절대 변경되지 않음

 

 

🧠 중요한 특징

항목 설명
💡 DOM 변경 ❌ 없음
⛔ 사이드 이펙트 ❌ 금지 (useEffect나 DOM 조작은 여기서 실행되지 않음)
🔁 실행 횟수 여러 번 발생 가능
🧵 중단 가능 ✅ (Concurrent Mode에서는 중단하고 재시작 가능)

 

 

 

 

📌 예제

function Counter({ count }) {
  console.log("Rendering:", count); // Render Phase에서 실행됨
  return <div>{count}</div>;
}

 

console.log는 Render Phase에서 실행되며, useEffect나 DOM 변경은 이 시점엔 일어나지 않는다.

 

 


 

 

 

 

 

✅ 2. Commit Phase — 실제로 DOM을 바꾸는 단계


Render Phase에서 계산된 내용을 실제 화면에 반영하는 단계다. 이때가 되어야만 사이드 이펙트가 실행된다.

✅ 이 단계에서 하는 일
- 실제 DOM 조작 (ReactDOM 내부에서 발생)
- ref 연결
- useEffect, useLayoutEffect 실행

- componentDidMount, componentDidUpdate, componentWillUnmount 호출 (클래스 컴포넌트의 경우)

 


🧠 중요한 특징

항목 설명
✅ DOM 변경 이 시점에 반영
🔥 사이드 이펙트 실행 useEffect, useLayoutEffect
🔁 실행 횟수 한 번만 발생
🚫 중단 불가 반드시 끝까지 수행됨

 

 

📌 예제

useEffect(() => {
  console.log("Effect 실행됨!"); // Commit Phase에서 실행
}, []);

 

useEffect는 항상 Commit Phase에서만 실행되며, 그 전에는 아무 일도 하지 않는다.

 

 

 

🧪 예시 흐름: 상태 업데이트가 발생할 때

function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("커밋 완료!"); // Commit Phase
  }, [count]);

  return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}

 

클릭 시 실행 흐름
- setCount로 상태 업데이트
- Render Phase:
  > 컴포넌트 함수 실행
  > 새로운 JSX 계산
- Commit Phase:
  > DOM 업데이트
  > useEffect 실행 → "커밋 완료!" 로그 출력

 

 

 

 

 


 

🧠 왜 두 단계로 나누는 걸까?


1. 성능 최적화
렌더 단계는 비동기적으로 여러 번 수행 가능 → 빠르고 유연한 업데이트

DOM에 영향을 주지 않으므로 안전하게 "계산"만 가능

2. 예측 가능한 사이드 이펙트
useEffect는 항상 Commit 후 실행 → 비동기 로직과 충돌 최소화

3. 병렬/중단 처리 지원 (React 18 이후)
Render 중 우선순위 낮은 작업은 취소 가능 → 사용자 인터랙션 우선 처리

 

 


 

🧭 개발 시 주의할 점

체크리스트 이유
render()나 함수형 컴포넌트 안에서는 사이드 이펙트를 넣지 말 것 렌더는 여러 번 실행될 수 있음
useEffect, useLayoutEffect는 실제로 DOM이 업데이트된 후 실행됨 사이드 이펙트를 Commit Phase에서 안전하게 실행
StrictMode에서는 렌더링이 2번 호출됨 부작용을 감지하기 위한 의도적인 설계

 

 

리액트를 깊이 이해하고 싶다면 렌더와 커밋의 차이를 감각적으로 익히는 것이 매우 중요하다. 이 두 단계를 잘 이해하면 불필요한 렌더링을 피하고, 예측 가능한 코드 작성이 가능해진다. 특히 useEffect, ref, setState의 동작 시점을 오해하지 않도록 주의하자!

 

 

 

 

 

728x90