컴포넌트의 생명주기를 온전히 이해하기 위해 먼저 렌더링 과정부터 살펴본다.
리액트에서의 렌더링이란
브라우저가 렌더링에 필요한 DOM 트리를 만드는 과정이다.
리액트 공식 문서에서는 이를
렌더링 = 컴포넌트 호출이라고 표현한다.
렌더링 프로세스는 크게 두 단계로 나뉜다.
render phase → commit phase

함수 컴포넌트는 return 문으로 JSX를 반환한다.
function App() {return <h1>Hello World</h1>;}
하지만 실제로 반환되는 것은 JSX가 아니라 ReactElement이다.
import React from 'react';function App() {return React.createElement('h1', null, 'Hello world');}
그래서 항상 import React가 필요했다.
function App() {return <h1>Hello World</h1>;}
컴파일 결과:
import { jsx as _jsx } from 'react/jsx-runtime';function App() {return _jsx('h1', { children: 'Hello world' });}
컴파일러가 자동으로 ReactElement 변환 코드를 삽입해준다.
여기서 가장 중요한 포인트:
React 렌더링과 브라우저 렌더링은 완전히 다르다.
순서:
React render→ React commit (DOM 업데이트)→ Browser render (paint)
즉:
앞에서 렌더링을 “컴포넌트 호출”이라고 했는데,
좀 더 정확하게 쓰면 다음과 같다.
렌더링이란컴포넌트를 호출하여 JSX를 ReactElement로 변환하고,VDOM을 재조정(reconcile)하는 과정이다.(현재는 fiber 구조 기반)
구조적으로 보면:
reconciler: 컴포넌트 호출 + VDOM 재조정renderer: 실제 DOM에 마운트
그래서 항상 구분해야 한다.
구분
컴포넌트 호출
DOM 삽입
브라우저 렌더링
이 세 개는 서로 다른 단계다.
commit 단계는 보통 매우 빠르다.
하지만 render phase는 느릴 수 있다.
특히 concurrent mode에서는:
그래서 React는:
이 때문에 render phase 안에서는
side effect를 절대 넣으면 안 된다.

컴포넌트 생명주기란:
컴포넌트가 생성, 변경, 제거되는 전체 사이클
3단계로 나뉜다.
단계
Mount
Update
Unmount
lazy initialize→ render phase→ commit phase→ layoutEffect 실행→ 브라우저 렌더링→ effect 실행
render phase→ commit phase→ layoutEffect 정리→ layoutEffect 실행→ 브라우저 렌더링→ effect 정리→ effect 실행
layoutEffect 정리→ effect 정리
useState(() => {console.log("init");return 0;});
Update는 state 변경으로 렌더링이 다시 트리거된 상황
추가되는 점:
언마운트 시에는:
오직:
layoutEffect 정리→ effect 정리
useEffect는:
외부 시스템과 컴포넌트를 동기화하는 Hook
외부 시스템 예시:
useEffect(setup, dependencies);
순서:
의존성 배열은:
effect 내부에서 참조한 모든 반응형 값의 목록
반응형 값:
선택이 아니라 필수
구분
사용자가 클릭 → 이벤트 핸들러
화면에 나타났기 때문 → effect
렌더링을 위한 계산을 effect로 하면 안 된다.
// ❌useEffect(() => {setFullName(first + last);}, [first, last]);
// ✅const fullName = first + last;
props 변경으로 state 수정도 effect 불필요
렌더링 중에 이전 값과 비교해서 처리 가능
setCardBrand→ render → commit → paint→ effect → setCardNumber→ render → commit → paint
useEffect(() => {if (length > max) {setCardNumber(card.substring(0, max));}}, [cardBrand])
setCardBrand→ render→ render phase update→ commit → paint
if (length > max) {setCardNumber(card.substring(0, max));}
useLayoutEffect는:
브라우저가 화면을 그리기 전에 실행되는 effect
특징:
DOM을 기준으로 즉시 UI 보정이 필요할 때
예시:
useEffect(() => {if (value === 0) {setValue(Math.random() * 200);}}, [value
결과:
useLayoutEffect(() => {if (value === 0) {setValue(Math.random() * 200);}}, [value
결과:
React는 항상 이 순서로 움직인다.
State 변경→ render phase (계산)→ commit phase (DOM 반영)→ useLayoutEffect→ 브라우저 렌더링→ useEffect
생명주기는 이 렌더링 파이프라인 위에서 시간 구간으로 나뉜 개념일 뿐이다.