Study/React

7. Lifecycle method(라이프사이클 메서드)

delay100 2022. 7. 14. 15:39
728x90
반응형
SMALL

안녕하세요! delay100입니다. 이번 포스팅에서는 컴포넌트의 라이프사이클 메서드에 대해 공부해봅시다.

대부분의 설명은 주석으로 달아놓았으니 코드에 대한 설명은 주석을 확인해주세요!

 리액트를 다루는 기술, 개정판의 7 내용을 다루고 있습니다.

프로젝트 실행 방법에 대한 설명은 여기에 있습니다.

이번 포스팅의 Github 링크

https://github.com/delay-100/study-react/tree/main/ch7/hello-react
 

GitHub - delay-100/study-react

Contribute to delay-100/study-react development by creating an account on GitHub.

github.com

 


1. 라이프사이클?

 

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

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

용어

마운트(mount)
: 페이지에 컴포넌트가 나타남(DOM이 생성되고 웹 브라우저상에 나타나는 것)
업데이트: 컴포넌트 정보를 업데이트(리렌더링)
=> 컴포넌트를 업데이트하는 경우는 총 4가지가 있습니다.
1. props가 바뀔 때, 2. state가 바뀔 때, 3. 부모 컴포넌트가 리렌더링될 떄,
4. this.forceUpdate로 강제로 렌더링을 트리거할 때

언마운트: 페이지에서 컴포넌트가 사라짐

 


 

2. 라이프사이클 메서드 

 

라이프사이클 메서드는 클래스형 컴포넌트에서만 사용할 수 있습니다. 함수 컴포넌트에서는 사용이 불가능한데, 그 대신 Hooks의 기능을 사용하여 비슷한 작업을 처리할 수 있습니다.(Hooks는 다음 포스팅에서 다룹니다.)

 

컴포넌트의 라이프사이클 메서드 흐름

출처: https://thebook.io/080203/ch07/04/

 

 

2-1. 예시

 

모든 설명은 주석에 달아두었습니다!

  • 라이프사이클 메서드를 사용 예시 - src/LifeCycleSample.js
import { Component } from "react";

// 각 lifecycle을 실행할 때마다 콘솔 디버거에 기록하고, 부모 컴포넌트에서 props로 색상을 받아 버튼을 누르면 state.number 값을 1씩 더함
class LifeCycleSample extends Component {
  state = {
    number: 0,
    color: null,
  };

  myRef = null; // ref를 설정할 부분

  // 컴포넌트에 state를 설정할 때는 다음과 같이 constructor 메서드를 작성하여 설정함
  // 컴포넌트 생성자 메서드
  constructor(props) {
    // 클래스형 컴포넌트에서 constructor를 작성할 때는 반드시 이 문장을 호출해야 함
    // 함수 호출 시 현재 클래스형 컴포넌트가 상속받고 있는 리액트의 Component 클래스가 지닌 생성자 함수를 호출해줌
    super(props);
    console.log("constructor");
  }

  /* 아래부터 업데이트할 때 호출하는 메서드를 순서대로 나열함 */

  // getDerivedStateFromProps 메서드 - props로 받아 온 값을 state에 동기화시키는 용도로 사용하며, 컴포넌트가 마운트될 때와 업데이트 될 때 호출됨
  static getDerivedStateFromProps(nextProps, prevState) {
    console.log("getDerivedStateFromProps");
    if (nextProps.color !== prevState.color) {
      // 조건에 따라 특정 값 동기화
      return { color: nextProps.color }; // 부모에게 받은 color 값을 state에 동기화 하고 있음
    }
    return null; // state를 변경할 필요가 없다면 null을 반환
  }

  // ComponentDidMount 메서드 - 컴포넌트를 만들고, 첫 렌더링을 다 마친 후 실행
  // 이 안에서 다른 자바스크립트 라이브러리 또는 프레임워크의 함수를 호출하거나 이벤트 등록, setTimeout, setInterval, 네트워크 요청 같은 비동기 작업을 처리하면 됨
  componentDidMount() {
    console.log("componentDidMount");
  }

  // shouldComponentUpdate 메서드 - props 또는 state를 변경했을 때, 리렌더링을 시작할지 여부를 지정하는 메서드
  // 이 메서드에서는 반드시 true값 또는 false값을 반환해야함
  shouldComponentUpdate(nextProps, nextState) {
    console.log("shouldComponentUpdate", nextProps, nextState);
    // 숫자의 마지막 자리가 4면 리렌더링하지 않습니다.
    return nextState.number % 10 !== 4; // state.number 값의 마지막 자리 수가 4이면(예, 4, 14, 24) 리렌더링을 취소하도록 설정
  }

  // componentWillUnmount 메서드 - 컴포넌트를 DOM에서 제거할 때 실행
  // componentDidMount에서 등록한 이벤트, 타이머, 직접 생성한 DOM이 있다면 여기서 제거 작업을 해야 함
  componentWillUnmount() {
    console.log("componentWillUnmount");
  }

  handleClick = () => {
    this.setState({
      number: this.state.number + 1,
    });
  };

