React로 UI를 만들다 보면, 컴포넌트의 탄생부터 소멸까지의 흐름, 즉 생명주기(lifecycle)를 이해하는 것이 매우 중요하다. 생명주기를 알면 언제 데이터를 가져오고, 언제 정리(clean-up)를 해야 하는지 명확히 파악할 수 있다.
이번 글에서는 React 컴포넌트의 생명주기 개념을 정리하고, 클래스 컴포넌트와 함수형 컴포넌트 각각에서 생명주기를 어떻게 다루는지 예제와 함께 소개한다.
React 컴포넌트는 다음 3단계를 거쳐 작동한다:
각 단계마다 호출되는 메서드(클래스) 또는 실행 타이밍(useEffect 등)이 존재한다.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
console.log("1. constructor");
}
static getDerivedStateFromProps(props, state) {
console.log("2. getDerivedStateFromProps");
return null;
}
componentDidMount() {
console.log("3. componentDidMount");
// API 호출 등 side effect 처리
}
shouldComponentUpdate(nextProps, nextState) {
console.log("4. shouldComponentUpdate");
return true; // false면 업데이트 생략
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log("5. getSnapshotBeforeUpdate");
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("6. componentDidUpdate");
}
componentWillUnmount() {
console.log("7. componentWillUnmount");
}
render() {
console.log("render 호출");
return <div>{this.state.count}</div>;
}
}
| 메서드 | 호출 시점 | 설명 |
|---|---|---|
| constructor | 컴포넌트 생성 시 | state 초기화, 메서드 바인딩 |
| getDerivedStateFromProps | props 변경 시 | state 동기화 목적 (static) |
| render | 매 렌더링 시 | JSX 반환 |
| componentDidMount | 마운트 직후 | 데이터 fetch, 구독 등록 등 |
| shouldComponentUpdate | 업데이트 직전 | 렌더링 여부 결정 |
| getSnapshotBeforeUpdate | render 후, DOM 반영 전 | 스크롤 위치 저장 등 |
| componentDidUpdate | 업데이트 완료 후 | side effect 처리 가능 |
| componentWillUnmount | 컴포넌트 제거 시 | 이벤트 제거, 타이머 해제 등 |
useEffect함수형 컴포넌트에서는 useEffect 훅을 활용하여 생명주기를 구현한다.
useEffect(() => {
console.log("마운트됨");
// API 호출 등
}, []);
useEffect(() => {
console.log("state 또는 props 변경됨");
}, [stateOrProps]);
useEffect(() => {
return () => {
console.log("컴포넌트 언마운트됨");
// 타이머 정리, 이벤트 제거 등
};
}, []);
useLayoutEffect, useInsertionEffectReact는 useEffect 외에도 생명주기와 관련된 고급 훅을 제공한다.
useLayoutEffect(() => {
console.log("레이아웃 effect 실행");
}, []);
useInsertionEffect(() => {
console.log("스타일 삽입 전에 실행");
}, []);
| 단계 | 클래스 컴포넌트 | 함수형 컴포넌트 |
|---|---|---|
| 마운트 | componentDidMount | useEffect(() => {}, []) |
| 업데이트 | componentDidUpdate | useEffect(() => {}, [deps]) |
| 언마운트 | componentWillUnmount | useEffect(() => return cleanup) |
| 동기 레이아웃 반영 | useLayoutEffect | |
| 스타일 삽입 직전 | useInsertionEffect |
React의 생명주기를 제대로 이해하면, 불필요한 렌더링을 줄이고, 효율적인 리소스 관리가 가능하다. 클래스 컴포넌트에서는 생명주기 메서드를 직접 다뤄야 했지만, 함수형 컴포넌트에서는 useEffect 하나로 대부분을 처리할 수 있어 훨씬 간결하다.
다만, 마운트/업데이트/언마운트 타이밍을 명확히 이해하지 못하면 useEffect가 의도치 않게 실행될 수 있으므로, 생명주기 개념을 기반으로 훅을 사용하는 습관을 들이는 것이 중요하다.
https://ko.legacy.reactjs.org/docs/state-and-lifecycle.html
https://ko.react.dev/learn/lifecycle-of-reactive-effects
메서드포테이토는 제가제일좋아하는음식중하나입니다~
힙하하~