0525

FE

React 컴포넌트의 생명주기 이해하기

| 서론

안녕하세요, 팡일입니다.

이전 글에서는 React에서 마운트와 언마운트가 무엇인지 알아보았습니다.

마운트는 컴포넌트가 화면에 나타나는 것이고, 언마운트는 화면에서 사라지는 것을 의미했습니다. 그런데 컴포넌트는 화면에 나타난 뒤 가만히 있지 않습니다 사용자의 입력이나 데이터의 변화에 따라 다시 렌더링되며, 필요한 작업을 실행하거나 기존 작업을 정리하기도 합니다.

이처럼 컴포넌트가 생성되고 변화하다가 사라지기까지의 전체 흐름을 컴포넌트 생명주기(Component Lifecycle)라고 합니다.

이번 글에서는 마운트와 언마운트 자체보다 컴포넌트가 어떤 흐름으로 동작하는지를 중심으로 알아보겠습니다.

| 컴포넌트 생명주기의 전체 흐름

React 컴포넌트의 생명주기는 크게 세 단계로 구분할 수 있습니다.

text
마운트 → 업데이트 → 언마운트
  • 마운트: 컴포넌트가 처음 화면에 나타나는 단계

  • 업데이트: 컴포넌트가 변경되어 다시 렌더링되는 단계

  • 언마운트: 컴포넌트가 화면에서 제거되는 단계

마운트와 언마운트가 각각 등장과 퇴장을 의미한다면, 업데이트는 그사이에서 컴포넌트가 계속 변화하는 과정입니다.

실제 React 애플리케이션에서는 컴포넌트가 마운트된 이후 여러 번 업데이트되고, 더 이상 필요하지 않을 때 언마운트되는 흐름이 반복됩니다.

| 컴포넌트는 언제 업데이트될까?

컴포넌트의 업데이트는 주로 다음과 같은 상황에서 발생합니다.

  • 컴포넌트의 state가 변경될 때

  • 부모에게 전달받은 props가 변경될 때

  • 사용 중인 Context의 값이 변경될 때

  • 부모 컴포넌트가 다시 렌더링될 때

간단한 카운터를 예로 들어보겠습니다.

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

  return (
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
  );
}

Counter 컴포넌트가 처음 화면에 나타나면 마운트됩니다.

이후 버튼을 눌러 count가 변경될 때마다 컴포넌트가 다시 렌더링됩니다. 이 과정이 업데이트입니다.

버튼을 여러 번 눌러도 컴포넌트가 매번 새롭게 마운트되는 것은 아닙니다. 기존 컴포넌트가 유지된 상태에서 변경된 값을 화면에 반영하는 것입니다.

| 렌더링과 화면 반영

컴포넌트가 업데이트된다고 해서 React가 기존 화면을 모두 지우고 다시 만드는 것은 아닙니다.

React는 컴포넌트를 다시 실행하여 이전 결과와 새로운 결과를 비교합니다. 이후 실제로 변경이 필요한 부분만 DOM에 반영합니다.

TSX
function Greeting({ name }: { name: string }) {
  return <p>안녕하세요, {name}님!</p>;
}

name광일에서 팡일로 변경되면 React는 새로운 렌더링 결과를 확인하고, 실제 DOM에서는 이름이 표시되는 부분만 변경합니다.

이러한 동작 덕분에 개발자는 DOM을 직접 수정하기보다 상태와 속성에 따라 화면이 어떻게 보여야 하는지를 선언할 수 있습니다.

| 생명주기와 useEffect

렌더링만으로 처리할 수 없는 작업은 useEffect를 사용합니다.

대표적으로 다음과 같은 작업이 있습니다.

  • 서버에 데이터 요청하기

  • 브라우저 이벤트 등록하기

  • 타이머 실행하기

  • 외부 시스템과 연결하기

  • 변경된 값에 따라 부가적인 작업 실행하기

TSX
function SearchResult({ keyword }: { keyword: string }) {
  useEffect(() => {
    console.log(`${keyword} 검색 결과를 불러옵니다.`);
  }, [keyword]);

  return <div>{keyword} 검색 결과</div>;
}

