개발/React

♻️ 리액트 컴포넌트 생명 주기(Lifecycle) 핵심 정리

hanks 2025. 6. 3. 23:38

리액트 컴포넌트는 마치 살아있는 존재처럼 생명 주기(Lifecycle)를 갖는다. 컴포넌트가 생성되어 화면에 표시되고(탄생), 상태 변화에 따라 업데이트되며(변화), 마지막으로 화면에서 사라지는(소멸) 일련의 과정을 거친다. 이 흐름을 이해하는 것은 리액트 개발의 기본이자 핵심이다.

과거에는 클래스형 컴포넌트에서 명시적인 생명 주기 메서드를 사용했지만, 현재는 함수형 컴포넌트useEffect Hook을 통해 이를 더 간결하고 유연하게 다룬다.


1. 클래스형 컴포넌트의 생명 주기

클래스형 컴포넌트는 각 단계별로 특정 메서드가 순차적으로 호출된다. 주요 단계는 다음과 같다.

Mounting (마운팅) - 컴포넌트의 탄생

컴포넌트가 처음 생성되어 DOM에 삽입될 때 발생한다.

  • constructor()
    • 컴포넌트 생성자. this.state 초기화 및 메서드 바인딩을 수행한다.
  • render()
    • UI를 렌더링한다. React 요소를 반환하며, 순수 함수로 작성되어야 한다.
  • componentDidMount()
    • 컴포넌트가 DOM에 성공적으로 마운트된 후 호출된다.
    • 주요 사용: API 요청, 외부 라이브러리 연동, DOM 이벤트 리스너 등록 등.

Updating (업데이트) - 컴포넌트의 변화

컴포넌트의 props 또는 state가 변경될 때 리렌더링 과정을 거친다.

  • shouldComponentUpdate(nextProps, nextState)
    • 리렌더링 여부를 결정한다. true (기본값) 반환 시 리렌더링, false 반환 시 중단.
    • 주요 사용: 불필요한 렌더링을 방지하여 성능 최적화.
  • render()
    • 변경된 propsstate를 기반으로 UI를 다시 렌더링한다.
  • componentDidUpdate(prevProps, prevState)
    • 컴포넌트 업데이트가 완료된 후 호출된다.
    • 주요 사용: 이전 props/state와 현재 값을 비교하여 조건부 로직 수행 (예: 특정 조건 만족 시 추가 API 요청).

Unmounting (언마운팅) - 컴포넌트의 소멸

컴포넌트가 DOM에서 제거될 때 발생한다.

  • componentWillUnmount()
    • 컴포넌트가 DOM에서 제거되기 직전에 호출된다.
    • 주요 사용: componentDidMount에서 등록한 이벤트 리스너, 타이머, 구독 등을 해제하는 정리(cleanup) 작업. 메모리 누수 방지에 필수적이다.

2. 함수형 컴포넌트와 useEffect Hook

함수형 컴포넌트는 useEffect Hook을 사용하여 클래스형 컴포넌트의 생명 주기 기능 대부분을 구현한다. useEffect는 렌더링 이후에 부수 효과(side effects)를 실행한다.

기본 구조: useEffect(callback, [dependencies])

  • callback: 실행할 함수.
  • dependencies (의존성 배열): 배열 안의 값이 변경될 때만 callback 함수가 재실행된다.

useEffect로 생명 주기 흉내 내기

  1. 마운트 시 (componentDidMount 대체)

    • 의존성 배열을 빈 배열([])로 전달하면, 컴포넌트가 처음 렌더링될 때 한 번만 실행된다.
    import React, { useEffect } from 'react';
    
    function MyComponent() {
      useEffect(() => {
        console.log('컴포넌트 마운트됨!');
        // API 호출 등
      }, []); // 빈 배열: 마운트 시에만 실행
    
      return <div>내 컴포넌트</div>;
    }
  2. 업데이트 시 (componentDidUpdate 대체)

    • 의존성 배열에 특정 값([value])을 전달하면, 해당 값이 변경될 때마다 실행된다.
    • 의존성 배열을 생략하면 리렌더링 시마다 실행되므로 주의해야 한다.
    import React, { useState, useEffect } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0);
    
      useEffect(() => {
        console.log(`count가 ${count}(으)로 변경됨!`);
        // count 변경 시 수행할 작업
      }, [count]); // count가 변경될 때마다 실행
    
      return (
        <div>
          <p>Count: {count}</p>
          <button onClick={() => setCount(count + 1)}>증가</button>
        </div>
      );
    }
  3. 언마운트 시 (componentWillUnmount 대체) - 정리(Cleanup) 함수

    • useEffect의 콜백 함수에서 정리 함수(cleanup function)를 반환하면, 해당 함수는 컴포넌트가 언마운트되기 직전 또는 다음 effect가 실행되기 전에 호출된다.
    import React, { useState, useEffect } from 'react';
    
    function TimerComponent() {
      useEffect(() => {
        const timerId = setInterval(() => {
          console.log('타이머 동작 중...');
        }, 1000);
    
        // 정리 함수
        return () => {
          clearInterval(timerId);
          console.log('타이머 정리됨! (언마운트 또는 다음 effect 실행 전)');
        };
      }, []); // 마운트 시 타이머 설정, 언마운트 시 타이머 정리
    
      return <div>타이머 컴포넌트</div>;
    }

💡 마무리

리액트 컴포넌트의 생명 주기를 이해하는 것은 컴포넌트의 동작을 예측하고, 상태 변화에 따른 적절한 작업을 수행하며, 애플리케이션의 성능을 최적화하는 데 매우 중요하다.

최신 리액트 개발에서는 함수형 컴포넌트와 useEffect Hook을 사용하는 것이 권장된다. 이를 통해 더욱 간결하고 직관적으로 컴포넌트의 생명 주기를 관리할 수 있다.