← 개인 공부 · Frontend
STUDY NOTE · 개념 정리

React 컴포넌트 생명주기 정리: 클래스 메서드와 함수형 컴포넌트의 대응

Frontend
생명주기는 컴포넌트가 생성(마운트) → 갱신(업데이트) → 제거(언마운트) 되는 과정이다. 클래스 컴포넌트는 단계마다 메서드를 두고, 함수형 컴포넌트는 Hook으로 같은 시점을 표현한다.

1. 세 단계

단계언제대표적으로 하는 일
마운트 (Mount)컴포넌트가 처음 DOM에 붙을 때초기 데이터 요청, 구독 시작, 타이머 등록
업데이트 (Update)props·state·context가 바뀌어 다시 렌더될 때바뀐 값에 맞춰 외부와 다시 동기화
언마운트 (Unmount)DOM에서 제거될 때구독 해제, 타이머 정리, 요청 취소

에러가 났을 때를 위한 에러 처리 단계도 따로 있다(Error Boundary).

2. 업데이트가 일어나는 원인

원인설명
state 변경setState / useState의 setter 호출
부모 리렌더부모가 렌더되면 자식도 렌더 (props가 같아도)
context 변경구독 중인 Provider의 value가 바뀜
forceUpdate클래스에서 강제 렌더 (거의 쓰지 않음)

props가 바뀌어서 렌더되는 게 아니라, 부모가 렌더되면 자식이 렌더된다는 점이 중요하다. props 비교로 건너뛰려면 React.memo를 쓴다.

3. Render 단계와 Commit 단계

구분Render 단계Commit 단계
하는 일컴포넌트 함수(render) 호출, 이전 결과와 비교바뀐 부분을 실제 DOM에 반영
순수해야 하나예 (부수효과 금지)부수효과 가능
중단·재시작가능 (동시성 렌더링)불가 (한 번에 끝까지)
여기서 실행되는 것constructor, render, getDerivedStateFromProps, shouldComponentUpdatecomponentDidMount/Update, useLayoutEffect, (이후) useEffect

Render 단계는 React가 여러 번 실행하거나 중간에 버릴 수 있다. 그래서 렌더 중에 API 호출이나 DOM 조작 같은 부수효과를 넣으면 안 된다.

4. 클래스 컴포넌트 생명주기 메서드

단계메서드 (호출 순서)용도
마운트constructorstate 초기화, 메서드 바인딩
마운트static getDerivedStateFromPropsprops로 state를 맞춰야 할 때 (드묾)
마운트renderJSX 반환 (순수 함수)
마운트componentDidMountDOM 접근, 데이터 요청, 구독
업데이트static getDerivedStateFromProps위와 동일
업데이트shouldComponentUpdatefalse면 렌더 생략 (최적화)
업데이트render다시 렌더
업데이트getSnapshotBeforeUpdateDOM 변경 직전 값 기록 (스크롤 위치 등)
업데이트componentDidUpdate변경 후 처리, 이전 props와 비교해 재요청
언마운트componentWillUnmount정리 작업
에러static getDerivedStateFromError에러 시 대체 UI용 state 설정
에러componentDidCatch에러 로깅

componentWillMount, componentWillReceiveProps, componentWillUpdate는 동시성 렌더링에서 여러 번 호출될 수 있어 위험하다는 이유로 UNSAFE_ 접두사가 붙은 레거시가 됐다.

5. 함수형 컴포넌트에서의 대응

클래스함수형
constructoruseState 초기값 (무거우면 useState(() => 계산))
render함수 본문 자체
componentDidMountuseEffect(fn, [])
componentDidUpdateuseEffect(fn, [의존성])
componentWillUnmountuseEffect가 반환하는 cleanup
shouldComponentUpdateReact.memo (+ useMemo, useCallback)
getSnapshotBeforeUpdate대응 Hook 없음 (useLayoutEffect + ref로 흉내)
getDerivedStateFromProps렌더 중 계산, 또는 key로 초기화
Error Boundary대응 Hook 없음 (클래스로 작성해야 함)

