모든 리액트 컴포넌트에는 라이프 사이클이 존재한다. 컴포넌트의 수명은 페이지에 렌더링 되기 전 준비과정부터 시작해서 페이지에서 사라질 때 끝이난다. 라이프 사이클 메서드는 클래스형 컴포넌트에서만 사용할 수 있고 함수형 컴포넌트에서는 Hooks기능을 사용해 비슷한 작업을 처리할 수 있다.
라이프 사이클 Method는 총 9가지 있다.
라이프 사이클은 총 3가지 마운트, 업데이트, 언마운트 카테고리로 나뉜다.
마운트(mount) -> DOM이 생성되고 웹 브라우저상에 나타나는 것
constructor
: 컴포넌트를 새로 만들때 마다 호출되는 클래스 생성자getDerivedStateFromProps
: props
에 있는 값을 state
에 넣을 때 사용render
: UI를 렌더링componentDidMount
: 컴포넌트가 웹 브라우저에 나타난 후 호출하는 메서드업데이트 -> 4가지의 경우에 업데이트
props가 바뀔 때, state가 바뀔 때, 부모 컴포넌트가 리렌더링될 때, this.forceUpdate
로 강제 렌더링 트리거할 때
getDerivedStateFromProps
: 마운트 과정에서도 호출되며, 업데이트가 시작하기 전에도 호출된다. props의 변화에 따라 state 값에도 변화를 주고 싶을 때 사용shouldComponentUpdate
: 컴포넌트가 리렌더링을 해야 할지 말아야 할지를 결정하는 Method, 이 Method에서는 true
또는 false
값을 반환해야 하며 true
를 반환하면 다음 라이플 사이클 Method를 계속 실행하고, false
를 반환하면 작업을 중지함. 특정 함수에서 this.forceUpdate
함수를 호출하면 이 과정을 생략하고 render
함수를 호출render
: 컴포넌트를 렌더링getSnapshotBeforeUpdate
: 컴포넌트 변화를 DOM에 반영하기 바로 직전에 호출componentDidUpdate
: 컴포넌트의 업데이트 작업이 끝난 후 호출언마운트(unmount) -> 마운트의 반대 과정, 컴포넌트를 DOM에서 제거하는 것
componentWillUnmount
: 컴포넌트가 웹 브라우저상에서 사라지기 전에 호출이 메서드는 클래스형 컴포넌트 React를 사용해봤다면 아마도 매우 익숙할것이다. 이 Method는 컴포넌트 모양을 정의합니다 그렇기 때문에 가장 중요한 메서드라고 할 수 있고, 라이프 사이클 Method중 유일한 필수 Method이기도 합니다. 이 Method 안에서는 이벤트 설정이 아닌 다른곳에서 setState
를 사용하면 안되며, 브라우저 DOM
에도 접근하면 안된다. state
에 변화를 주거나 DOM
접근할 때는 componentDidMount
에서 처리해야 한다.
constructor
는 컴포넌트의 생성자 Method로 컴포넌트를 만들 때 처음으로 실행된다. 이 Method에서는 초기 state
를 정할 수 있다.
getDerivedStateFromProps
는 react v16.3
이후에 새로 만든 라이프 사이클 Method입니다.
props
로 받아온 값을 state
에 동기화 시키는 용도로 사용하며, 컴포넌트가 마운트될 때랑 업데이트될 때 호출
static getDerivedStateFromProps(nextProps, prevState) {
// props에서 파생된 state를 동기화
if (nextProps.value !== prevState.value) {
return {
value: nextProps.value,
};
}
// 변경이 없으면 null 반환하여 state를 변경하지 않음
return null;
}
componentDidMount
는 컴포넌트가 마운트된 직후에 호출되는 라이프 사이클 메소드로, 주로 비동기 작업이나 이벤트 리스너 등록 등에 사용됩니다. 아래는 componentDidMount
의 사용 예시
class ExampleComponent extends Component {
// 초기 state를 설정합니다.
state = {
data: null,
loading: true,
};
// 컴포넌트가 마운트된 직후에 호출됩니다.
componentDidMount() {
// 네트워크 요청을 통해 데이터를 가져옵니다.
fetch('https://api.example.com/data')
.then(response => response.json()) // 응답을 JSON으로 변환합니다.
.then(data => this.setState({ data, loading: false })) // 데이터를 state에 저장하고 로딩 상태를 false로 변경합니다.
.catch(() => this.setState({ loading: false })); // 오류가 발생하면 로딩 상태만 false로 변경합니다.
// 윈도우 리사이즈 이벤트 리스너를 등록합니다.
window.addEventListener('resize', this.handleResize);
}
// 컴포넌트가 언마운트되기 직전에 호출됩니다.
componentWillUnmount() {
// 리사이즈 이벤트 리스너를 제거합니다.
window.removeEventListener('resize', this.handleResize);
}
// 리사이즈 이벤트 핸들러 함수입니다.
handleResize = () => {
console.log('Window resized');
};
// 컴포넌트를 렌더링합니다.
render() {
const { data, loading } = this.state;
// 로딩 중에는 "Loading..."을, 로딩이 완료되면 데이터를 출력합니다.
return loading ? <p>Loading...</p> : <div>Data: {JSON.stringify(data)}</div>;
}
}
shouldComponentUpdate
는 컴포넌트가 업데이트되기 전에 호출되어 컴포넌트가 업데이트될지 여부를 결정합니다. 기본적으로 shouldComponentUpdate
는 true
를 반환하며, false
를 반환하면 컴포넌트는 업데이트되지 않습니다. 이를 통해 불필요한 렌더링을 방지하고 성능을 최적화할 수 있습니다.
class ExampleComponent extends Component {
state = {
count: 0,
};
shouldComponentUpdate(nextProps, nextState) {
// 현재 state의 count와 새로운 state의 count가 다를 때만 업데이트
return this.state.count !== nextState.count;
}
increment = () => {
this.setState((prevState) => ({ count: prevState.count + 1 }));
};
render() {
console.log('Component re-rendered');
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
getSnapshotBeforeUpdate
는 컴포넌트가 DOM
에 업데이트되기 직전에 호출됩니다. 이 메소드는 DOM에서의 변경 사항을 반영하기 전에 특정 값을 캡처할 수 있게 합니다. 메소드가 반환한 값은 componentDidUpdate
로 전달됩니다.
componentDidUpdate
는 컴포넌트가 업데이트된 후에 호출됩니다. 이 메소드는 이전 props
와 state
를 참조하여 컴포넌트가 업데이트된 후에 실행해야 하는 작업을 수행하는 데 사용됩니다. 예를 들어, DOM
조작, 네트워크 요청, 스크롤 위치 복구 등이 가능합니다.
class ChatComponent extends Component {
constructor(props) {
super(props);
this.chatContainerRef = React.createRef();
}
state = {
messages: ['Hello', 'fffff', 'bye'],
};
// 컴포넌트가 DOM에 업데이트되기 직전에 호출됩니다.
getSnapshotBeforeUpdate(prevProps, prevState) {
// 스크롤 위치를 캡처합니다.
if (prevState.messages.length < this.state.messages.length) {
const chatContainer = this.chatContainerRef.current;
return chatContainer.scrollHeight - chatContainer.scrollTop;
}
return null;
}
// 컴포넌트가 DOM에 업데이트된 후에 호출됩니다.
componentDidUpdate(prevProps, prevState, snapshot) {
// 캡처한 스크롤 위치를 복구합니다.
if (snapshot !== null) {
const chatContainer = this.chatContainerRef.current;
chatContainer.scrollTop = chatContainer.scrollHeight - snapshot;
}
}
render() {
return (
// ...
);
}
}
componentWillUnmount
는 컴포넌트가 DOM
에서 제거되기 직전에 호출됩니다. 이 메소드는 타이머 제거, 이벤트 리스너 해제, 네트워크 요청 취소 등 정리 작업을 수행하는 데 사용됩니다.
class TimerComponent extends Component {
state = {
count: 0,
};
componentDidMount() {
// 1초마다 count를 증가시키는 타이머 설정
this.timerID = setInterval(() => {
this.setState((prevState) => ({ count: prevState.count + 1 }));
}, 1000);
}
componentWillUnmount() {
// 타이머를 정리합니다.
clearInterval(this.timerID);
}
render() {
return <div>Count: {this.state.count}</div>;
}
componentDidCatch는 컴포넌트의 하위 트리에 있는 컴포넌트에서 오류가 발생했을 때 호출됩니다. 이 메소드를 사용하여 오류를 잡고, 오류 메시지를 표시하거나 로깅하는 등의 작업을 수행할 수 있습니다.
class ErrorBoundary extends Component {
state = {
hasError: false,
errorMessage: '',
};
// 하위 컴포넌트에서 오류가 발생했을 때 호출됩니다.
componentDidCatch(error, errorInfo) {
// 오류를 state에 저장합니다.
this.setState({
hasError: true,
errorMessage: error.toString(),
});
// 오류 정보를 로깅하거나 외부 서비스에 전송할 수 있습니다.
console.error("Error caught by ErrorBoundary:", error, errorInfo);
}
render() {
if (this.state.hasError) {
// 오류가 발생하면 사용자에게 표시할 내용을 정의합니다.
return <h1>Something went wrong: {this.state.errorMessage}</h1>;
}
// 오류가 발생하지 않으면 하위 컴포넌트를 렌더링합니다.
return this.props.children;
}
}
class BuggyComponent extends Component {
render() {
// 일부러 오류를 발생시킵니다.
throw new Error("I crashed!");
return <div>Buggy Component</div>;
}
}
function App() {
return (
<ErrorBoundary>
<BuggyComponent />
</ErrorBoundary>
);
}
리액트의 라이프 사이클 메소드들은 컴포넌트의 생성, 업데이트, 제거 과정에서 다양한 작업을 수행할 수 있도록 도와줍니다. 이를 통해 상태 관리, DOM 조작, 이벤트 처리, 에러 처리 등을 효율적으로 수행할 수 있습니다. 클래스형 컴포넌트에서 이러한 메소드를 적절히 사용함으로써 더욱 안정적이고 성능 최적화된 애플리케이션을 개발할 수 있습니다.