React의 라이프 사이클에 대해 설명해 주세요.
면접용
리액트의 라이프사이클은 각 컴포넌트에 존재하는 마운트, 업데이트, 언마운트로 이루어진 주기를 뜻합니다. 라이프사이클은 다양한 메서드들로 관리할 수 있습니다.
클래스형 컴포넌트에서는 componentDidMount, componentDidUpdate, componentWillUnmount 메서드를 사용하여 상태 관리 및 리소스를 정리할 수 있습니다.
함수형 컴포넌트에서는 16.8 버전부터 추가된 hook을 이용하여 같은 기능을 사용할 수 있습니다. useState와 useEffect 훅을 사용해 마운트, 업데이트, 언마운트에 해당하는 작업을 처리할 수 있습니다.
개념설명
리액트의 주요 특징 중 하나는 컴포넌트 기반 아키텍쳐라는 점입니다. 각 컴포넌트는 자체적인 라이프사이클을 다음처럼 가지고 있습니다.
컴포넌트 마운트(생성)
컴포넌트 업데이트
컴포넌트 언마운트(제거)
라이프사이클을 통해 컴포넌트가 언제 어떻게 동작하는지 예측할 수 있으며, 라이프사이클 API를 활용해 컴포넌트의 상태와 동작을 제어할 수 있습니다.
라이프사이클 메서드는 클래스형과 함수형 컴포넌트에서 다소 차이가 있습니다. React 16.8 버전 이후부터 Hooks 업데이트가 추가되면서 함수형 방식에서도 라이프사이클을 다룰 수 있게 되었습니다.
클래스형 컴포넌트에서의 라이프사이클 메서드
리액트 컴포넌트가 생겨나는 Mount 단계에서는 다음과 같은 라이프사이클 메서드가 생성되고, 실행됩니다.
constructor()
: 생성자 메서드static getDerivedStateFromProps()
: 부모 컴포넌트에서 전달되는 props가 변경될 때마다 state를 업데이트해주는 메서드함수형 컴포넌트에서 useState의 set함수를 렌더링 과정 중에 사용하는 것
/** 자식 컴포넌트 */ class Counter extends Component { constructor(props) { super(props); // 초기 state 설정 this.state = { count: 0 }; } // static getDerivedStateFromProps 메서드 static getDerivedStateFromProps(nextProps, prevState) { // 부모에서 전달되는 count 값이 변경될 때마다 상태 업데이트 if (nextProps.count !== prevState.count) { return { count: nextProps.count }; } // 변경 사항이 없으면 null을 반환하여 상태를 변경하지 않음 return null; } render() { return ( <div> <h1>Current Count: {this.state.count}</h1> </div> ); } } /** 부모 컴포넌트 */ class Parent extends Component { constructor(props) { super(props); this.state = { count: 0 }; } increment = () => { this.setState({ count: this.state.count + 1 }); }; render() { return ( <div> <button onClick={this.increment}>Increment</button> <Counter count={this.state.count} /> </div> ); } }
render()
: JSX를 리턴하면 HTML로 컴파일되어 보여지게 되는 라이플 사이클 메서드입니다.state, props가 변화되었을 때 재렌더링됩니다.
componentDidMount()
: 컴포넌트가 생성되면 트리거되는 메서드입니다.컴포넌트가 생겼을 때 한번만 실행되며, 변화가 일어나도 재실행되지 않습니다.
다음으로, 컴포넌트 Update 단계에서 실행되는 메서드는 다음과 같습니다.
static getDerivedStateFromProps()
shouldComponentUpdate()
: return 하는 값(boolean)에 따라 컴포넌트의 렌더링 여부가 결정됩니다.shouldComponentUpdate(nextProps, nextState){ if (this.state !== nextState) { return true; } return false; }
render()
getSnapshotBeforeUpdate()
: render 메서드가 호출된 후에, 컴포넌트가 업데이트되기 직전 실행됩니다.업데이트 전 DOM의 상태를 스냅샷으로 캡처할 수 있습니다.
return값은 componentDidUpdate()의 인자가 됩니다.
ex) 스크롤 위치 추적
함수형 컴포넌트에서 사용할 수 없습니다.
componentDidUpdate()
: 재랜더링시 실행되는 메서드입니다.컴포넌트가 업데이트된 후 마지막으로 실행됩니다.
props, state가 변경되어 → 컴포넌트 렌더링이 끝나고 → DOM이 업데이트된 뒤에 → 실행됩니다.
DOM 업데이트 후에 추가적인 작업을 할 때 유용합니다.
class ScrollTracker extends Component { state = { scrollY: 0 }; // getSnapshotBeforeUpdate: 업데이트 직전에 호출되어 스크롤 위치 캡처 getSnapshotBeforeUpdate(prevProps, prevState) { return window.scrollY; // 현재 스크롤 위치 반환 } // componentDidUpdate: getSnapshotBeforeUpdate에서 반환된 값(snapshot) 사용 componentDidUpdate(prevProps, prevState, snapshot) { window.scrollTo(0, snapshot); // 이전 스크롤 위치로 복원 } render() { return ( <div> <h1>Scroll Tracker</h1> <div style={{ height: '1500px' }}> <p>Scroll down to track your scroll position.</p> </div> </div> ); } }
마지막으로 컴포넌트 Unmount 단계에서 생성되고, 실행되는 메서드는 다음과 같습니다.
componentWillUnmount()
: 컴포넌트가 사라질 때 실행되는 메서드입니다.기존 컴포넌트에 할당해둔 것을 해제할 때 사용합니다.
Chart.js와 같은 외부 라이브러리를 사용할 때, 컴포넌트가 제거되기 전에 인스턴스를 해제하여 메모리 누수를 막을 수 있습니다.
componentWillUnmount() { // 차트 인스턴스를 파괴하여 메모리 해제 if (this.chart) { this.chart.destroy(); } }
함수형 컴포넌트에서의 라이프사이클 메서드
함수형 컴포넌트에서는 클래스형보다 적은 개수의 메서드를 사용합니다. 또한 기존에 사용되는 메서드들을 hook을 통해 구현할 수 있습니다.
컴포넌트 마운트 시 실행되는 함수들을 hook으로 어떻게 대체할 수 있는지 살펴보겠습니다.
constructor
constructor() 메서드를 사용하는 클래스형과 달리 useState hook을 사용합니다.
//class class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 } ; } } // function => Hooks const Example = () => { const [count,setCount] = useState(0); }
render()
render 메서드를 명시적으로 쓰지 않고도 렌더링할 수 있습니다.
componentDidMount()
useEffect hook을 사용하여 해당 메서드를 구현합니다.
빈 의존성 배열을 사용해야 같은 기능을 구현할 수 있습니다.
// Class class Example extends React.Component { componentDidMount() { ... } } // Hooks const Example = () => { useEffect(() => { ... }, []); }
++ static getDerivedStateFromProps()
useState의 set 함수가 렌더링 중에 호출될 때와 같은 방식으로 동작합니다.
const [count, setCount] = useState(0);
const [isClicked, setIsClicked] = useState(false);
// 렌더링 중 상태를 업데이트
useEffect(() => {
if (!isClicked) {
setIsClicked(true); // 상태 업데이트로 컴포넌트가 리렌더링 될 것
setCount(10); // 상태 업데이트로 count도 변경
}
}, []); // 마운트 시에만 실행되도록 빈 배열
다음으로 컴포넌트 업데이트 시 실행되는 함수들을 hook으로 대체한 모습입니다.
render()
componentDidUpdate()
useEffect를 사용하여 동일하게 동작할 수 있습니다.
// class constructor(props) { super(props); this.state = { count: 0 }; } // componentDidUpdate는 props나 state가 변경된 후에 호출됩니다. componentDidUpdate(prevProps, prevState) { if (prevState.count !== this.state.count) { console.log('Count has updated to: ', this.state.count); } } // function const [count, setCount] = useState(0); // useEffect는 컴포넌트가 렌더링된 후에 실행됩니다. useEffect(() => { console.log('Count has updated to: ', count); }, [count]); // count가 변경될 때마다 실행됩니다.
마지막으로 컴포넌트 언마운트 단계에서 실행되는 함수들을 hook으로 대체한 모습입니다.
componentWillUnmount
useEffect, CleanUp 함수를 통해 구현할 수 있습니다.
const chartRef = useRef(null); // Chart.js 차트를 그릴 canvas 요소를 참조 useEffect(() => { // 차트 생성 const chart = new Chart(chartRef.current, { ... }); // 컴포넌트가 언마운트될 때 차트 인스턴스를 파괴하는 클린업 함수 return () => { if (chart) { chart.destroy(); // 차트 인스턴스 정리 } }; }, []); // 빈 배열로 설정하면 컴포넌트 마운트 시 한 번만 실행되고 언마운트 시 클린업 함수 실행
Last updated