• [React] 리액트 생명주기 (Life Cycle)

    2024. 3. 3.

    by. 지은이: 김지은

    728x90

     

    생명주기 (Life Cycle)이란?

    일반적으로 생명주기라는 건 인간의 생애주기처럼 탄생부터 죽음까지 타임라인을 단계별로 나누어놓은 것을 말한다.

    리액트 컴포넌트의 라이프사이클은 간단히 말하면 컴포넌트가 생성되고 제거되는 과정을 말하며

    크게 Mount, Update, Unmount로 나눌 수 있다.

     

    • Mount: 탄생. 컴포넌트가 화면에 렌더링 됨
    • Update: 변화. 컴포넌트가 리렌더 됨
    • Unmout: 죽음. 컴포넌트가 화면에서 제거됨

    이 과정을 좀 더 자세하게 알아보기 위해 카운터 앱을 만들어보자!

     

    import { useState, useEffect } from "react";
    
    function App() {
      // useState 훅을 사용해서 count 상태와 그 값을 업데이트 하는 setCount 함수 생성
      const [count, setCount] = useState(0);
    
      // useEffect 훅을 사용해서 컴포넌트가 마운트되거나 업데이트될 때마다 호출
      useEffect(() => {
        console.log(count);
      }, [count]);
    
      const handleClickButton = () => {
        setCount(count + 1);
      };
    
      return (
        <div>
          <p>{count}</p>
          <button onClick={handleClickButton}>증가</button>
        </div>
      );
    }
    
    export default App;

    useEffect는 두 가지 인자를 받는데 첫 번째 인자는 원하는 동작을 수행하는 콜백함수, 두 번째는 의존성 배열로 의존성 배열에 있는 값 중 하나라도 변경되면 콜백 함수를 다시 실행시킨다.

    그래서 의존성 배열에 있는 count의 변화를 감지해서 count가 변경될 때마다 콘솔에 해당 값을 출력하도록 설정했다.

    그렇기 때문에 useEffect는 이 의존성 배열에 어떤 값을 넣어주느냐에 따라 동작이 달라진다라고 이해하면 되고, 값도 여러 개 넣을 수 있다.

     

    근데 콘솔창에 가보면 count가 바뀌지 않았는데도 useEffct의 콜백함수가 실행되어 0이 찍혀있는데

    이 App컴포넌트가 마운트 되어 useEffect가 한번 실행이 된다.

     

    근데 이 과정은 굳이 useEffect를 사용하지 않아도 버튼을 클릭했을 때 콘솔에 count값을 출력하면 되지 않을까?

    const handleClickButton = () => {
        setCount(count + 1);
        console.log(count);
    };

     

     

    보는 것처럼 화면과 콘솔이 출력하는데 차이가 있는데 setCount 함수는 비동기로 동작한다.

    그래서 버튼을 클릭해서 setCount함수를 호출 후 즉시 콘솔을 실행한다면 새로운 count값이 콘솔에 반영되지 않을 수 있다.

    하지만 useEffect를 사용해서 count 값의 변화를 감지하고 있다면, count가 업데이트된 후에 실행이 되므로 항상 최신의 count값을 콘솔에 출력할 수 있다.

     

     

    이제 위에서 컴포넌트의 라이프 사이클 Mount, Upadate, Unmount 이 3가지는 useEffect로 제어할 수 있다.

     

    Update시 라이프 사이클 제어하기

    useEffect(() => {
        console.log("업데이트");
    });

    컴포넌트가 업데이트될 때 어떠한 동작을 수행시키려면 위처럼 콜백함수만 전달하고 두 번째 인수를 생략하면 된다. 

    따라서 맨 처음 컴포넌트가 마운트 되었을 때와, 그 이후 버튼을 클릭해서 count가 증가가 되면 콘솔에 '업데이트'가 출력될 것

     

    하지만 새로고침을 하면 업데이트가 되지 않았는데 마운트 되어 콘솔에 업데이트가 출력이 되는데 이걸 방지하고 싶다면,

    useRef를 사용하면 된다.

    const isMountRef = useRef(false); // 레퍼런스 객체 생성 (초기값 false)
    
    useEffect(() => {
        if (!isMountRef.current) {
          isMountRef.current = true;
          return;
        }
        console.log("업데이트");
      });

    useRef는 DOM요소를 참조하는 것 외에 값이나 상태를 저장할 때도 사용이 가능하다.

    useEffect안에 조건문을 추가해서 isMountRef.current값이 false일 때 true로 바꿔주고 return 시키면

    현재 isMount값이 false이기 때문에 true로만 변경하고 함수가 종료되면서 콘솔을 출력하지 않게 되면서 업데이트할 때만 콘솔에 출력할 수 있게 된다.

     

    Mount시 라이프 사이클 제어하기

    useEffect(() => {
        console.log("마운트");
      }, []);

    컴포넌트의 Mount는 두 번째 인수 의존성배열을 빈배열로 전달하면 된다.

    이 뜻은 어떠한 값도 감지 하지 않는다를 의미하며 원래 useEffect는 의존성 배열에 들어간 값을 감지해서 값이 바뀌면 콜백함수를 실행하지만, 값이 아무것도 없으면 콜백함수를 한번 컴포넌트가 마운트 될 때만 실행되게 한다는 뜻 

     

    Unmount시 라이프 사이클 제어하기

    Unmount는 컴포넌트가 제거가 되어야하기 때문에 count의 값이 짝수일 때 나타날 수 있게끔 Even 컴포넌트를 따로 만들어주었다.

    // Even.jsx
    
    const Even = () => {
      return <div>짝수입니다.</div>;
    };
    // App.jsx
    
    {count % 2 === 0 && <Even />}

    count가 2로 나누었을 때 0으로 나누어 떨어진다면 짝수이기 때문에 참이면 Even 컴포넌트를 렌더링 하게 했다.

     

    import { useEffect } from "react";
    
    const Even = () => {
      useEffect(() => {
        return () => {
          console.log("언마운트");
        };
      }, []);
      return <div>짝수입니다.</div>;
    };

    앞서 봤듯이 useEffect는 컴포넌트가 Mount 될 때 콜백함수를 실행할 수 있다.

     

    근데 콜백함수내에 다시 함수를 반환하면 그 반환된 함수는 컴포넌트가 제거될 때 실행되며 이걸 클린업 함수라고 한다.

    그리고 의존성배열에 빈 배열을 전달하면 컴포넌트가 Mount 될 때 한 번만 실행되어야하지만,

    클린업 함수로 인해 컴포넌트가 제거될 때만 실행되게 된다.

    댓글