[6장]Component의 Life Cycle
React에서 Component를 생성하는 방법은 함수로 만드는 방법과 클래스로 만드는 방법이 있다는 것을 학습했었다. 클래스로 만들 경우 React.Component를 상속하여 만들게 되고, 이 경우 Component가 그려지고 갱신되고, 없어지는 일련의 과정에 대한 메소드를 제공한다. 우리는 Component가 그려지고 갱신되고, 없어지는 일련의 과정을 Life Cycle(이하: 생명주기)이라고 부른다. 이 생명 주기를 잘 이해해야 Component에 대한 자원을 할당 및 제거를 적절한 타이밍에 처리 할 수 있다.
우선 Component의 생명 주기는 크게 Mount, Update, UnMount 이렇게 진행되고 각각은 좀 더 세분화된 메소드를 통해 생명주기를 관리하게 된다. 이번 장에서는 생명주기와 관련된 메소드에 대해서 알아보자.
Component의 Life Cycle
위 그림과 같은 세부 라이프 사이클을 가진다.
라이프 사이클은 크게 Mount, Update, UnMound 3개의 큰 분류를 가진다. 여기서 Mount는 최초 한번 동작하게 되고, 이후 변경이 발생하는 내용에 대해서는 Update의 라이프 사이클을 가지게 된다. Mount를 보게 되면 Component의 constructor부터 시작하여 render를 거쳐 componentDidMount에서 끝나고 단 한번 동작 하는데, 여기서 Component에서 필요한 자원을 셋팅을 해야 Component 변경 시에 계속적으로 자원을 셋팅하지 않는다. 이제부터는 위에서 설명한 내용이 맞는지 코드를 보면서 확인해보자.
Life Cycle 이해를 위한 소스
import React from 'react';
import ReactDOM from 'react-dom';
import ComponentLifeCycle from './ComponentLifeCycle';
import React from 'react';
import ReactDOM from 'react-dom';
import ComponentLifeCycle from './ComponentLifeCycle';
class Layer extends React.Component {
constructor(props) {
super(props);
setTimeout(function() {
ReactDOM.render(<ComponentLifeCycle hi='우현ㅎㅇ'/>, document.getElementById('componentLifeCycleComponent'))
}, 1000)
setTimeout(function() {
ReactDOM.render(<ComponentLifeCycle hi='준영ㅎㅇ'/>, document.getElementById('componentLifeCycleComponent'))
}, 2000)
setTimeout(function() {
ReactDOM.unmountComponentAtNode(document.getElementById('componentLifeCycleComponent'));
}, 3000)
}
render() {
return (
<div id="componentLifeCycleComponent">
</div>
)
}
componentDidCatch(err, info) {
console.log('componentDidCatch 호출');
}
}
export default Layer;
export default Layer;
우선 Layer Component를 보면 constructor에서 1, 2 초 간격으로 ComponentLifeCycle Component를 그려주고 3초 뒤에 해당 Component를 unmount 한다. 여기서 중요한 포인트가 하나 있는데, 1,2초 간격으로 Component를 두번 그리면 해당 컴포넌트의 라이플 사이클이 Mount부터 시작 되지 않고 뒤에 그려지는 Component는 Update 생명 주기를 가지게 된다. 라이프 사이클에 대한 소스를 다 보고 난 뒤, setTimeout으로 동작하는 내용들을 적절한 생명 주기에 위치 시켜 보도록 하자.
import React from 'react';
class ComponentLifeCycle extends React.Component {
constructor(props) {
super(props);
this.state = {};
console.log("Mount 단계 시 호출 - constructor");
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log("Mount, Update 단계 시 호출 - getDerivedStateFromProps");
return prevState;
}
render() {
console.log("render 동작");
return (
<div>
<h3>컴포넌트 라이프 사이클</h3>
<h3>{this.props.hi}</h3>
</div>
)
}
componentDidMount() {
console.log("Mount 단계 시 호출 - componentDidMount");
}
shouldComponentUpdate(nextProps, nextState) {
console.log("Update 단계 시 호출 - shouldComponentUpdate");
return true;
}
getSnapshotBeforeUpdate(nextProps, nextState) {
console.log("Update 단계 시 호출 - getSnapshotBeforeUpdate");
return true;
}
componentDidUpdate() {
console.log("Update 단계 시 호출 - componentDidUpdate");
}
componentWillUnmount() {
console.log("Unmount 단계 시 호출 - componentWillUnmount");
}
componentDidCatch() {
console.log("자식의 에러 캐치 시 호출");
}
}
export default ComponentLifeCycle;
이제 소스를 돌려보면 아래 그림과 같이 생명 주기가 동작하는 것을 볼 수 있다.
처음에 설명했던 것과 같은 라이프 사이클이 동작하는 것을 확인 할 수 있다. 그럼 setTimeout을 이용해서 ComponentLifeCycle Component를 그렸던 Layer Component를 라이프 사이클을 이용해서 ComponentLifeCycle Component를 그리도록 바꿔보자.
import React from 'react';
import ReactDOM from 'react-dom';
import ComponentLifeCycle from './ComponentLifeCycle';
class Layer extends React.Component {
render() {
return (
<div id="componentLifeCycleComponent">
</div>
)
}
componentDidMount() {
//ComponentLifeCycle Component를 render하는건 componentDidMount에서
ReactDOM.render(<ComponentLifeCycle hi='우현ㅎㅇ'/>, document.getElementById('componentLifeCycleComponent'))
ReactDOM.render(<ComponentLifeCycle hi='준영ㅎㅇ'/>, document.getElementById('componentLifeCycleComponent'))
ReactDOM.unmountComponentAtNode(document.getElementById('componentLifeCycleComponent'));
}
}
export default Layer;
ComponentLifeCycle이 그려지려면 Layer의 render함수가 동작 한 뒤에야 그려질 수 있으므로 componentDidMount에 ComponentLifeCycle Component를 그리고 지우는 로직을 constructor에서 가져왔다. 이렇게 하고 실행 시켜보면, 아무 반응이 일어나지 않는다. 똑똑한 React가 한 생명주기에서 바로 Unmount 되는 동작에 대해서 최적화를 하면서 해당 소스가 아래와 같이 바꼇을 것이기 때문이다. 즉, 우리가 생각하고 짯던 소스 코드가 React의 compiler에 의해서 우리가 생각했던 것과는 다르게 변경 될 수 있다는 것을 명심하자.
componentDidMount() {
//ComponentLifeCycle Component를 render하는건 componentDidMount에서
//compile 과정에서 제거됨
//ReactDOM.render(<ComponentLifeCycle hi='우현ㅎㅇ'/>, document.getElementById('componentLifeCycleComponent'))
//compile 과정에서 제거됨
//ReactDOM.render(<ComponentLifeCycle hi='준영ㅎㅇ'/>, document.getElementById('componentLifeCycleComponent'))
ReactDOM.unmountComponentAtNode(document.getElementById('componentLifeCycleComponent'));
}
그래서 이 경우 최초 그리는 경우를 제외 하고 setTimeout을 사용해줘야 한다.
componentDidMount() {
//ComponentLifeCycle Component를 render하는건 componentDidMount에서
ReactDOM.render(<ComponentLifeCycle hi='우현ㅎㅇ'/>, document.getElementById('componentLifeCycleComponent'))
setTimeout(function() {
ReactDOM.render(<ComponentLifeCycle hi='준영ㅎㅇ'/>, document.getElementById('componentLifeCycleComponent'))
}, 1000)
setTimeout(function() {
ReactDOM.unmountComponentAtNode(document.getElementById('componentLifeCycleComponent'));
}, 2000)
}
정리
Component 생명 주기에 대해서 알아 보았다. 전체적인 생명 주기를 이해 함에 있어서 큰 어려움은 없지만, getDerivedStateFromProps, shouldComponentUpdate, getSnapshotBeforeUpdate 이 3개의 메소드는 보고 이해를 하기 힘들다. 입력값이 있고 리턴 값이 있기 때문이다. 다음 포스팅에서는 이 3개의 함수가 정확히 무엇인지 알아보자.
Comments