상세 컨텐츠

본문 제목

[리엑트 스타터1] 7장. 컴포넌트의 라이프 사이클 메서드

22-23/22-23 리액트 스타터 1

by 2jo 2022. 11. 17. 10:00

본문

728x90

 

 

 


 

 

이번 7장에서는 컴포넌트의 라이프사이클 메서드에 대해서 알아보고자 합니다.

모든 리액트 컴포넌트에는 라이프사이클-수명 주기-가 존재하는데,

이는 컴포넌트 상태의 변화가 있을 때부터 실행하는 함수로서,

컴포넌트의 수명은 페이지 렌더링 전부터, 페이지의 삭제까지입니다.

그런데, 리엑트 프로젝트를 진행하다 보면, 이러한 컴포넌트의 수명주기에 개입해 조작해야 하는 경우가 생길 수 있습니다. 가령 컴포넌트의 업데이트 전후에 추가 작업 처리를 만들거나, 특정 업데이트를 방지하는 경우처럼요.

이러한 상황을 위해, 컴포넌트의 라이프사이클 메서드가 이용됩니다.

라이프사이클 메서드의 특징 중 하나는, 함수형 컴포넌트에는 사용 불가능하고, 클래스형 컴포넌트에서만 사용할 수 있다는 점입니다.

 

7.1 라이프사이클 메서드의 이해

라이프사이클 메서드는 총 9개의 종류가 있습니다.

그중, 접두사 두 가지의 특징은 아래와 같습니다.

  • WIll + : 작업 작동 전에 실행
  • DID + : 작업 작동 후에 실행

라이프사이클은 총 세가지 카테고리로 나눌 수 있습니다.

마운트
업데이트
언마운트
정의
DOM이 생성되고 웹 브라우저 상에 나타나는 것
props/state의 변화, 혹은 부모 컴포넌트의 리렌더링, 또는 this.forceUpdate로 인한 강제 리렌더링 시 업데이트가 일어남
마운트의 반대과정이다. 컴포넌트를 DOM에서 제거하는 것
호출 메서드 도식화
아래의 매서드를 순서대로 실행한다
메서드
constructor
getDerivedStateFromProps
componentWillUnmount
getDerivedStateFromProps
shouldComponentUpdate
render
render
componentDidMount
getSnapshotBeforeUpdate
componentDidUpdate

7.2 라이프사이클 메서드 살펴보기

  • render() { ... }
    • 컴포넌트의 모양세 정의
    • 컴포넌트에서 가장 중요한 메서드
    • 라이프사이클 메서드의 유일한 필수 메서드
    • render내부에서 this.props와 this.state에 접근 가능.
    • react 요소 (tag/component/false/null 등) 반환
    • DOM 접근 X
      • componentDidMount 에서 처리
    • 내부의 이벤트 설정 외의 곳에서 setState 사용 X
      • componentDidMount 에서 처리

  • contructor(props) { ...}
    • 생성자 메서드
    • 컴포넌트 생성시 처음 실행
    • 초기 state 값 설정

  • getDerivedStateFromProps
    • react v.16.3 이후 새로 만들어진 메서드
    • props로 받은 값을 state에 동기화
    • 컴포넌트 마운트 시 호출
    • 컴포넌트 업데이트 시 호출
static getDerivedStateFromProps(nextProps, prevState) { 
     if(nextProps.value != = prevState.value) {               // 조건에 따라 특정 값 동기화 
          return { value: nextProps.value }; 
     } 
     return null;                                            // state를 변경할 필요가 없다면 null을 반환 
}

 

  • componentDidMount () { ... }
    • 컴포넌트 생성과 첫 랜더링 이후 실행
    • 타 자바스크립트 라이브러리/프레임워크 함수 호출 작업 처리
    • 비동기 작업 (이벤트 등록, setTimeout, setInterval, 네트워크 요청 등) 처리

 

  • shouldComponentUpdate (nextProps, nextState) { ... }
    • props / state변경시 리렌더링 시작 여부 지정 메서드
    • 반드시 true / false 값 반환
      • 개발자의 정의 부재시, 디폴트값인 true로 반환.
      • false 정의 시, 업데이트 과정 중지. 리렌더링 과방지
    • props 접근
      • 현재일 경우, this.props
      • 새로 설정될 값일 경우, nextProps
    • state 접근
      • 현재일 경우, this.state
      • 새로 설정될 값일 경우, nextState

 

  • getSnapshotBeforeUpdate
    • 리엑트 v.16.3 이후 만든 메서드
    • render에서 만든 결과물이 브라우저에 실제 반영 되기 직전에 호출.
    • snapshor으로 반환 값 전달 받음
    • 업데이트 직전 값을 참고 시 활용