이 Effect는 컴포넌트가 처음 화면에 반영된 뒤 실행됩니다. 이후 keyword가 변경되면 다시 실행됩니다.

즉, useEffect의 실행 시점은 의존성 배열을 통해 결정할 수 있습니다.

TSX
useEffect(() => {
  // 렌더링이 화면에 반영될 때마다 실행
});
TSX
useEffect(() => {
  // 처음 화면에 반영된 뒤 실행
}, []);
TSX
useEffect(() => {
  // 처음 화면에 반영된 뒤와 value가 변경된 뒤 실행
}, [value]);

생명주기를 이해할 때 중요한 것은 useEffect가 단순히 마운트 시점에만 사용하는 기능이 아니라는 점입니다.

컴포넌트가 화면에 반영된 이후 외부 시스템과 동기화하기 위해 사용하는 기능에 가깝습니다.

| 정리 함수는 언마운트할 때만 실행될까?

useEffect의 정리 함수는 컴포넌트가 언마운트될 때 실행됩니다. 하지만 언마운트될 때만 실행되는 것은 아닙니다.

의존하는 값이 변경되어 Effect가 다시 실행될 때도 이전 Effect를 먼저 정리합니다.

TSX
function ChatRoom({ roomId }: { roomId: string }) {
  useEffect(() => {
    const connection = connectToRoom(roomId);

    return () => {
      connection.disconnect();
    };
  }, [roomId]);

  return <div>{roomId} 채팅방</div>;
}

roomId가 변경되면 다음과 같은 순서로 동작합니다.

  1. 기존 채팅방 연결을 해제합니다. (useEffect가 반환한 정리 함수가 먼저 실행)

  2. 새로운 roomId로 다시 렌더링된 화면을 반영합니다.

  3. 새로운 채팅방에 연결합니다.

컴포넌트가 언마운트될 때도 마지막으로 사용하던 연결을 해제합니다.

따라서 정리 함수는 단순히 컴포넌트의 퇴장을 처리하는 기능이 아니라, 이전 Effect의 작업을 정리하는 기능으로 이해하는 것이 좋습니다.

| 생명주기를 왜 이해해야 할까?

생명주기를 이해하면 코드가 언제 실행되고 정리되는지를 예측할 수 있습니다.

예를 들어 다음과 같은 문제를 방지하는 데 도움이 됩니다.

  • 데이터 요청이 불필요하게 반복되는 문제

  • 이벤트가 중복으로 등록되는 문제

  • 이전 데이터나 연결이 그대로 남아 있는 문제

  • 타이머가 컴포넌트 제거 후에도 실행되는 문제

  • 최신 상태가 화면에 반영되지 않는 문제

결국 생명주기를 이해한다는 것은 단순히 용어를 외우는 것이 아니라, 컴포넌트가 언제 생성되고 다시 렌더링되며 작업을 정리하는지 이해하는 것입니다.

| 한눈에 정리하기

단계

컴포넌트의 상태

주요 동작

마운트

처음 화면에 나타남

초기 렌더링 및 Effect 실행

업데이트

상태나 속성이 변경됨

재렌더링 및 필요한 Effect 재실행

언마운트

화면에서 제거됨

마지막 Effect 정리

| 결론

React 컴포넌트의 생명주기는 마운트, 업데이트, 언마운트로 이어지는 전체 흐름을 의미합니다.

이 가운데 실제 애플리케이션에서 가장 자주 발생하는 것은 상태나 속성의 변화에 따른 업데이트입니다. React는 변화가 발생하면 컴포넌트를 다시 렌더링하고, 필요한 부분만 실제 화면에 반영합니다.

또한 useEffect를 통해 화면 반영 이후 필요한 작업을 실행하고, 정리 함수를 통해 이전 작업을 안전하게 종료할 수 있습니다.

컴포넌트 생명주기를 이해하면 React 코드가 언제 실행되고 변화하며 정리되는지를 더욱 명확하게 파악할 수 있습니다.

컴포넌트 생명주기는 컴포넌트의 등장과 퇴장뿐만 아니라, 그사이에서 일어나는 모든 변화의 흐름입니다.