[React] - useEffect 사용하기

Ahnzi·2024년 11월 21일

useEffect

useEffect란 React 컴포넌트의 사이드 이펙트를 제어하는 React Hook 입니다.

사이드 이펙트는 우리말로 “부작용”이라는 뜻이지만, React에서는 “부수적인 효과” 또는 “파생되는 효과”로 해석할 수 있습니다.

즉, React에서의 사이드 이펙트란 컴포넌트의 동작에 따라 발생하는 추가적인 효과를 의미합니다.

예를 들어, 컴포넌트 내부의 값이 변경되었을 때 해당 값이 어떻게 변경되었는지를 콘솔에 출력하거나, 컴포넌트가 Mount되었을 때 “Mount”, Update되었을 때 “Update”, Unmount되었을 때 “Unmount”라고 콘솔에 출력하는 라이프 사이클 제어도 컴포넌트의 사이드 이펙트로 볼 수 있습니다.

useEffect를 이용하면 사이드 이펙트를 제어할 수 있습니다.


useEffect 사용하기

useEffect 또한 React Hook이기 때문에 기존 실습 예제 코드에서 useEffect를 import 해줍니다.

import "./App.css";
import Controller from "./components/Controller";
import Viewer from "./components/Viewer";

import { useState, useEffect } from "react";

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

  const onClickButton = (value) => {
    setCount(count + value);
  };

  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <Viewer count={count} />
      </section>
      <section>
        <Controller onClickButton={onClickButton} />
      </section>
    </div>
  );
}

export default App;

App 컴포넌트 내부에서 count 상태가 변경될 때 원하는 동작을 수행하려면 useEffect를 사용하면 됩니다. useEffect의 첫 번째 인수로는 실행할 콜백 함수를 전달하고, 두 번째 인수로는 배열을 전달합니다. 배열에 count를 추가하면, count가 변경될 때마다 콜백 함수가 실행됩니다.

import "./App.css";
import Controller from "./components/Controller";
import Viewer from "./components/Viewer";

import { useState, useEffect } from "react";

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

  useEffect(() => {}, []);

  const onClickButton = (value) => {
    setCount(count + value);
  };

  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <Viewer count={count} />
      </section>
      <section>
        <Controller onClickButton={onClickButton} />
      </section>
    </div>
  );
}

export default App;

useEffect는 두 번째 인수로 전달한 배열에 있는 값이 변경될 때마다, 사이드 이펙트로서 첫 번째 인수로 전달한 콜백 함수를 실행합니다. 두 번째 인수 배열에 count 상태 값을 넣으면, useEffect는 count 상태가 변경될 때마다 콜백 함수를 실행하게 됩니다. 이 콜백 함수 내부에서 console.log를 사용해 count 상태 값을 출력할 수 있습니다.

import "./App.css";
import Controller from "./components/Controller";
import Viewer from "./components/Viewer";

import { useState, useEffect } from "react";

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

  useEffect(() => {
    console.log(`count : ${count}`);
  }, [count]);

  const onClickButton = (value) => {
    setCount(count + value);
  };

  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <Viewer count={count} />
      </section>
      <section>
        <Controller onClickButton={onClickButton} />
      </section>
    </div>
  );
}

export default App;

브라우저의 콘솔 탭을 확인하면, App 컴포넌트가 렌더링될 때 최초로 count 값이 초기화되므로 count: 0이라는 메시지가 출력됩니다.

그 이후에 count 값을 변경하면, 브라우저 콘솔에 변경된 count 값이 출력됩니다.

useEffect는 두 번째 인수로 전달한 배열에 포함된 count 상태 값이 변경될 때마다 콜백 함수를 실행합니다. 이 과정에서 변경된 count 값을 브라우저 콘솔에 지속적으로 출력합니다.

따라서 useEffect는 두 번째 인수로 전달된 배열에 의존하게 됩니다. 배열에 어떤 값을 넣느냐에 따라 동작이 달라지기 때문에 이 배열을 의존성 배열(Dependency Array)이라고 부릅니다. 줄여서 deps라고도 합니다.