  // getSnapshotBeforeUpdate 메서드 - render에서 만들어진 결과물이 브라우저에 실제로 반영되기 직전에 호출됨
  // 이 메서드의 반환 값은 componentDidUpdate에서 세 번째 파라미터인 snapshot 값으로 전달받을 수 있음 -> 주로 업데이트하기 직전의 값을 참고할 일이 있을 때 활용됨
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("getSnapshotBeforeUpdate");
    if (prevProps.color !== this.props.color) {
      // DOM에 변화가 일어나기 직전의 색상 속성을 snapshot 값으로 반환하여 이것을 componentDidUpdate에서 조회할 수 있게함
      return this.myRef.style.color;
    }
    return null;
  }

  // componentDidUpdate 메서드 - 리렌더링을 완료한 후 실행함
  // 업데이트가 끝난 직후이므로, DOM 관련 처리를 해도 됨.
  // 여기서는 prevProps 또는 prevState를 사용하여 컴포넌트가 이전에 가졌던 데이터에 접근 가능
  // getSnapshotBeforeUpdate에서 반환한 값이 있다면 여기서 snapshot 값을 전달받을 수 있음
  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)}>
          {/* 부모 컴포넌트에서 props로 색상을 받음*/}
          {this.state.number}
        </h1>
        <p>color: {this.state.color}</p>
        <button onClick={this.handleClick}>더하기</button>
      </div>
    );
  }
}

export default LifeCycleSample;
  • 라이프사이클 메서드를 사용 예시 - src/App.js
import { Component } from "react";
import LifeCycleSample from "./LifeCycleSample";

// 랜덤 색상을 생성합니다. (state의 color 값을 랜덤 색상으로 설정)
// 16777215를 hex로 표현하면 ffffff가 되므로 해당 코드는 000000부터 ffffff 값을 반환합니다.
function getRandomColor() {
  return "#" + Math.floor(Math.random() * 16777215).toString(16);
}

class App extends Component {
  state = {
    color: "#000000",
  };

  handleClick = () => {
    this.setState({
      color: getRandomColor(),
    });
  };

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>랜덤 색상</button>
        {/* 버튼을 렌더링하고, 누를 때마다 handleClick 메서드가 호출되게 이벤트를 설정하며, */}
        <LifeCycleSample color={this.state.color} />
        {/* 불러온 LifeCycleSample 컴포넌트에 color 값을 props로 설정함*/}
      </div>
    );
  }
}

export default App;

라이프사이클 메서드를 사용 예시 실행 결과

 


2-2. 에러 발생 알림

 

2-1의 LifeCycleSample 컴포넌트의 render 함수에서 의도적으로 에러를 발생시켜보겠습니다.

render 함수에서 에러1. 주로 존재하지 않는 함수를 사용하려고 하거나, 2. 존재하지 않는 객체의 값을 조회하려고 할 때 발생합니다.

 

아래의 예시는 2. 존재하지 않는 개체의 값을 조회했을 때, 사용자에게 현재 에러가 발생했다고 보여주겠습니다.

 

  • 라이프사이클 메서드 (일부러)에러 발생 예시 - src/LifeCycleSampleError.js
{this.props.missing.value}

라이프사이클 메서드를 사용 예시 - src/LifeCycleSample.js에서 render 부분에 위의 코드만 추가했습니다.

import { Component } from "react";

// 각 lifecycle을 실행할 때마다 콘솔 디버거에 기록하고, 부모 컴포넌트에서 props로 색상을 받아 버튼을 누르면 state.number 값을 1씩 더함
class LifeCycleSampleError extends Component {
  state = {
    number: 0,
    color: null,
  };

  myRef = null; // ref를 설정할 부분

  // 컴포넌트에 state를 설정할 때는 다음과 같이 constructor 메서드를 작성하여 설정함
  // 컴포넌트 생성자 메서드
  constructor(props) {
    // 클래스형 컴포넌트에서 constructor를 작성할 때는 반드시 이 문장을 호출해야 함
    // 함수 호출 시 현재 클래스형 컴포넌트가 상속받고 있는 리액트의 Component 클래스가 지닌 생성자 함수를 호출해줌
    super(props);
    console.log("constructor");
  }

  /* 아래부터 업데이트할 때 호출하는 메서드를 순서대로 나열함 */

  // getDerivedStateFromProps 메서드 - props로 받아 온 값을 state에 동기화시키는 용도로 사용하며, 컴포넌트가 마운트될 때와 업데이트 될 때 호출됨
  static getDerivedStateFromProps(nextProps, prevState) {
    console.log("getDerivedStateFromProps");
    if (nextProps.color !== prevState.color) {
      // 조건에 따라 특정 값 동기화
      return { color: nextProps.color }; // 부모에게 받은 color 값을 state에 동기화 하고 있음
    }
    return null; // state를 변경할 필요가 없다면 null을 반환
  }

  // ComponentDidMount 메서드 - 컴포넌트를 만들고, 첫 렌더링을 다 마친 후 실행
  // 이 안에서 다른 자바스크립트 라이브러리 또는 프레임워크의 함수를 호출하거나 이벤트 등록, setTimeout, setInterval, 네트워크 요청 같은 비동기 작업을 처리하면 됨
  componentDidMount() {
    console.log("componentDidMount");
  }

