[react] 리액트 컴포넌트 생명주기_lifecycle method 알아보기

호박쿵야·2021년 12월 6일
0

react

목록 보기
5/7

모든 리액트 컴포넌트에는 라이프사이클이 존재한다. 컴포넌트의 수명은 페이지에 렌더링되기 전 준비 과정에서 시작해 컴포넌트가 페이지에서 사라질 때 끝난다.
라이프사이클 메서드는 컴포넌트를 처음 렌더링할 때 어떤 작업을 처리해야 하거나, 컴포넌트 업데이트 전과 후에 작업을 처리해줘야할 때 등에 사용된다.

❗ 라이프사이클 메서드는 클래스형 컴포넌트에서만 사용할 수 있다. 함수형 컴포넌트에서는 useEffect와 같은 Hook을 통해 비슷한 작업을 처리할 수 있다.

react Lifecycle Method

lifecycle method의 종류

라이프사이클 메서드의 종류는 총 9가지이다. Will 접두사가 붙은 메서드는 어떤 작업을 작동하기 에 실행되는 메서드, Did 접두사가 붙으면 어떤 작업을 실행한 에 작동하는 메서드이다. 크게 3가지 카테고리로 나누어 살펴보자.

Mount (마운트)

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, 네트워크 요청과 같은 비동기 작업을 처리할 수 있다.

Update (업데이트)

컴포넌트가 다음과 같은 경우에 업데이트를 한다.

  1. Props가 바뀔 때
  2. state가 바뀔 때
  3. 부모 컴포넌트가 리렌더링될 때
  4. this.forceUpdate로 강제 렌더링을 트리거할 때
  • shouldComponentUpdate : props 또는 state를 변경했을 때, 리렌더링을 시작할지 여부를 지정하는 메서드
    shouldComponentUpdate(nextProps, nextState){...}
    shouldComponentUpdate 메서드는 반드시 true 또는 false 값을 반환해야한다. 컴포넌트를 만들 때 이 메서드를 따로 생성하지 않으면 기본적으로 true를 반환한다.
    ( 만약 특정 함수에서 this.forceUpdate() 함수를 호출한다면 이 과정을 생략하고 바로 render함수를 호출한다. )
    메서드 안에서 현재 props와 state는 this.propsthis.state로 접근하고, 새로 설정되는 값은 nextPropsnextState로 접근할 수 있다.
    👉 프로젝트 성능 최적화를 할 때 상황에 맞는 알고리즘을 작성하여 리렌더링을 방지할 수 있다.

  • getSnapshotBeforeUpdate : render에서 만들어진 결과물이 브라우저에 실제로 반영되기 직전에 호출된다.
    getSnapshotBeforeUpdate(prevProps, prevState){
      if(prevState.array !== this.state.array){
        const {scrollTop, scrollHeight} = this.list;
        return {scrollTop, scrollHeight}
      }
    }

리액트 버전 16.3 이후에 새로 만들어진 라이프사이클 메서드로, 해당 메서드의 반환 값을 componentDidUpdate에서 세 번째 파라미터인 snapshot 값으로 전달 받을 수 있다. 주로 업데이트 직전의 값을 참고할 때 사용한다. (ex.스크롤바 위치 유지)

  • componentDidUpdate : 리렌더링을 완료한 후 실행되는 메서드
    componentDidUpdate(prevProps, prevState, snapshot){...}
    prevState, prevProps를 사용하여 컴포넌트가 이전에 가졌던 데이터에 접근할 수 있다. getSnapshotBeforeUpdate에서 반환한 값이 있다면 snapshot 값을 전달받을 수 있다.

Unmount (언마운트)

컴포넌트를 DOM에서 제거하는 것을 언마운트라고 한다.

  • componentWillUnmount : 컴포넌트를 DOM에서 제거할 때 실행하는 메서드
    componentWillUnmount(){...}
    componentDidMount에서 등록한 이벤트, 타이머, 직접 생성한 DOM 이 있다면 이 메서드 안에서 제거 작업을 해야한다.

직접 예제를 통해 확인해보자.
각 라이프사이클 메서드를 실행할 때마다 콘솔 디버거에 기록하고, 부모 컴포넌트로부터 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 : 컴포넌트 렌더링 도중 에러가 발생했을 때 오류 UI를 보여줄 수 있게 한다.
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로 감싸준다.

0개의 댓글