본문 바로가기
FE/React

Lifecycle(feat. DOM)

by ideal_string 2022. 10. 3.

컴포넌트는 각자 라이프사이클을 가지고 있다. 컴포넌트는 생성되고, 업데이트되고, 사라진다. 자세히 얘기해보자. 컴포넌트가 DOM에 끼워질 때 생성된다, 그리고 업데이트되면 DOM에 표시된 컴포넌트가 변한다. DOM에서 컴포넌트가 사라진다면 그 컴포넌트는 역할을 다한 셈이다. 라이프사이클(lifecycle) 나타나고 수정하고 사라지는 모든 것을 말하며, 리액트에서는 이 라이프사이클을 조절할 수 있다. (리액트 공식 문서에서는 생명주기라고 번역되어 있으나 실제 현장에선 라이프사이클이라 표현한다.)

사실 필자는 전에 라이프사이클과 관련 있는 글을 썼다. 바로 useEffect. useEffect가 컴포넌트의 라이프사이클을 조절하는 hook이었다. 함수형 컴포넌트는 라이프사이클을 useEffect로 조절하며, class 컴포넌트일 경우는 조금 다르다. 이번 글은 class 컴포넌트를 이용한 lifecycle을 정리한다.

 

기본 순서

먼저 라이프 사이클은 컴포넌트를 DOM에 위치시키는 등록(mounting), 변경된 값을 지속 반영하는 최신화(updating), 모든 생명주기가 끝났을 때 실행하는 해지(unmouting)까지 3단계로 이뤄진다.

 

class를 이용한 lifecyle 폼

class에서 등록은 componentDidMount(), 최신화는 componentDidUpdate(), 해제는 componentWillUnMount()로 설정할 수 있다. 함수형에서 쓰는 useEffect와 비교하면 componentDidMount()는 useEffect(()=>{}, []), componentDidUpdate()는 useEffect(()=>{}, [값]), componentWillUnMount()는 useEffect(()=>{return()=>{}}, [])과 같다. 기본 폼에 쓴 메서드 외에도 다양한 메서드가 있다.

import { Component } from "react";
import Router from "next/router";

export default class ClassCounterPage extends Component {
  state = {
    count: 0,
  };

  componentDidMount() {
    console.log("그려지고 나서 실행");
  }

  componentDidUpdate() {
    console.log("변경되고 나서 실행");
  }

  componentWillUnmount() {
    console.log("사라질 때 실행");
  }

  onClickCountUp = () => {
    this.setState((prev: { count: number }) => ({ count: prev.count + 1 }));
    // console.log(this.state.count);
  };

  onClickMove() {
    void Router.push("/");
  }

  render() {
    return (
      <>
        <div>{this.state.count}</div>
        <button onClick={this.onClickCountUp}>카운터 올리기</button>
        <button onClick={this.onClickMove}>나가기</button>
      </>
    );
  }
}

등록(Mounting)

컴포넌트 등록 시 아래 메서드가 순차로 호출된다. 주의할 점은 렌더가 된 후 componentDidMount()를 실행한다. 즉, 클래스에 선언된 화면을 그린 후 마지막에 실행된다는 점이다.

  1. constructor()
  2. static getDerivedStateFromProps()
  3. render()
  4. componentDidMount()

최신화(Updating)

컴포넌트가 props, state든, forceUpdate 등 무엇이든 변경된다면 해당 컴포넌트를 리렌더링한다. 그 순서는 다음과 같다.

  1. static getDerivedStateFromProps()
  2. shouldComponentUpdate()
  3. render()
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate()

해제(Unmounting)

마운트 해제는 아주 간단하다. (끝난 마당에 뭐가 더 필요할까 싶은 마음은 다 똑같나보다.)

  1. componentWillUnmount()
※ static getDerivedStateFromProps()
getDerivedStateFromProps()는 단어 그대로 state가 props에 따라 좌지우지될 때 주로 사용하며, 최초 등록할 때, 최신화할 때 모두 render() 직전에 실행한다. 등록 시 state 갱신을 위한 객체를 반환하건, 최신화 시 null을 반환해 아무것도 갱신하지 않을 수 있다.
※ shouldComponentUpdate()
shouldComponentUpdate()는 현재 state 또는 props의 현재 상태 변화를 인지하고 있는 메서드다. 아직 렌더링 전이기 때문에 이 단계에서 return false 할 경우 render()를 멈출 수 있다. 보통 성능 최적화를 위해 사용한다. 단, 렌더링을 방지하는 목적으로만 사용할 경우 추천하지 않는다. 리액트 공식문서에서는 shouldComponentUpdate()의 내용을 직접 작성하기보다 PureComponent를 사용하길 권장한다. PureComponent는 얕은 비교만 하기에, 컴포넌트가 복잡할 경우 forceUpdate()를 사용하는 게 나을 수도 있다.
더불어, shouldComponentUpdate()에서 JSON.stringify() 사용 시 비효율적이고 성능을 떨어뜨릴 수 있다고 공식문서는 경고한다.
※ getSnapshotBeforeUpdate()
getSnapshotBeforeUpdate()은 가장 마지막으로 렌더링 된 결과가 DOM에 반영되기 전에 호출한다. 이 메서드는 componentDidUpdate()의 인자로 전달된다. DOM으로부터 스크롤 위치 등과 같은 정보를 최신화 전에 얻을 수 있기 때문에 채팅 화면의 스크롤 위치를 처리하는 UI를 만들 때 유용할 수 있다.
// 사용 예시
getSnapshotBeforeUpdate(prevProps, prevState) {
    // Are we adding new items to the list?
    // Capture the scroll position so we can adjust scroll later.
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
 }​

메서드 사용 시 주의

등록 시 componentWillMount(), 최신화 시 componentWillUpdate(), componentWillReceiveProps() 메서드는 이제 사용하지 않는다. 자료를 찾다 보면 옛 자료들에서 볼 수 있는데, 버전 17까지만 쓰였으므로 그 이후 버전을 사용한다면 유의해야 한다. 자세한 내용은 공식문서 레거시 생명주기 메서드를 참고한다.

 

한눈에 보는 라이프사이클

출처 : https://www.zerocho.com/category/React/post/579b5ec26958781500ed9955

마무리

useEffect에 이어 class에서 사용한 라이프사이클을 정리했다. 솔직히 처음엔 componentDidMount, componentDidUpdate, componentWillUnmount까진 useEffect와 똑같아서 별거 아닌 듯해보였다가, getDerivedStateFromProps나 shouldComponentUpdate 같은 것을 보고 긴장했다. 그래도 공식 문서와 여러 자료를 천천히 읽어보니 새로운 개념이라기 보다 컴포넌트를 DOM에 인식시키면서 생길 수 있는, 그리고 그 가운데 값을 조절하고 싶은 때 사용하는 메서드라는 것을 알고 나니, 이해하는 데 무리가 없었다. 다만 등록, 최신화, 해제같은 기본 메서드말고 다른 메서드는 어느 때 쓰는 게 적절한 지 알 수 없었다. 아마 실무에 가봐야 알 듯한데,,, 이젠 클래스 컴포넌트보다 함수 컴포넌트를 많이 사용하니, 볼 일이 없을지도 모르겠다.

※ 잘못된 내용이 있을 경우 댓글로 알려주세요. 배우고 익히고 수정하겠습니다:)

 

'FE > React' 카테고리의 다른 글

HOC & HOF  (0) 2022.10.19
전역 상태 관리  (0) 2022.10.09
Class Component & Functional Component  (0) 2022.10.01
useEffect  (1) 2022.09.30
useRef  (0) 2022.09.29