deps에는 여러 개의 값을 넣을 수 있습니다. 새로운 input 상태(state)를 생성하고, 새로운 섹션(section)을 추가한 뒤, input 태그 안에 value 속성에는 해당 input 상태를, onChange 이벤트 핸들러에는 임시로 콜백 함수를 사용하여 setInput(event.target.value)를 호출하도록 설정합니다. useEffect React Hook에서도 input 정보를 출력해줍니다.

import "./App.css";
import Controller from "./components/Controller";
import Viewer from "./components/Viewer";

import { useState, useEffect } from "react";

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

  useEffect(() => {
    console.log(`count : ${count} / input : ${input}`);
  }, [count, input]);

  const onClickButton = (value) => {
    setCount(count + value);
  };

  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <input
          type="text"
          value={input}
          onChange={(event) => {
            setInput(event.target.value);
          }}
        />
      </section>
      <section>
        <Viewer count={count} />
      </section>
      <section>
        <Controller onClickButton={onClickButton} />
      </section>
    </div>
  );
}

export default App;

count의 값이 변경되거나, input에 값을 입력할 때, 변경된 값들이 브라우저 콘솔에 출력됩니다.

useEffect를 사용하면 컴포넌트 내에서 특정 값이 변경될 때만 원하는 동작을 콜백 함수로 실행하도록 설정할 수 있습니다.


console.log 로 확인할 수 있지 않나?

🙋🏻‍♂️ 누군가는 이렇게 질문할 수도 있습니다.

useEffect를 사용하면 원하는 값이 변경될 때 콜백 함수가 실행된다는 건 알겠어! 그런데 이벤트 핸들러에서 그냥 console.log로 확인하면 안 되는 거야?

count 상태 값이 변경될 때마다 특정 동작을 수행하고 싶다면, 굳이 useEffect를 사용할 필요 없이, onClickButton 이벤트 핸들러에서 setCount를 호출한 뒤, 변경된 값을 console.log로 출력하면 되는 것 아니냐는 이야기입니다.

직접 확인해봅니다.

import "./App.css";
import Controller from "./components/Controller";
import Viewer from "./components/Viewer";

import { useState, useEffect } from "react";

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

  useEffect(() => {
    // console.log(`count : ${count} / input : ${input}`);
  }, [count, input]);

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

  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <input
          type="text"
          value={input}
          onChange={(event) => {
            setInput(event.target.value);
          }}
        />
      </section>
      <section>
        <Viewer count={count} />
      </section>
      <section>
        <Controller onClickButton={onClickButton} />
      </section>
    </div>
  );
}

export default App;

이때 “+100” 버튼을 한 번만 눌렀을 때, 현재 카운트 값은 “100”이지만 브라우저 콘솔에는 “0”이 출력됩니다. 또 한 번 “+100” 버튼을 누르면 현재 카운트 값은 “200”이지만 브라우저 콘솔에는 “100”이 출력됩니다. 변경되기 이전의 값이 브라우저 콘솔에 출력됩니다.

왜그럴까요? setCount라는 React의 상태 변화 함수는 비동기로 동작하기 때문입니다.

비동기로 동작한다.

"비동기로 동작한다."라는 말은 무엇일까요?

JavaScript의 setTimeout 함수처럼, setCount(count + value)를 호출하더라도 함수의 완료는 나중에 이루어집니다. 따라서 console.log(count) 라인이 실행될 때에는 setCount 함수가 호출되었을 뿐, 실제로 완료되지 않은 상태입니다. 그 결과, count 상태 값은 아직 변경되지 않았으며, 브라우저 콘솔에는 변경되기 전의 count 상태 값이 계속 출력되는 것입니다.

결론적으로, React의 상태(state)는 비동기로 업데이트되기 때문에, 변경된 상태 값을 즉시 사용하여 부가적인 작업(사이드 이펙트)을 수행하려고 한다면, 위와 같은 방식으로는 안 됩니다. 이 경우 useEffect를 사용해야 합니다. 그래야 버튼을 눌렀을 때 변경된 상태 값을 즉시 활용할 수 있습니다.

profile
운동하는 개발자 Ahnzi 입니다.

0개의 댓글