[리액트공부] 7. 라이프사이클 메서드

kkado·2022년 7월 30일
0

리다기

목록 보기
8/16
post-thumbnail

모든 리액트 컴포넌트에는 라이프사이클(수명 주기)가 존재한다.

컴포넌트의 수명은 페이지에 렌더링되기 전 준비과정부터 시작하여 페이지에서 사라질 때 끝난다.

리액트로 프로젝트를 만들다 보면, 어떤 컴포넌트가 렌더링되기 전/후에 특정한 작업을 처리해야 하는 경우가 있다.

이 때는 컴포넌트의 라이프사이클 메서드를 이용한다. (라이프사이클 메서드는 클래스형 컴포넌트에서만 사용할 수 있다.)

라이프사이클 메서드

라이프사이클은 총 3가지로 나뉜다. 마운트, 업데이트, 언마운트가 그것이다.

마운트

DOM이 생성되고 웹 브라우저상에 나타나는 것을 마운트(mount)라고 한다. 이 때 호출되는 메서드는 다음과 같다.

  • constructor : 컴포넌트를 새로 만들 때마다 호출되는 클래스 생성자 메서드. 컴포넌트의 생성자 메서드로 컴포넌트를 만들 때 처음으로 실행. 초기 state를 정할 수 있다.

  • getDerivedStateFromProps : props에 있는 값을 state에 넣을 때 사용하는 메서드. 마운트될 때와 업데이트될 때 호출된다.

  • render : 우리가 준비한 UI를 렌더링하는 메서드

  • componentDidMount : 컴포넌트를 만들고, 렌더링 후에 실행한다. 이 안에서 다른 자바스크립트 라이브러리 또는 프레임워크의 함수를 호출하거나, 이벤트 등록, setTimeout, setInterval 등의 비동기 작업을 처리한다.

업데이트

컴포넌트는 다음 4가지 경우에 업데이트를 한다.

  • props가 바뀔 때
  • state가 바뀔 때
  • 부모 컴포넌트가 리렌더링될 때
  • this.forceUpdate 를 통해 강제로 렌더링을 트리거 할 때

그리고 컴포넌트가 업데이트될 때, 다음 메서드들을 호출한다.

  • getDerivedStateFromProps : props의 변화에 따라 state 값에 변화를 주고 싶을 때 사용한다. 마운트될 때와 업데이트될 때 호출된다.

  • shouldComponentUpdate : props나 state를 변경했을 때 리렌더링 해야 할지 말아야 할지를 결정한다. true/false를 반환하며, false 값을 반환한다면 업데이트가 중지된다.

  • render : 컴포넌트를 리렌더링한다. 컴포넌트의 모양새를 정의한다. 메서드 안에서 this.props와 this.state에 접근할 수 있고, 리액트 요소를 반환한다. 이 메서드 안에서는 이벤트 설정이 아닌 곳에서 setState를 사용해선 안 되며 DOM에 접근해서도 안 된다. DOM 정보를 가져오거나 state에 변화를 줄 때는 componentDidMount에서 처리해야 한다.

  • getSnapshotBeforeUpdate : render에서 만들어진 결과물이 DOM에 반영되기 직전에 호출된다. 업데이트하기 직전의 값을 참고할 일이 있을 때 활용한다.

  • componentDidUpdate : 컴포넌트의 업데이트가 끝난 후 호출하는 메서드

언마운트

마운트의 반대, 즉 컴포넌트를 DOM에서 제거하는 것을 의미한다.

  • componentWillUnmount : 컴포넌트가 웹 브라우저상에서 사라지기 직전에 호출하는 메서드.

기타

  • componentDidCatch : 컴포넌트 렌더링 도중에 에러가 발생했을 때 애플리케이션이 먹통이 되지 않고 오류 UI를 보여줄 수 있게 해 준다.

실습

import React, { Component } from "react";

export default class LifeCycleSample 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);
    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", prevProps, prevState);
    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>
    );
  }
}


App.js에서 props로 색상을 넣어주었다.


처음 랜더링 됐을 때의 콘솔 창 모습

버튼을 한 번 눌렀을 때의 콘솔 창 모습. 업데이트 되었으므로 getDerivedFromProps 함수 부터 차례로 실행되고 있다.


그리고 number가 4일 때는 나눈 나머지가 0이므로 리렌더링을 취소한다. 그래서 shouldComponentUpdate 이후에 실행되어야 하는 함수들이 실행되지 않고 있다.

에러 핸들링

이번에는 에러를 일부러 발생시키고 그것을 핸들링하고자 한다.

  render() {
    console.log("render");
    const style = {
      color: this.props.color,
    };

    return (
      <div>
        {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>
    );
  }

위의 코드에서 render 함수를 수정하였다. {this.props.missing.value} 라는, 존재하지 않는 props의 value를 렌더링하고자 하니 당연히 오류가 발생할 것이다.

오류가 발생하였다. 이제 에러를 잡아주는 ErrorBoundary 컴포넌트를 생성한다.

export default 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;
  }
}

componentDidCatch 메서드를 이용해서, 에러가 발생했다면 error state를 true로 만들고 어떤 에러인지 콘솔에 출력하게끔 하였다.

        <ErrorBoundary>
          <LifeCycleSample color="blue" />
        </ErrorBoundary>

그리고 App.js에서 에러가 발생하는 컴포넌트인 LifeCycleSample을 ErrorBoundary로 감싸 준다.


에러가 발생했을 때의 결과인 <div>에러가 발생했습니다.</div>가 잘 렌더링되었고, componentDidCatch 메서드에 의해 에러의 정보 또한 콘솔 창에 잘 출력되었다. 'cannot read properties of undefined' 라고 한다.


정리

라이프사이클 메서드는 컴포넌트의 상태에 변화가 있을 때마다(마운트, 언마운트, 업데이트) 실행하는 메서드이다. 이 메서드들은 서드 파티 라이브러리를 사용하거나, DOM을 직접 건드려야 하는 상황에서 유용하다. 추가로 컴포넌트 업데이트의 성능을 개선할 때에는 shouldComponentUpdate가 중요하게 사용된다.

profile
울면안돼 쫄면안돼 냉면됩니다

0개의 댓글