[개발일지] 22년 28주차 - Life Cycle

FeRo 페로·2022년 7월 17일
3

28주차

이번 주는 리액트를 조금 더 배워보는 과정이었다. 그 과정에서 리덕스와 firebase를 이용한 전역 상태 관리를 해보았다. 곰곰히 생각해보면 아직은 컴포넌트 설계를 최적으로 했는가, State를 적절하게 생성하고 관리했는가 하는 부분에서는 만족스럽지 못하다. 더불어 아직은 리액트에서 어떤 공부를 하면서 약점을 보완해 나가야 하는지에 대한 기준이 머리에서 명확하게 확립되지 않았다. 아직은 2주 밖에 안되니까 당연한 것일지도 모르지만 조금은 조급함을 가지는 것이 좋을 것 같다는 생각을 하게 된 한 주였다.

이번 주 강의에서 라이프 사이클에 대한 부분을 배웠는데 이 부분에 대해서 글을 쓰면서 한 번 더 정리해 보려고 한다.

Life Cycle

리액트를 하면 굉장히 자주 듣는 개념이 라이프 사이클이라는 개념이다. 사실 이 용어가 생소하진 않다. 넓게 보면 컴퓨터의 모든 자원은 라이프 사이클을 가지기 때문이다. 자바스크립트에서의 함수를 예로 들어보겠다.

const testFunction = () => {
  const variable = "someThing's cycle";
  console.log(variable);
}
testFunction(); // someThing's cycle
  1. testFunction은 식별자 해결 단계를 지나 실행 단계에서 초기화가 되고, 그 과정에서 function object가 생성되어 할당되고 그 안에 scope가 [[Scope]]안에 할당된다.
  2. 이후 호출이 되면 엔진은 함수 안으로 들어가서 실행 콘텍스트를 초기화 하게 된다. 이때 Scope는 외부 렉시컬 환경 참조에 할당된다.
  3. 함수 안에서 식별자 해결을 하고 함수를 실행하게 되는데 이때 console.log를 출력해주며 함수의 실행이 끝난다.

이게 testFunction이라는 함수의 라이프 사이클이다. 리액트에도 Life Cycle이 있다. 컴포넌트가 브라우저에 나타났다가 업데이트 했다가 사라지는 흐름을 말한다. 이 흐름은 클래스 컴포넌트와 함수 컴포넌트가 다르니 따로 살펴보도록 하자.

클래스 컴포넌트

클래스 컴포넌트는 특정 라이프 사이클 단계에서 호출할 수 있는 메소드들이 있다. 이어지는 세 가지 상황을 기준으로 어떤 상황에서 어떤 메소드가 호출되는지 살펴보자.

마운트될 때

constructor실행 👉 props와 state 선언 👉 render메소드 실행 👉 ref 할당 👉 componentDidMount메소드 호출

가장 먼저 constructor(State, Props)와 메소드가 선언되고 최초 렌더링 메소드가 실행되면서 ref가 초기화 된다. 이때 실행되는 메소드가 componentDidMount이다. 시맨틱 그대로 컴포넌트가 첫 마운트가 된 직후에 호출되는 메소드이다. 여기서는 보통 컴포넌트에서 필요로 하는 데이터를 비동기로 요청하거나 외부 라이브러리를 연동하는 등의 과정을 설정해둔다.

업데이트 할 때

shouldComponentUpdate메소드 호출 👉 (shouldComponentUpdate가 true이면 ) componentDidUpdate메소드 호출

업데이트는 기본적으로 setState가 발생하거나 props가 바뀔 때 진행된다. 하지만 shouldComponentUpdate메소드를 사용해서 특정 조건에만 Update가 진행되게 해줄 수 있다. 시맨틱 그대로 업데이트할 건지 한 번 검토해 주는 것이다. 그래서 여기서 조건을 충족하면(true) 다음 단계로 componentDidUpdate메소드가 호출된다.

언마운트 할 때

componentWillUnmount메소드 호출

