모든 리액트 컴포넌트에는 라이프사이클이 존재한다. 컴포넌트의 수명은 페이지에 렌더링되기 전 준비 과정에서 시작해 컴포넌트가 페이지에서 사라질 때 끝난다.
라이프사이클 메서드는 컴포넌트를 처음 렌더링할 때 어떤 작업을 처리해야 하거나, 컴포넌트 업데이트 전과 후에 작업을 처리해줘야할 때 등에 사용된다.
❗ 라이프사이클 메서드는 클래스형 컴포넌트에서만 사용할 수 있다. 함수형 컴포넌트에서는 useEffect와 같은 Hook을 통해 비슷한 작업을 처리할 수 있다.
라이프사이클 메서드의 종류는 총 9가지이다. Will 접두사가 붙은 메서드는 어떤 작업을 작동하기 전에 실행되는 메서드, Did 접두사가 붙으면 어떤 작업을 실행한 후에 작동하는 메서드이다. 크게 3가지 카테고리로 나누어 살펴보자.
DOM이 생성되고 웹 브라우저상에 나타나는 것을 마운트(mount)라고 한다.
construtor : 컴포넌트를 새로 만들 때마다 호출되는 클래스 생성자 메서드
constructor(props){...}
컴포넌트 생성자 메서드는 컴포넌트를 만들 때 처음으로 실행된다. 이 메서드에서 초기 state를 정의할 수 있다.
getDerivedStateFromProps : props에 있는 값을 state에 넣을 때 사용하는 메서드
static getDerivedStateFromProps(nextProps, prevState){
//조건에 따라 특정 값 동기화
if(nextProps.value !==prevState.value){
return {value:nextProps.value}
}
return null
}
리액트 버전 16.3 이후에 새로 만들어진 라이프사이클 메서드이다. props로 받아 온 값을 state에 동기화시키는 용도이며, 컴포넌트가 마운트 될 때와 업데이트될 때 호출된다.
render : UI 레더링 메서드
컴포넌트를 그려주기 때문에 가장 중요한 메서드이다. 라이프사이클 메서드 중 유일한 필수 메서드이기도 하다. 이 메서드 안에서 this.props와 this.state에 접근할 수 있으며, react element를 반환한다.
render 메서드가 아닌 곳에서 setState를 사용하거나, DOM에 접근해서도 안된다. DOM 정보를 가져오거나 state에 변화를 줄 때는 componentDidMount에서 처리해야한다.
componentDidMount : 컴포넌트가 웹 브라우저상에 나타난 후 호출하는 메서드
componentDidMount(){...}
컴포넌트를 생성하고 첫 렌더링을 다 마친 후 실행한다. componentDidMount 메서드 안에서 다른 함수를 호출하거나 이벤트 등록, setTimeOut, setInterval, 네트워크 요청과 같은 비동기 작업을 처리할 수 있다.
컴포넌트가 다음과 같은 경우에 업데이트를 한다.
shouldComponentUpdate(nextProps, nextState){...}
this.forceUpdate()
함수를 호출한다면 이 과정을 생략하고 바로 render함수를 호출한다. )this.props
와 this.state
로 접근하고, 새로 설정되는 값은 nextProps
와 nextState
로 접근할 수 있다. getSnapshotBeforeUpdate(prevProps, prevState){
if(prevState.array !== this.state.array){
const {scrollTop, scrollHeight} = this.list;
return {scrollTop, scrollHeight}
}
}
리액트 버전 16.3 이후에 새로 만들어진 라이프사이클 메서드로, 해당 메서드의 반환 값을 componentDidUpdate에서 세 번째 파라미터인 snapshot 값으로 전달 받을 수 있다. 주로 업데이트 직전의 값을 참고할 때 사용한다. (ex.스크롤바 위치 유지)
componentDidUpdate(prevProps, prevState, snapshot){...}
컴포넌트를 DOM에서 제거하는 것을 언마운트라고 한다.
componentWillUnmount(){...}
직접 예제를 통해 확인해보자.
각 라이프사이클 메서드를 실행할 때마다 콘솔 디버거에 기록하고, 부모 컴포넌트로부터 color 값을 props로 받는다. 버튼을 누르면 state.number에 1씩 더해주는 컴포넌트를 만든다.
🧩RandomColorNumber.js
import { Component } from "react";
class RandomColorNumber extends Component{
state = {
number:0,
color:null
}
myRef = null;
constructor(props){
super(props)
console.log('constructor')
}
static getDerivedStateFromProps(nextProps, prevState){
console.log('getDerivedStateFromProps')
if(nextProps.color !==prevState.color){
return {color:nextProps.color}
}
return null;
}
componentDidMount(){
console.log('componentDidMount')
}
shouldComponentUpdate(nextProps, nextState){
console.log('shouldComponentUpdate', nextProps, nextState)
//숫자 마지막 자리가 4이면 리렌더링 되지 않는다.
return nextState.number% 10 !==4
}
componentWillUnmount(){
console.log('componentWillUnmount')
}
handleClick = ()=>{
this.setState({
number : this.state.number+1
})
}
getSnapshotBeforeUpdate(prevProps, prevState){
console.log('getSnapshotBeforeUpdate')
if(prevProps.color !== this.props.color){
return this.myRef.style.color
}
return null
}
componentDidUpdate(prevProps, prevState, snapshot){
console.log('componentDidUpdate')
if(snapshot){
console.log(`업데이트 되기 전 색상 : ${snapshot}`)
}
}
render(){
console.log('render')
const style = {
color:this.props.color
}
return(
<div>
<h1 style = {style} ref={(ref)=> this.myRef=ref}>{this.state.number}</h1>
<p>color: {this.state.color}</p>
<button onClick ={this.handleClick}>더하기</button>
</div>
)
}
}
export default RandomColorNumber;
🧩RandomColor.js
import { Component } from "react";
import RandomColorNumber from "./RandomColorNumber";
function getRandomColor(){
return `#${Math.floor(Math.random()*16777215).toString(16)}`
}
class RandomColor extends Component{
state = {
color:"#000000"
}
handleClick =()=>{
this.setState({
color:getRandomColor()
})
}
render(){
return(
<>
<button onClick = {this.handleClick}>랜덤 색상</button>
<RandomColorNumber color ={this.state.color}/>
</>
)
}
}
export default RandomColor;
실행하면 아래와 같이 확인할 수 있다.
componentDidCatch(error, info){
this.setState({
error:true
})
console.log({error, info})
}
error는 파라미터에 어떤 에러가 발생했는지 알려주며, info 파라미터는 어디에 있는 코드에서 오류가 발생했는지에 대한 정보를 준다. 이 메서드를 사용할 때는 컴포넌트 자신에게 발생하는 에러를 잡아낼 수 없고 자신의 this.props.children으로 전달되는 컴포넌트에서 발생하는 에러만 잡아낼 수 있다.
위의 예제를 기반으로 의도적으로 에러를 만들어 확인해보자. this.props.missing.value
라는 존재하지 않는 props를 추가해준다.
🧩RandomColorNumber.js
class RandomColorNumber extends Component{
state = {
number:0,
color:null
}
myRef = null;
...
render(){
console.log('render')
const style = {
color:this.props.color
}
return(
<div>
//존재하지 않는 props 추가
{this.props.missing.value}
<h1 style = {style} ref={(ref)=> this.myRef=ref}>{this.state.number}</h1>
<p>color: {this.state.color}</p>
<button onClick ={this.handleClick}>더하기</button>
</div>
)
}
}
이와 같이 에러가 나는 것을 확인할 수 있다. 이때 어디서 어떤 에러가 발생했는지 화면에 나타나게 되는데 이것은 우리가 개발 서버를 사용하여 실행했기 때문이다. 오른쪽 상단에 X버튼을 누르면
빈 화면을 보게 된다. 사용자가 웹 서비스를 사용할때 이렇게 흰 화면만 나타나는 것이 아니라 에러가 발생했다고 알려주어야 한다.
🧩ErrorBoundary.js
import { Component } from "react";
class ErrorBoundary extends Component{
state ={
error:false
}
componentDidCatch(error, info){
this.setState({
error:true
})
console.log({error, info})
}
render(){
if(this.state.error)return <div>에러가 발생했다!</div>
return this.props.children
}
}
export default ErrorBoundary
에러가 발생하면 componentDidCatch 메서드가 호출되며 this.state.error
값을 true로 업데이트 해준다. 그리고 render 함수는 this.state.error
이 true 일 때 에러가 발생했다는 문구를 보여준다.
사용법은 아래와 같다.
🧩RandomColor.js
function getRandomColor(){...}
class RandomColor extends Component{
...
render(){
return(
<>
<ErrorBoundary>
<button onClick = {this.handleClick}>랜덤 색상</button>
<RandomColorNumber color ={this.state.color}/>
</ErrorBoundary>
</>
)
}
}
에러를 캐치하려는 컴포넌트를 ErrorBoundary로 감싸준다.