  // shouldComponentUpdate 메서드 - props 또는 state를 변경했을 때, 리렌더링을 시작할지 여부를 지정하는 메서드
  // 이 메서드에서는 반드시 true값 또는 false값을 반환해야함
  shouldComponentUpdate(nextProps, nextState) {
    console.log("shouldComponentUpdate", nextProps, nextState);
    // 숫자의 마지막 자리가 4면 리렌더링하지 않습니다.
    return nextState.number % 10 !== 4; // state.number 값의 마지막 자리 수가 4이면(예, 4, 14, 24) 리렌더링을 취소하도록 설정
  }

  // componentWillUnmount 메서드 - 컴포넌트를 DOM에서 제거할 때 실행
  // componentDidMount에서 등록한 이벤트, 타이머, 직접 생성한 DOM이 있다면 여기서 제거 작업을 해야 함
  componentWillUnmount() {
    console.log("componentWillUnmount");
  }

  handleClick = () => {
    this.setState({
      number: this.state.number + 1,
    });
  };

  // getSnapshotBeforeUpdate 메서드 - render에서 만들어진 결과물이 브라우저에 실제로 반영되기 직전에 호출됨
  // 이 메서드의 반환 값은 componentDidUpdate에서 세 번째 파라미터인 snapshot 값으로 전달받을 수 있음 -> 주로 업데이트하기 직전의 값을 참고할 일이 있을 때 활용됨
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("getSnapshotBeforeUpdate");
    if (prevProps.color !== this.props.color) {
      // DOM에 변화가 일어나기 직전의 색상 속성을 snapshot 값으로 반환하여 이것을 componentDidUpdate에서 조회할 수 있게함
      return this.myRef.style.color;
    }
    return null;
  }

  // componentDidUpdate 메서드 - 리렌더링을 완료한 후 실행함
  // 업데이트가 끝난 직후이므로, DOM 관련 처리를 해도 됨.
  // 여기서는 prevProps 또는 prevState를 사용하여 컴포넌트가 이전에 가졌던 데이터에 접근 가능
  // getSnapshotBeforeUpdate에서 반환한 값이 있다면 여기서 snapshot 값을 전달받을 수 있음
  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>
        {this.props.missing.value}
        {/* 존재하지 않는 props인 missing 객체의 value를 조회해서 렌더링하려고 함 -> 당연히 브라우저에서 에러 발생*/}
        <h1 style={style} ref={(ref) => (this.myRef = ref)}>
          {/* 부모 컴포넌트에서 props로 색상을 받음*/}
          {this.state.number}
        </h1>
        <p>color: {this.state.color}</p>
        <button onClick={this.handleClick}>더하기</button>
      </div>
    );
  }
}

export default LifeCycleSampleError;
  • 라이프사이클 메서드 (일부러)에러 발생 예시 - src/ErrorBoundary.js
import { Component } from "react";

class ErrorBoundary extends Component {
  state = {
    error: false,
  };

  // 에러 발생 시 componentDidCatch 메서드가 호출됨 - 이 메서드는 this.state.error 값을 true로 업데이트해줌
  componentDidCatch(error, info) {
    this.setState({
      error: true,
    });
    console.log({ error, info });
  }
  render() {
    // this.state.error 값이 true라면 에러가 발생했음을 알려줌
    if (this.state.error) return <div>에러가 발생했습니다!</div>;
    return this.props.children;
  }
}

export default ErrorBoundary;
  • 라이프사이클 메서드 (일부러)에러 발생 예시 - src/App.js
import { Component } from "react";
// import LifeCycleSample from "./LifeCycleSample";
import LifeCycleSampleError from "./LifeCycleSampleError";
import ErrorBoundary from "./ErrorBoundary";

// 랜덤 색상을 생성합니다. (state의 color 값을 랜덤 색상으로 설정)
// 16777215를 hex로 표현하면 ffffff가 되므로 해당 코드는 000000부터 ffffff 값을 반환합니다.
function getRandomColor() {
  return "#" + Math.floor(Math.random() * 16777215).toString(16);
}

class App extends Component {
  state = {
    color: "#000000",
  };

  handleClick = () => {
    this.setState({
      color: getRandomColor(),
    });
  };

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>랜덤 색상</button>
        {/* 버튼을 렌더링하고, 누를 때마다 handleClick 메서드가 호출되게 이벤트를 설정하며, */}
        <ErrorBoundary>
          {/* <LifeCycleSample color={this.state.color} /> */}
          <LifeCycleSampleError color={this.state.color} />
          {/* 불러온 LifeCycleSample 컴포넌트에 color 값을 props로 설정함*/}
        </ErrorBoundary>
      </div>
    );
  }
}

export default App;

라이프사이클 메서드 (일부러)에러 발생 예시 실행 결과

 


읽어주셔서 감사합니다. 잘못된 정보는 댓글로 알려주세요! 

728x90
반응형
LIST