호출되는 메소드가 하나인데 이름도 Will이 들어간다. 시맨틱적으로 보면 다른 경우의 메소드들은 '했는지'를 확인하는 반면에 이 메소드는 언마운트를 '할 건지'를 묻는다. 즉 Unmount 직전에 호출되는 메소드이다. 여기서는 주로 등록했던 setTimeout를 해제하거나 DOM에 등록된 이벤트를 해제해 준다.

함수 컴포넌트

이처럼 클래스 컴포넌트는 생명 주기(Life Cycle)도 있고 여러 메소드를 이용해서 컴포넌트를 만들 수 있다. 이와는 다르게 함수 컴포넌트는 원래 생명 주기라고 할만한 부분도 없고 그렇기 때문에 관련된 메소드도 따로 없다. 하지만 Hook이 함수 컴포넌트도 클래스 컴포넌트처럼 역할을 할 수 있게 해준다. 그래서 함수 컴포넌트에서는 useState라는 Hook을 이용해서 state를 설정하고, useRef라는 Hook을 이용해서 ref도 설정할 수 있다.

클래스 컴포넌트와 차이가 있다면 클래스 컴포넌트에서는 shouldComponentUpdate, componentDidUpdate, componentDidMount가 따로 있다면 함수 컴포넌트에서는 useEffect 하나에서 이 모든 것을 처리한다.

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
  
  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

클래스 컴포넌트에서는 예시 코드처럼 해당 라이프 사이클에 맞는 메소드가 있다.

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);
  
  const onClickBtn = () => setCount(count + 1);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
    // return ()=>{console.log("return에 있는 함수는 unmount될 때 실행됨")}
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={onClickBtn}>
        Click me
      </button>
    </div>
  );
}

위 함수 컴포넌틑 예시에서는 두 개의 Hook이 쓰였다. useState를 통해서 count state를 설정했고, useEffect를 통해서 componentDidMount, componentDidUpdate와 componentWillUnmount의 처리를 같이 해주고 있다.

useEffect의 첫 번째 요소에 있는 함수는 최초 렌더링 될 때 실행된다.(componentDidMount의 역할)
useEffect의 두 번째 요소에 있는 배열은 의존성 배열이라고 한다. 쉽게 말해서 해당 배열 안에 있는 값이 변경될 때 useEffect의 첫 번째 요소에 있는 함수를 실행한다.(componentDidUpdate)
그리고 첫 번째 함수에 return문을 작성해 줄 수 있는데 저기에 작성된 함수는 컴포넌트가 사라질 때 실행된다.(componentWillUnmount)

이런 방법으로 함수 컴포넌트에서는 useEffect를 통해서 클래스 컴포넌트에서 했던 라이프 사이클을 구현할 수 있다.

Hook의 규칙

글을 마치기 전에 마지막으로 훅의 가장 중요한 규칙 세 가지만 알아보도록 하자.

첫 번째로 함수 컴포넌트에서만 쓸 수 있다. 조금 더 정확히는 React function 내에서만 쓸 수 있다.
두 번째로 컴포넌트의 최상위에서만 호출할 수 있다.
세 번째로 반복문, 중첩문에서는 호출할 수 없다. 그 이유는 함수 컴포넌트에서는 렌더링 될 때 훅의 실행 순서를 정하게 되는데, 반복문이나 중첩문에서 호출을 하면 컴포넌트가 렌더링 될 때 훅의 실행 순서를 동일하게 보장해 줄 수 없다.

위에서 소개한 것 말고도 훅의 종류는 많다. 리액트 공식 도큐먼트에 설명이 굉장히 잘 되어있으니, 여길(리액트 자습서) 참고하는 것이 가장 정확한 방법이다.

[참고자료]
https://ko.reactjs.org/docs/hooks-reference.html
https://www.youtube.com/watch?v=2DFXAcck-DQ&list=PLcqDmjxt30RtqbStQqk-eYMK8N-1SYIFn&index=46

profile
주먹펴고 일어서서 코딩해

0개의 댓글