본문 바로가기
웹/React

[React] useEffect 사용하기

by 천무지 2024. 7. 26.
반응형

 

 

 

useEffect는 리액트에서 컴포넌트의 side effect를 제어하는 리액트 훅이다.

 

컴포넌트의 side effect는 무엇인가?

 

리액트에서 컴포넌트의 사이드 이펙트란 컴포넌트가 어떤 동작을 했을때 발생하게 되는 파생적인 효과를 말한다.

 

예를 들어서

컴포넌트의 어떤 값이 변경되었을 때 콘솔레 변경된 값을 출력하거나,

컴포넌트가 마운트 되었을 때 콘솔에 Mount라고 출력하거나, 

컴포넌트가 리렌더링(업데이트) 되었을 때 콘솔에 Update라고 출력하거나,

컴포넌트가 언마운트 되었을 때 콘솔에 Ummount라고 출력하는 상황을 말한다.

 

이렇게 라이프 사이클을 제어하는 것도 컴포넌트의 사이드 이펙트라고 할 수 있다.

 

이번에 알아볼 useEffect를 이용하면 이러한 사이드 이펙트들을 제어할 수 있다.

 

내가 만약 카운터를 1증가시키는 +버튼을 누른 다음 count의 값을 콘솔로그를 통해 확인하고 싶다고 하자.

 

그래서 다음과 같이 코드를 작성하였다.

const onClickButton = (e) => {
    setCount((prev) => prev + Number(e.target.value));
    console.log(count);
  };

이러면 setCount를 통해 바뀐 count의 값이 출력이 될까?

 

아니다. 바뀌기 전의 값이 출력이 된다.

 

왜냐하면 setCount는 리액트의 상태 변화 함수인데 이는 비동기로 동작하기 때문이다.

 

여기서 비동기란 setTimeOut 함수 처럼 호출은 진작에 되었지만 함수의 완료가 뒤늦게 되는 것을 말한다.

 

그래서 setCount로 count의 값을 바꾸라는 명령어가 실행되었고, 그 다음에 콘솔로그 명령어가 실행되었지만, setCount 함수는 아직 완료가 되지 않았기에, 콘솔로그에서 출력하는 count의 값은 아직 바뀌기 전의 값인 것이다.

 

리액트에서는 state가 비동기로 처리되기 때문에 변경된 state를 바로 사용하고 싶다면 useEffect를 사용해야 한다.

 

useEffect를 라이프 사이클과 같이 알아보자.


1. Mount

  useEffect(() => {
    console.log("mount");
  }, []);

 

useEffect는 콜백 함수와 deps라는 배열을 인자로 가진다.

 

useEffect는 deps라는 배열 안에 있는 값이 변경되면 실행되는데, 비어있기 때문에 컴포넌트가 처음 불렀을때(마운트되었을때) 실행되고 이후에는 실행이 되지 않는다.

 

따라서 어떤 컴포넌트가 마운트될 때 어떤 데이터를 불러오는 코드로 활용이 가능하겠다.


2. Update

useEffect(() => {
    console.log("update");
  },[value]);

 

이때에는 Mount 될 때 실행되고, value의 값이 바뀔 때마다 useEffect 함수가 실행된다.

 

useEffect(() => {
    console.log("update");
  });

 

여기서 배열을 아예 생략해 버리면 컴포넌트가 Mount될 때 실행되고, 컴포넌트가 리렌더링이 발생할때마다 실행이 된다.

 

여기서 위 두 상황의 공통점은 업데이트 뿐만 아니라 컴포넌트가 Mount될 때에도 실행된다는 점이다.

 

만약 업데이트될 때에만 실행이 되도록 하고 싶다면 다음과 같이 useRef를 사용하여 코드를 작성할 수 있다.

const isMount = useRef(false);

useEffect(() => {
    if (!isMount.current) {
      isMount.current = true;
      return;
    }
    console.log("update");
  });

 

레퍼런스 객체를 하나 생성하고 boolean 값으로 false를 넣어준 뒤 Mount에서 useEffect가 실행되면 if문을 만나 래퍼런스 객체의 current 값을 토글하고 함수를 종료 시킨다.

 

이렇게 하면 Mount될 때에는 실행이 되지 않게 할 수 있다.


3. Unmount

Unmount는 컴포넌트가 종료될 때를 말한다.

 

