[TIL] React의 useEffect Hook - 만들어진 이유와 사용법

승민·2025년 5월 9일

TIL

목록 보기
20/20

useEffect가 만들어진 이유

useEffect는 함수 컴포넌트에서 부수효과(Side Effects)를 처리하기 위해 도입되었다.
React는 선언적 UI를 강조하며, 컴포넌트가 렌더링될 때마다 예측 가능한 방식으로 동작해야 한다. 하지만 API 호출, 이벤트 리스너 등록, 타이머 설정 같은 부수효과는 컴포넌트 렌더링 외부에서 관리해야 한다.
클래스 컴포넌트에서는 생명주기 메서드가 이를 처리했지만, 코드가 복잡하고 중복 로직이 발생하기 쉬웠다.

useEffect는 이를 하나의 Hook으로 통합해 다음과 같은 문제를 해결한다.

  1. 단순화
    생명주기 메서드를 별도로 관리할 필요 없이, 부수효과를 한 곳에서 처리.
  2. 유연성
    의존성 배열을 통해 특정 상태/props 변경 시에만 부수효과 실행.
  3. 클린업
    메모리 누수를 방지하기 위해 부수효과 정리 로직 제공.

사용법

useEffect는 컴포넌트가 렌더링된 후 부수효과를 실행하고, 필요 시 정리 작업을 수행한다.

import { useEffect } from 'react';

useEffect(() => {
  // 부수효과 로직 (예: API 호출, 이벤트 리스너 등록)
  console.log('Effect 실행');

  return () => {
    // 클린업 로직 (예: 이벤트 리스너 제거, 타이머 해제)
    console.log('클린업 실행');
  };
}, [/* 의존성 배열 */]);

주요 구성 요소

  1. 콜백 함수
    부수효과를 실행하는 로직. 컴포넌트 마운트 또는 의존성 변경 시 실행.
  2. 클린업 함수
    콜백 함수에서 return으로 반환. 컴포넌트 언마운트 또는 의존성 변경 전 실행.
  3. 의존성 배열
    비어 있음([]): 마운트 시 한 번만 실행, 언마운트 시 클린업.
    값 포함([dep1, dep2]): 지정된 값이 변경될 때마다 실행.
    생략: 매 렌더링마다 실행.

사용 예시
API 호출 (마운트 시)

import { useState, useEffect } from 'react';

function Component() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then((res) => res.json())
      .then(setData);
  }, []); // 빈 배열: 마운트 시 한 번만 실행

  return <div>{data ? data.name : 'Loading...'}</div>;
}

이벤트 리스너 (의존성 변경 시)

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    const handleResize = () => console.log(`Count: ${count}`);
    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize); // 클린업
    };
  }, [count]); // count 변경 시마다 실행

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

타이머 설정 (매 렌더링)

import { useEffect } from 'react';

function Component() {
  useEffect(() => {
    const timer = setInterval(() => console.log('Tick'), 1000);

    return () => clearInterval(timer); // 클린업
  }); // 의존성 배열 없음: 매 렌더링마다 실행

  return <div>Timer running...</div>;
}

주의점

  1. 의존성 배열 관리
    모든 외부 변수(상태, props, 함수 등)를 의존성 배열에 포함해야 예상치 못한 동작 방지.
useEffect(() => {
  console.log(props.value); // props.value 사용
}, [props.value]); // 반드시 포함
  1. 클린업 함수
    이벤트 리스너, 구독, 타이머 등은 반드시 정리해 메모리 누수 방지.

  2. ESLint
    eslint-plugin-react-hooks를 사용해 의존성 배열 누락 방지.

왜 매 업데이트마다 실행되나?

React 공식 문서(Why Effects Run on Each Update)에 따르면, useEffect는 의존성 배열에 지정된 값이 변경될 때마다 실행된다. 이는 React의 선언적 철학을 따르며, 컴포넌트 상태와 부수효과가 항상 동기화되도록 보장한다. 클린업 함수가 먼저 실행되고, 새로운 부수효과가 적용되므로 이전 상태의 부수효과가 남아 있지 않다. 이를 통해 버그를 줄이고 예측 가능한 동작을 제공한다.

결론

useEffect는 클래스 컴포넌트의 생명주기 메서드를 대체하며, 함수 컴포넌트에서 부수효과를 간결하게 관리한다. API 호출, 이벤트 리스너, 타이머 설정 등 다양한 작업을 처리하며, 의존성 배열과 클린업 함수로 유연성과 안정성을 제공한다. 프론트엔드 개발에서 useEffect를 올바르게 사용하면 컴포넌트의 생명주기를 효과적으로 관리하고, 메모리 누수를 방지하며, 사용자 경험을 개선할 수 있다.

0개의 댓글