본문 바로가기
리엑트/실무 중심 FE 입문자를 위한 React

04 LifeCycle과 Hooks

by 문자메일 2024. 8. 13.

 

React Hook 설명

https://well-made-codestory.tistory.com/44

 

[ReactJS] React Hook(리액트 훅) 이란?

React Hook(리액트 훅) 이란? 개요 리액트에서 중요한 React Hook(리액트 훅)의 개념 정리를 통해 프로젝트에서 효율적으로 사용할 수 있도록 하고, 대표적인 훅들에 대해 알아본다. 목차 React Hook의 등

well-made-codestory.tistory.com

 

 

https://ko.legacy.reactjs.org/docs/hooks-overview.html

 

Hook 개요 – React

A JavaScript library for building user interfaces

ko.legacy.reactjs.org

 

 

Hooks 종류

- useState

- useEffect

- useCallback

- useMemo, useContext, useRef, useLayoutEffect

<기본 내장 hooks만 10개 정도밖에 되지 않는다, 위 초록색 3개만 주로 사용한다>

 

React의 라이프사이클, React의 동작과정에 대해서 이해하려면 먼저 Hooks에 대해 알아야 한다.

 

 

useState - 이 컴포넌트의 state를 생성하는 hook

이 hook은 배열을 return 하는데, 첫 번째 요소는 state 값이고, 두번째 요소는 해당 state를 set 해주는 setter 함수이다.

그리고 useState 함수의 인자로 넘겨주는 값은 state의 초기 값을 설정하는 값이다.

 

  /**
   * useState: 상태 값과 그 값을 갱신하는 함수를 반환
   * - 인자: 초기 상태 값
   * - 반환: [상태 변수, 상태에 대한 Setter]
   */
  const [value, setValue] = useState(0);

 

 

  /**
   * useEffect: 컴포넌트가 렌더링 될 때, 특정 작업을 실행
   * - 인자
   *   - 실행하고자 하는 함수 (effect callback)
   *     - effect는 정리(clean-up) 함수를 반환할 수 있음
   *     - 반환된 함수는 컴포넌트가 언마운트 또는 effect 재실행 이전에 실행됨
   *   - 의존성 배열 (dependency list)
   */

useEffect - 컴포넌트가 렌더링 될 때 현재 상태 변화에 따라서 조건적으로 특정 작업을 실행하기 위한 hook

컴포넌트의 State가 변경될 때마다 컴포넌트의 렌더링을 다시 실행하는 리렌더링 작업을 함

이 때 매번 같은 로직을 실행하는 것이 아니라, 첫 번째 렌더링에만( = 즉 컴포넌트가 마운트 될 때) 그 때 한 번만 특정 로직을 실행시키고 싶을 때 이 useEffect라는 hook을 사용한다.

 

 

위 이미지 로그 제일 위에 'Render Counter!' 로그는 컴포넌트가 렌더링 될 때 매번 실행이 되는 부분이고 확인할 수 있다.

두 번째 useEffect() 로그도 찍힌 것 확인 가능하다. 

위에서 useEffect() hooks는 의존하는 state가 없기 때문에 ([]) 컴포넌트가 처음 마운트 됐을 때만 실행되게 된다.

useEffect()안에 함수가 또 다른 함수를 return 하고 있는데, 이것을 클린업 함수라고 한다.

클린업 함수는 컴포넌트가 언마운트 될 때 실행이 되는 함수이다.

 

클린업 함수의 존재 의의 : 컴포넌트가 마운트 됐다 언마운트 되어도 컴포넌트가 언마운트 되기 전에 실행되었던 이 리소드(이벤트, 객체)들은 여전히 남아 있게 되어서, 다시 해당 컴포넌트가 마운트가 되면 로직이 중복 실행되는 문제가 생긴다.

이 때 클린업 함수 부분에서 그런 리소스들을 정리할 수 있게 지원을 해준다.

 

 

 

 

위 이미지처럼 컴포넌트가 언마운트 될 때, 할당된 리소스(event)를 해제하여 나중에 다시 컴포넌트가 마운트 되었을 때 로직 중복 실행 문제를 막을 수 있다. 

 

 

useEffect의 정확한 동작 원리는 컴포넌트의 상태 변화에 따라 특정 로직을 실행시켜 준다는 것이다.

아래 예시에서는 value 스테이트가 변경되는 순간에 useEffect에 정의한 함수를 실행하겠다는 의미이다.

이때 CleanUp 함수는 Value 스테이트가 변경되어서 리렌더링 될 때, useEffect() 도 한 번 더 실행이 될 텐데, 실제적으로 안에 있는 함수가 실행되기 직전에 이 CleanUp 함수를 실행하고 그 다음에 useEffect() 안에 있는 함수를 실행한다.

 

아래 이미지에서 state의 변화에 따라 useEffect 함수 내에서 로직 실행되는 순서 콘솔창에서 확인이 가능하다.

 

 

 

  /**
   * useCallback: 메모이제이션된 콜백을 반환
   * - 인자
   *   - 메모이제이션 할 함수
   *   - 의존성 배열
   * - 반환: 메모이제이션 된 함수
   * *의존성 배열을 제대로 셋팅하지 않으면 함수 안에서 사용되는 값이 업데이트 되지 않은 값일 수 있음
   */