함수형에서는 "언제 실행할까"보다 "무엇과 동기화할까"로 생각한다. useEffect 하나가 마운트·업데이트·언마운트를 의존성 배열로 함께 표현한다.

6. 실행 순서 (부모-자식)

시점순서
렌더부모 render → 자식 render (위에서 아래로)
마운트 완료자식 componentDidMount / useEffect → 부모 (아래에서 위로)
언마운트부모 cleanup → 자식 cleanup (위에서 아래로)

자식이 먼저 DOM에 붙어야 부모가 완성되므로 마운트 완료 콜백은 자식부터 실행된다.

7. 자주 헷갈리는 포인트

  1. key가 바뀌면 리렌더가 아니라 재마운트: 같은 위치라도 key가 다르면 React는 다른 컴포넌트로 보고 언마운트 후 새로 마운트한다. state가 초기화된다.
  2. StrictMode의 이중 실행: 개발 모드에서 마운트 → 언마운트 → 마운트를 한 번 더 해서 cleanup 누락을 드러낸다. 프로덕션에서는 한 번만 실행된다.
  3. Error Boundary가 잡지 못하는 것: 이벤트 핸들러 안의 에러, 비동기 코드(setTimeout, Promise)의 에러, 서버 렌더링 에러, Error Boundary 자신의 에러.

8. 실제로 어떻게 적용되나

  1. 목록 페이지 데이터 요청: 마운트 시 요청하고, 페이지 번호가 바뀌면(업데이트) 다시 요청하고, 언마운트 시 진행 중인 요청을 취소한다. 함수형에서는 의존성이 [page]인 useEffect 하나와 cleanup으로 세 단계를 모두 처리한다.
  2. 채팅 스크롤 유지: 새 메시지가 위에 추가될 때 스크롤 위치를 보존하려면 DOM이 바뀌기 직전 높이를 기록해야 한다. 클래스의 getSnapshotBeforeUpdate, 함수형에서는 useLayoutEffect + ref를 쓰는 대표 사례다.
  3. 선택한 사용자가 바뀌면 폼 초기화: props 변화를 감지해 state를 초기화하는 대신 <Form key={userId} />로 재마운트시킨다.
  4. 화면 일부의 에러 격리: 위젯 하나에서 에러가 나도 페이지 전체가 하얗게 되지 않도록 그 영역을 Error Boundary로 감싼다.

9. 면접 질문으로 정리

  1. Q. React 생명주기를 설명해주세요. 마운트, 업데이트, 언마운트 세 단계가 있고, 클래스는 componentDidMount·componentDidUpdate·componentWillUnmount 같은 메서드로, 함수형은 useEffect와 cleanup으로 각 시점의 작업을 처리한다.
  2. Q. Render 단계와 Commit 단계의 차이는? Render 단계는 컴포넌트를 호출해 바뀔 내용을 계산하는 순수한 단계로 중단·재시작될 수 있고, Commit 단계는 실제 DOM에 반영하고 부수효과를 실행하는 단계다.
  3. Q. componentDidMount와 useEffect(fn, [])는 완전히 같나? 실행 시점이 다르다. componentDidMount는 DOM 반영 직후 동기로 실행되어 useLayoutEffect에 가깝고, useEffect는 보통 화면을 그린 뒤 실행된다.
  4. Q. 리렌더링과 재마운트의 차이는? 리렌더링은 같은 인스턴스가 다시 렌더되어 state가 유지되고, 재마운트는 기존 컴포넌트를 제거하고 새로 만들어 state가 초기화된다. key 변경이 대표적인 재마운트 원인이다.
Gunmo Lee
React 컴포넌트 생명주기 정리: 클래스 메서드와 함수형 컴포넌트의 대응 | Gunmo's Dev Life