Skip to content

라이프사이클 (Lifecycle)

React 컴포넌트는 화면에 나타나고, 업데이트되고, 사라지는 과정을 가진다.
이 흐름을 라이프사이클(Lifecycle) 이라고 한다.

🚀New Article:React State and Lifecycle – Managing Dynamic Data(useEffect)🚀

React는 이 과정마다 특정 작업을 실행할 수 있도록 기능을 제공한다.

🔄 라이프사이클을 이해해야 하는 이유

React에서 라이프사이클을 이해하면 컴포넌트가 언제 렌더링되고, 언제 부수 효과를 실행해야 하는지 알 수 있다. 특히 다음과 같은 작업을 할 때 중요하다.

  • API 요청 시점 정하기
  • 불필요한 렌더링 줄이기
  • 이벤트 리스너 정리하기
  • 타이머 정리하기
  • 메모리 누수 방지하기
  • props나 state 변경에 따른 작업 처리하기

라이프사이클이란?

라이프사이클은 컴포넌트가 브라우저 화면에 나타난 뒤, 상태props 변화에 따라 다시 렌더링되고, 더 이상 필요하지 않을 때 화면에서 사라지는 전체 흐름을 의미한다.

예를 들어 다음과 같은 상황들이 라이프사이클과 관련된다.

  • 컴포넌트가 처음 화면에 나타날 때
  • state가 변경되어 화면이 다시 렌더링될 때
  • props가 변경될 때
  • 컴포넌트가 화면에서 사라질 때
  • API 요청, 타이머, 이벤트 등록과 정리가 필요할 때

React 컴포넌트의 라이프사이클 단계

React 컴포넌트의 라이프사이클은 크게 세 단계로 나눌 수 있다.

1. Mount

Mount는 컴포넌트가 처음 화면에 나타나는 단계이다.

  • 초기 데이터 요청 / 이벤트 등록 / 타이머 설정 / 외부 라이브러리 초기화

2. Update

Update는 컴포넌트가 다시 렌더링되는 단계이다.

  • state가 변경될 때 / props가 변경될 때 / 부모 컴포넌트가 다시 렌더링될 때

3. UnMount

Unmount는 컴포넌트가 화면에서 사라지는 단계이다.
예를 들어 조건부 렌더링으로 컴포넌트를 숨기거나, 다른 페이지로 이동해서 기존 컴포넌트가 사라질 때 Unmount가 발생한다.

  • 타이머 제거 / 이벤트 제거 / 구독 해제 / 진행 중인 작업 정리

클래스 컴포넌트의 라이프사이클 메서드

과거에는 클래스 컴포넌트에서 라이프사이클 메서드를 사용했다.

jsx
class Example extends React.Component {
  componentDidMount() {
    console.log("컴포넌트가 처음 화면에 나타남");
  }

  componentDidUpdate() {
    console.log("컴포넌트가 업데이트됨");
  }

  componentWillUnmount() {
    console.log("컴포넌트가 화면에서 사라짐");
  }

  render() {
    return <div>Example</div>;
  }
}

대표적인 라이프사이클 메서드는 다음과 같다.

메서드실행 시점
componentDidMount컴포넌트가 처음 화면에 나타난 후
componentDidUpdate컴포넌트가 업데이트된 후
componentWillUnmount컴포넌트가 화면에서 사라지기 전

함수 컴포넌트의 useEffect

함수 컴포넌트에서는 라이프사이클 메서드 대신 useEffect를 사용한다.
useEffect는 렌더링 이후 특정 작업을 실행할 수 있게 해주는 Hook이다.

jsx
import { useEffect } from "react";

function Example() {
  useEffect(() => {
    console.log("컴포넌트가 렌더링된 후 실행");
  });

  return <div>Example</div>;
}

useEffect로 라이프사이클 표현하기

useEffect의 의존성 배열을 어떻게 작성하느냐에 따라 실행 시점이 달라진다.

1. 렌더링될 때마다 실행

jsx
useEffect(() => {
  console.log("렌더링될 때마다 실행");
});
  • 의존성 배열을 생략하면 컴포넌트가 렌더링될 때마다 실행된다.
  • 즉, 처음 렌더링될 때도 실행되고, stateprops가 변경되어 다시 렌더링될 때도 실행된다.

2. 처음 한 번만 실행

jsx
useEffect(() => {
  console.log("처음 한 번만 실행");
}, []);
  • 의존성 배열에 빈 배열 []을 넣으면 컴포넌트가 처음 화면에 나타난 뒤 한 번만 실행된다.
  • 클래스 컴포넌트의 componentDidMount와 비슷한 역할을 한다.
  • 주로 초기 API 요청에 사용한다.
    jsx
    useEffect(() => {
      fetchData();
    }, []);

3. 특정 값이 변경될 때 실행

jsx
useEffect(() => {
  console.log("count가 변경될 때 실행");
}, [count]);
  • 의존성 배열에 특정 값을 넣으면 그 값이 변경될 때마다 effect가 실행된다.
  • 클래스 컴포넌트의 componentDidUpdate와 비슷하게 사용할 수 있다.

4. 컴포넌트가 사라질 때 정리하기

useEffect 안에서 함수를 return하면 정리 함수로 사용된다.

jsx
useEffect(() => {
  const timer = setInterval(() => {
    console.log("타이머 실행 중");
  }, 1000);

  return () => {
    clearInterval(timer);
    console.log("타이머 정리");
  };
}, []);
  • return된 함수는 컴포넌트가 화면에서 사라질 때 실행된다.
  • 클래스 컴포넌트의 componentWillUnmount와 비슷한 역할을 한다.

useEffect 실행 흐름

다음 코드를 보면 라이프사이클 흐름을 더 쉽게 이해할 수 있다.

jsx
import { useEffect, useState } from "react";

function LifeCycleExample() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("Mount 또는 count 변경 후 실행");

    return () => {
      console.log("다음 effect 실행 전 또는 Unmount 시 정리");
    };
  }, [count]);

  return (
    <div>
      <p>count: {count}</p>
      <button onClick={() => setCount(count + 1)}>증가</button>
    </div>
  );
}

이 코드의 실행 흐름은 다음과 같다.

  1. 컴포넌트가 처음 렌더링됨
  2. useEffect 실행
  3. 버튼 클릭
  4. count 변경
  5. 컴포넌트 다시 렌더링
  6. 이전 effect 정리 함수 실행
  7. 새로운 effect 실행
  8. 컴포넌트가 사라지면 정리 함수 실행

여기서 중요한 점은 return 함수가 꼭 Unmount 때만 실행되는 것은 아니라는 것이다. 의존성 배열의 값이 바뀌어 effect가 다시 실행되기 전에도 이전 effect를 정리하기 위해 실행된다.