React Hook – UseEffect 이해하기 


React의 useEffect는 컴포넌트의 생명주기를 관리하고 side effects를 처리하는 데 필수적인 Hook입니다. 이 포스트에서는 useEffect의 기본 사용법, 동작 시점, 클린업 처리, 그리고 주의사항에 대해서 설명합니다. 이를 통해 useEffect를 이해하고 효율적으로 활용하는데 도움이 됐으면 좋겠습니다.


1. useEffect란 무엇인가?

useEffect는 React의 함수형 컴포넌트에서 side effects를 처리하기 위해 사용됩니다. side effects란 컴포넌트의 렌더링 외에 발생하는 작업을 의미하며, 다음과 같은 작업을 포함합니다:

  • 데이터 가져오기 (API 호출)
  • DOM 조작
  • 타이머 설정
  • 이벤트 리스너 등록 및 해제

React 컴포넌트는 기본적으로 렌더링에만 집중하도록 설계되어 있지만, 실제 애플리케이션에서는 렌더링 외에도 다양한 작업이 필요합니다. 이때, useEffect를 사용하면 이러한 작업을 컴포넌트의 생명주기에 맞춰 쉽게 처리할 수 있습니다.


2. useEffect의 기본 사용법

useEffect는 두 가지 주요 인자를 받습니다:

  1. 콜백 함수: 실행할 작업을 정의합니다.
  2. 의존성 배열 (Dependency Array): 어떤 값이 변경될 때만 콜백을 실행할지 결정합니다.

2.1. 의존성 배열이 없는 경우

useEffect(() => {
  console.log("렌더링될 때마다 실행됩니다.");
});
  • 컴포넌트가 마운트되거나 업데이트될 때마다 실행됩니다.
  • 모든 렌더링마다 실행되므로, 성능에 영향을 줄 수 있습니다.

2.2. 빈 배열을 전달한 경우

useEffect(() => {
  console.log("초기 렌더링 시 한 번만 실행됩니다.");
}, []);
  • 컴포넌트가 처음 마운트될 때만 실행됩니다.
  • 이후 업데이트 시에는 실행되지 않습니다.

2.3. 특정 값에 의존하는 경우

useEffect(() => {
  console.log(`count 값이 변경됨: ${count}`);
}, [count]);
  • count 값이 변경될 때만 실행됩니다.
  • 의존성 배열에 포함된 값이 변경되지 않으면 실행되지 않습니다.

3. useEffect의 동작 시점

React의 useEffect는 컴포넌트의 렌더링 이후에 실행됩니다. 이는 React가 DOM 업데이트를 완료한 후에 useEffect 내부의 콜백 함수가 실행된다는 것을 의미합니다.

동작 시점

  1. 초기 렌더링 후 실행
    • 컴포넌트가 처음 화면에 렌더링된 직후 실행됩니다.
    • 의존성 배열을 빈 배열([])로 설정하면 초기 렌더링 시 한 번만 실행됩니다.
  2. 의존성 값 변경 시 실행
    • 의존성 배열에 포함된 값이 변경될 때마다 실행됩니다.
  3. 컴포넌트 언마운트 시 실행 (클린업 함수)
    • 컴포넌트가 화면에서 사라질 때, 또는 다음 렌더링 전에 클린업 함수가 실행됩니다.

4. 클린업(Cleanup) 처리

useEffect는 컴포넌트가 언마운트되거나, 다음 렌더링 전에 정리 작업을 수행할 수 있도록 클린업 함수를 제공합니다. 클린업은 다음과 같은 작업에 유용합니다:

  • 타이머 정리
  • 이벤트 리스너 제거
  • 구독 해제

클린업 함수 사용법

useEffect(() => {
  const timer = setInterval(() => {
    console.log("타이머 실행 중...");
  }, 1000);

  return () => {
    clearInterval(timer); // 타이머 정리
    console.log("타이머가 종료되었습니다.");
  };
}, []);
  • return으로 반환된 함수는 컴포넌트가 언마운트되거나, 다음 렌더링 전에 실행됩니다.
  • 이를 통해 메모리 누수를 방지할 수 있습니다.

5. 예제 코드

5.1. 기본 예제: 렌더링 시점 확인

import React, { useState, useEffect } from "react";

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

  useEffect(() => {
    console.log("렌더링 완료 후 실행");
  });

  useEffect(() => {
    console.log("초기 렌더링 시 한 번만 실행");
  }, []);

  useEffect(() => {
    console.log(`count 값이 변경됨: ${count}`);
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase Count</button>
    </div>
  );
}

export default Example;

5.2. 클린업 예제: 타이머 컴포넌트

import React, { useState, useEffect } from "react";

function Timer() {
  useEffect(() => {
    const timer = setInterval(() => {
      console.log("타이머 실행 중...");
    }, 1000);

    return () => {
      clearInterval(timer);
      console.log("타이머가 종료되었습니다.");
    };
  }, []);

  return <div>타이머가 실행 중입니다.</div>;
}

function App() {
  const [showTimer, setShowTimer] = useState(false);

  return (
    <div>
      <button onClick={() => setShowTimer(!showTimer)}>
        {showTimer ? "타이머 종료" : "타이머 시작"}
      </button>
      {showTimer && <Timer />}
    </div>
  );
}

export default App;

6. useEffect 사용 시 주의 사항

  1. 의존성 배열 관리:
    • 의존성 배열에 포함된 값이 변경될 때만 useEffect가 실행됩니다.
    • 의존성 배열을 잘못 설정하면, 의도치 않은 동작이나 성능 문제가 발생할 수 있습니다.
  2. 무거운 작업은 피하기:
    • useEffect 내부에서 무거운 작업을 수행하면 렌더링 성능에 영향을 줄 수 있습니다.
    • 무거운 작업은 별도의 함수로 분리하거나, 비동기 작업으로 처리하세요.
  3. 클린업 함수 작성:
    • 타이머, 이벤트 리스너, 구독 등은 반드시 클린업 함수를 통해 정리해야 합니다.
    • 그렇지 않으면 메모리 누수(memory leak)가 발생할 수 있습니다.
  4. 비동기 작업 주의:
    • useEffect 내부에서 비동기 작업을 수행할 때, 비동기 함수 자체를 useEffect에 직접 전달하지 마세요.
    • 비동기 작업은 useEffect 내부에서 별도의 함수로 정의해야 작업의 흐름을 명확히 제어할 수 있습니다. 
    • useEffect는 반환값으로 클린업 함수(cleanup function)를 기대하는 반면 비동기 함수는 Promise를 반환하므로, useEffect에 직접 전달하면 React가 이를 클린업 함수로 잘못 처리할 수 있습니다

7. 요약

React의 useEffect는 컴포넌트의 생명주기를 관리하고, 부수효과를 처리하는 데 필수적인 Hook입니다. 주요 포인트는 다음과 같습니다:

  • 렌더링마다 실행: 의존성 배열이 없을 때.
  • 마운트 시 한 번 실행: 빈 배열을 전달할 때.
  • 특정 값 변경 시 실행: 의존성 배열에 값을 전달할 때.
  • 클린업 함수: 컴포넌트 언마운트 시 정리 작업을 수행.

useEffect를 적절히 활용하면 React 애플리케이션의 상태 관리와 성능 최적화에 큰 도움을 줄 수 있습니다.

jeewoo jung 아바타