이러한 상황에서도 useEffect를 통해 어떠한 동작을 수행하도록 할 수 있다.

 

바로 클린업 함수다.

 

클린업 함수는 useEffect의 콜백 함수가 반환하는 함수를 말한다.

 

이 함수는useEffect가 종료될 때 실행된다.

 

사용하는 상황으로는 어떤 컴포넌트가 언마운트 될때, 해당 컴포넌트가 쓰고 있는 메모리를 해제할 때 사용할 수 있을 것이다.

useEffect(() => {
.
.
.
    return () => {
    console.log("unmount");
    };
}, []);

 

예시로는 만약 카운터의 값의 짝수라면 '짝수입니다' 라는 문구를 나타내는 컴포넌트가 있다고 한다면 해당 컴포넌트는 카운터의 값이 홀수 일때마다 Unmount될 것이다.

 

따라서 그때 클린업 함수에 console.log를 출력해보면 언마운트 된 사실을 잘 확인할 수 있을 것이다.

 

//App.js
import { useEffect, useState, useRef } from "react";
import "./App.css";
import Viewer from "./components/Vierwer";
import Controller from "./components/Controller";
import Judge from "./components/Judge";

function App() {
  const [count, setCount] = useState(0);
  const [input, setInput] = useState("");

  const isMount = useRef(false);

  // 1. 마운트: 탄생
  useEffect(() => {
    console.log("mount");
  }, []);
  // 빈 배열을 주게되면 useEffect는
  // deps의 값이 변경되어야 실행되는데
  // 비었으니까 컴포넌트가 처음 마운트 될때 이후에는 실행X
  // 어떤 컴포넌트가 마운트될 때 어떤 데이터를 불러오는 코드로 사용 가능

  // 2. 업데이트: 변화, 리렌더링
  useEffect(() => {
    console.log("update");
  });
  // 이렇게 deps 자체를 생략해주면, 마운트 될때 실행되고,
  // 컴포넌트가 리렌더링(업데이트)될 때마다 실행됨

  // 2-1. 만약 마운트될 때는 실행X, 업데이트만 실행하고 싶으면??
  useEffect(() => {
    if (!isMount.current) {
      isMount.current = true;
      return;
    }
    console.log("update");
  });
  // 래퍼런스 객체를 useRef로 생성하고 flag로써 사용하면됨.
  // 어떤 컴포넌트가 업데이트 되었을때, 정상적인 값들로 업데이트 되었는지 확인할 때 사용 가능

  // 3. 언마운트: 죽음

  const onClickButton = (e) => {
    setCount((prev) => prev + Number(e.target.value));
    console.log(count);
    // setCount 라는 리액트의 상태 변화 함수는 비동기로 동작
    // 비동기란?
    // setTimeOut처럼 함수 호출은 진작에 되었지만
    // 함수의 완료가 뒤늦게 되는 것
    // 그래서 아래에 콘솔로그가 호출되었을때, setCount는 완료되지 않았음
    // 그래서 count의 값은 아직 변경되지 않았음
    // 그래서 리액트의 state는 비동기로 처리되기 때문에 변경된 state를
    // 바로 사용하고 싶으면 useEffect를 사용하는 것
  };
  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <input
          value={input}
          onChange={(e) => {
            setInput(e.target.value);
          }}
        ></input>
      </section>
      <section>
        <Viewer count={count} />
        {count % 2 === 0 ? <Judge /> : null}
      </section>
      <section>
        <Controller onClickButton={onClickButton} />
      </section>
    </div>
  );
}

export default App;
//Judge
import React, { useEffect } from "react";

const Judge = ({ count }) => {
  useEffect(() => {
    // useEffect의 콜백함수가 반환하는 함수를 Clean-up 함수라고 함
    // 이 함수는 useEffect가 끝날때 실행됨
    // 그래서 빈 배열을 deps로 주면 mount될 때 실행되고,
    // 언마운트될 때 클린업 함수가 실행될거임.
    // 어떤 컴포넌트가 언마운트될 때, 해당 컴포넌트가 쓰고 있는 메모리를 해제할 때 사용 가능

    return () => {
      console.log("unmount");
    };
  }, []);

  return <div>"짝수입니다"</div>;
};

export default Judge;

 

앱 컴포넌트에서 Judge 컴포넌트가 호출되는 상황을 삼항연산자를 통해 작성하였다.

반응형