렌더링 순서를 공부하다가 갑자기 useEffect나 React.memo를 제대로 이해하고 있는지 의문이 들었다. 각각의 역할은 아는데 전체 코드를 보고 console 출력 순서를 설명해보라고 하면 틀릴 것 같은 너낌.. 그래서 간단하게 정리해보았다.
React 컴포넌트가 실행되고 Virtual DOM을 만든 뒤, 실제 DOM을 업데이트하는 과정이다.
✅ 주요 단계
| 단계 | 설명 |
|---|---|
| Trigger | state, props가 변경되면 렌더링 필요 신호 발생 |
| Render | 컴포넌트 함수 실행 → Virtual DOM 생성 → 이전과 비교(diff) |
| Commit | 실제 DOM 업데이트 + useEffect, ref 처리 등 |
[Render Phase]
1. 부모 render() 실행
2. JSX 중 자식 컴포넌트 만나면 자식 render() 실행
3. JSX 트리 구성 (return 내부 console.log 실행됨)
[Commit Phase]
4. 실제 DOM 업데이트
5. useEffect 실행 (부모, 자식 순은 보장되지 않음)
👩🏻💻 App 컴포넌트
const App = () => {
const [count, setCount] = useState(0);
console.log("❤️ App render");
useEffect(() => {
console.log("❤️ App useEffect - 의존성 배열: 빈배열");
}, []);
useEffect(() => {
console.log("❤️ App useEffect - 의존성 배열: count", count);
}, [count]);
return (
<div>
{console.log("❤️ App return 내부 console")}
<h1>Count: {count}</h1>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
<Child />
</div>
);
};
👩🏻💻 Child 컴포넌트
const Child = () => {
console.log("💙 Child render");
useEffect(() => {
console.log("💙 Child useEffect");
}, []);
return (
<div>{console.log("💙 Child return 내부 console")}자식 컴포넌트</div>
);
};
✅ 결과확인
console 출력 → useEffect 출력
✅ 코드 + 주석으로 확인

✅ jsx 트리 구조를 만들기 위해 “부모 → 자식 순서로 render 함수를 실행”함 → 그 다음 commit 단계에서 useEffect 실행
<Child /> 만남👩🏻💻 Child 컴포넌트

✅ 결과확인
state나 props가 변경됨React.memo로 감쌌을 때 props가 바뀌지 않는다면 리렌더링을 하지 않음
count 값이 변해도 자식 컴포넌트(Child 컴포넌트)는 렌더링 되지 않는다.
따라서 props가 자주 바뀌지 않거나 자식이 복잡한 렌더링 로직을 가질 때 사용하면 효과적이다.
👩🏻💻 App + Child 컴포넌트 (props 추가)

✅ 기본 props 전달
count가 바뀌면 props도 바뀜 → 자식도 다시 실행✅ React.memo 적용 props 전달
React.memo를 적용해도 props가 바뀌면 자식 렌더링은 똑같이 발생한다.memo가 리렌더링을 막아준다.React.memo(Component, areEqual);
shallow equality(===) 로 비교한다.📚 참고자료