static getSnapshotBeforeUpdate(prevProps, prevState) { 
     if(prevProps.array != = this.state.arrray) { // 조건에 따라 특정 값 동기화 
          const { scrollTop, scrollHeight } = this.list 
          return { scrollTop, scrollHeight };
     }
}
 

 

  • componentDidUpdate(prevProps, prevState, snapshot) { ... }
    • 리렌더링 완료 후 실행.
    • DOM 처리 가능.
    • prevProps/prevState로 컴포넌트 이전 데이터에 접근 가능
    • getSnapshotBeforeUpdate 반환값 O -> 여기서 snapshot 값 전달받음

  • componentWillUnmount() { ... }
    • 컴포넌트를 DOM에서 제거할 때 실행.
    • componentDidMount에서 등록한 이벤트, 타이머, 직접 생성 DOM 이 있으면 여기에서 제거.

  • componentDidCatch
    • 리엑트 v.16. 이후 만든 메서드
    • 렌더링 도중 에러 발생 시, 애플리케이션이 오류 UI를 보여줄 수 있게 함
    • 컴포넌트 자신의 에러 확인x
    • this.props.children으로 전달되는 컴포넌트의 에러 확인O

componentDidCatch(error, info) { //error = 파라미터에게 발생 에러 알려줌. info = 코드 위치 알려줌 
     this.setState({ 
          error: true 
     }); 
     console.log({error, info });
}
 }

7.3 라이프사이클 메서드 사용하기

src 디렉터리에 LifeCycleSample 이라는 컴포넌트를 생성하면, 아래와 같습니다.

 

// LifeCycleSample.js

import { Component } from 'react1;
class LifeCycleSample extends Component {
     state = {
          number: 0,
          color: null,
     }
     myRef = null; // ref를 설정할 부분
     constructor(props) {
          super(props);
          console.log( constructor');
     }
     static getDerivedStateFromProps(nextProps, prevState) {   
       // getDerivedStateFromProps ; 부모에게 받은 color를 state에 동기화 
          console.log('getDeriv은dStateFromProps');
          if(nextProps.color !== prevState.color) {
               return { color: nextProps.color };
          }     
          return null;
     }
     componentDidMount() {
          console.log('componentDidMount*);
     }
     shouldComponentUpdate(nextProps, nextState) {
          console.log('shouldComponentUpdate', nextProps, nextState);
          // shouldComponentUpdate : 숫자의 마지막 자리가 4면 리렌더링하지 않습니다. 4, 14, 24,,,
          return nextState.number % 10 !== 4; 
          }
          componentWillUnmo니nt() {
               console.log(,componentWillUnmount,);
          }
          handleClick =()=>{
               this.setState({
                    number: this.state.number + 1
               });
          }
          getSnapshotBeforeUpdate(prevPropsz prevState) {   //DOM 변화 일어나기 직전 색상 속성을 snapshot으로 반환
               console.log('getSnapshotBeforeUpdate'); 
               if(prevProps.color !== this.props.color) {
                    return this.myRef.style.color;
               }
               return null;
          }
          componentDidUpdate(prevProps, prevState, snapshot) {     // 반환값 조회 
               console.log(*componentDidUpdate1z 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>
                }
          }
}
export default LifeCycleSample;

 

 

위의 코드는, 콘솔 디버거에 라이프사이클 메서드의 실행 결과를 기록한다는 점과

버튼을 누르면 state.number 값을 1씩 더한다는 특징이 있습니다.

보다 자세한 내용은 코드 내 주석 참고를 바랍니다.

다음으로, 기존의 app.js 파일을 아래와 같이 수정하면 다음과 같습니다.

 

// app.js

import { Component } from 'react';
import LifeCycleSample from ./LifeCycleSample';
     
function getRandomColor() {
 // getRandomColor : state의 color 값을 랜덤으로 생성. hex(000000~ffffff)
      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〉
                        <LifeCycleSample color={this. state.color}/>
                  </div>
            ); 
      }
}