useCallback() - 성능을 위해서 등장한 hook

 

기본적으로 컴포넌트 내부에서 함수를 정의하고 사용한다고 했을 때 위 처럼 함수를 정의하고, 저 함수를 이벤트 등등에 전달을 한다.

그러면 코드가 실행이 되면서 렌더링이 되면서 함수를 한 번 생성을 하고 생성된 함수는 사용이 되는데, 컴포넌트가 리렌더링 되면 어떻게 되는가?

그러면 컴포넌트는 처음부터 렌더링을 시작할거고, 똑같은 함수를 생성하는 코드를 만나서 새로운 함수를 다시 생성한다. (기능은 같지만 아예 다른 메모리에 쌓여있는 함수)

즉, 리렌더링 될 때 마다, 이 코드를 만날 때 마다 함수를 계속 만들어 준다. (물론 이전에 생성한 함수는 더 이상 참조하는 곳이 없으니 브라우저가 자동으로 Garbage Collecting/메모리 해제을 해준다.)

그럼에도 매번 렌더링이 될때마다 불필요하게 똑같은 역할을 하는 함수를 계속 생성을 한다는 것이 비효율적이다.

위 문제를 해결하기 위해서 useCallback() 훅을 만들었다.

지정한 의존성 상태가 변경되지 않으면, 이전 렌더링에서 생성한 함수를 기억해서 그걸 그대로 재활용을 하는 것이다.

 

 

 

Hooks 사용에 주의사항

1. 조건적으로 hooks를 사용해서는 안 된다. (아래처럼 hook을 사용하면 안 된다.)

 

2. hooks는 오직 리엑트 컴포넌트에서만 사용할 수 있다.

 

 

 

 

4-2 React 렌더링 과정

Rerendering - 상태(State, Props)가 변경되면 화면을 다시 그림

 

 

처음 컴포넌트가 실행됐을 때와 다시 실행됐을 때의 동작 방식이 조금 달라진거다

이런 식으로 React의 컴포넌트는 처음 실행된 건지, 상태 변화에 의해 다시 실행된 건지, 또는 어떤 상태가 변했는지에 의해서 그 동작 방식이 조금씩 달라질 수 있다.

이것을 React의 라이프 사이클이라고 한다.

 

 

클래스형 컴포넌트의 Lifecycle

https://velog.io/@khpark/React-Class%ED%98%95-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4Life-Cycle

 

 

함수형 컴포넌트의 Lifecycle

 

클래스형 컴포넌트

import React, { Component } from 'react';

class ClassComponent extends Component {
  state = {
    value: 0
  };

  // 컴포넌트가 생성될 때 가장 먼저 실행
  constructor(props) {
    console.log('[Class] constructor');
    super(props);
    this.state = {
      value: 0
    };
  }

  // 컴포넌트가 렌더링 되기 전에 실행
  // (return 값이 false면 렌더링 중단)
  shouldComponentUpdate(nextProps, nextState) {
    console.log('[Class] shouldComponentUpdate');
    return true;
  }

  // 컴포넌트의 마운트가 끝나면 실행
  componentDidMount() {
    console.log('[Class] componentDidMount');
  }

  // 컴포넌트가 업데이트 된 후 실행
  componentDidUpdate(prevProps, prevState) {
    console.log('[Class] componentDidUpdate');
  }

  // 컴포넌트 언마운트 되기 직전에 실행
  componentWillUnmount() {
    console.log('[Class] componentWillUnmount');
  }

  render() {
    console.log('[Class] render');

    return (
      <div>
        <h1>ClassComponent</h1>
        <h1>value: {this.state.value}</h1>
        <button
          onClick={() => {
            this.setState((state) => ({
              value: state.value + 1
            }));
          }}
        >
          Increase value
        </button>
      </div>
    );
  }
}

export default ClassComponent;

 

마운트

 

업데이트
언마운트

 

 

 

함수형 컴포넌트

import { useEffect, useState } from 'react';

function FunctionalComponent() {
  console.log('[Function] Beginning');
  const [value, setValue] = useState(0);

  useEffect(() => {
    console.log('[Function] useEffect []');

    return () => {
      console.log(
        '[Function] useEffect return []'
      );
    };
  }, []);

  useEffect(() => {
    console.log('[Function] useEffect [value]');

    return () => {
      console.log(
        '[Function] useEffect return [value]'
      );
    };
  }, [value]);

  console.log('[Function] End');

  return (
    <div>
      <h1>FunctionComponent</h1>
      <h1>value: {value}</h1>
      <button
        onClick={() => {
          setValue((state) => state + 1);
        }}
      >
        Increase value
      </button>
    </div>
  );
}

export default FunctionalComponent;

 

마운트 단계
state 변경 단계
언마운트 단계

 

'리엑트 > 실무 중심 FE 입문자를 위한 React' 카테고리의 다른 글

06 React 환경 설정  (0) 2024.08.15
05 이벤트 헨들링  (0) 2024.08.14
Chapter 03 컴포넌트  (0) 2024.06.13
JSX  (0) 2024.06.10
React란?  (0) 2024.06.10

댓글