export default App;

이는 버튼을 클릭하면, 랜덤 색상을 보여주는 코드를 의미합니다.

위와 같은 코드에서는 어떨 때 에러가 발생할까요?

대체로, 존재하지 않는 함수를 사용하려는 시도-호출이나,

존재하지 않는 - 선언되지 않은 객체 값을 조회하려 할 때.

이 두 가지 경우입니다.

후자의 사례를 관찰하기 위해, render() 함수에 대해서 아래와 같은 코드를 <div> 사이에 작성해봅시다.

{this.props.missing.value}

이와 같은 잘못된 코드를 실행하면, 존재하지 않는 missing 객체의 value를 조회하려 하기에,

undefined 에러가 실행되고, 또는 빈 페이지를 브라우저 탭에서 볼 수 있을 것입니다.

에러 페이지가 나올 때, 이 페이지가 에러임을 나타내 주는 페이지를 아래와 같은 코드로 작성해볼 수 있습니다.

 
//ErrorBoundary.js

import React, { 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;

 

이렇게 작성한 ErrorBoundary.js 코드를 반영하기 위해서 App.js 코드를 아래와 같이 수정할 수 있습니다.

//App.js

import React, { Component } from 'react';
import Lifecyclesample from './Lifecyclesample';
import ErrorBoundary from './ErrorBoundary';

// 랜덤 색상을 생성합니다.
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>
               <ErrorBoundary>
                    <LifeCycleSample color={this.state.color} />
               </ErrorBoundary>
          </div>
          );
     }
}

export default App;

7.4 정리

위에서 설명한 바와 같이, 컴포넌트 라이프사이클은 3개의 단계로 나눌 수 있습니다.

마운트, 업데이트, 언마운트입니다.

위의 코드들을 정리해, 메서드의 흐름을 도식화한다면 아래의 사진과 같습니다.

출처: 리엑트를 다루는 기술 (https://product.kyobobook.co.kr/detail/S000001792882)


 

 

* 퀴즈!~!

문제
1
컴포넌트 상태에 변화가 있을 때마다 실행하는 메서드는?
라이프사이클 메서드
2
컴포넌트 라이프사이클을 크게 3개로 분류하면?
마운트, 업데이트, 언마운트
3
shouldComponentUpdate의 특징을 말한다면?
컴포넌트의 업데이트 성능을 개선할 때 중요하게 쓰인다
4
존재하지 않는 함수를 호출할 때 발생하는 상황은?
에러, 오류
5
존재하지 않는 컴포넌트의 객체를 부를 때 발생하는 상황은?
에러, 오류
6
에러가 발생할 때, 사용자에게 보여줄 파일을 작성한다면, 이를 기존의 App.js파일에 연결하는 방법은 무엇일까? (코드 작성)
import문을 이용해 작성한 js파일을 호출한다.
태그를 이용해 render() 함수에서 이를 부른다.
7
라이프사이클 메서드에서 유일한 필수 메서드는 무엇일까?
render()
8
마운트를 설명한다면?
DOM이 생성되고 웹 브라우저 상에 나타나는 것
9
업데이트를 간략히 설명한다면?
props/state의 변화, 혹은 부모 컴포넌트의 리렌더링, 또는 this.forceUpdate로 인한 강제 리렌더링 시 업데이트가 일어남
10
언마운트를 설명한다면?
마운트의 반대과정이다. 컴포넌트를 DOM에서 제거하는 것

 


 

Corner React1
Editor: 라마

728x90

관련글 더보기