개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

useEffect — 렌더링 이후 Side Effect를 처리하는 Hook

2026년 5월 3일
ReactuseEffectSide Effect

React 컴포넌트는 순수 함수처럼 동작해야 합니다. 같은 props와 state를 받으면 항상 같은 JSX를 반환해야 하며, 렌더링 중에 외부 시스템에 영향을 주어서는 안 됩니다. 하지만 실제 애플리케이션에서는 API 호출, 타이머 설정, DOM 직접 조작처럼 외부 시스템과 상호작용해야 하는 경우가 많습니다. useEffect는 이런 Side Effect를 안전하게 처리하는 Hook입니다.

useEffect 기본 구조

useEffect(() => {
  // Side Effect 코드
  return () => {
    // Cleanup 코드 (선택)
  };
}, [의존성]);

useEffect는 세 가지 요소로 구성됩니다. 첫 번째 인자인 Effect 함수, 이 함수가 반환하는 Cleanup 함수, 그리고 두 번째 인자인 의존성 배열입니다.

의존성 배열

의존성 배열은 Effect가 언제 다시 실행될지를 결정합니다.

// 1. 의존성 배열 없음 — 모든 렌더링 후 실행
useEffect(() => {
  console.log("렌더링될 때마다 실행");
});

// 2. 빈 배열 — 최초 마운트 시 한 번만 실행
useEffect(() => {
  console.log("마운트 시 한 번만 실행");
}, []);

// 3. 값이 있는 배열 — 배열 안의 값이 변경될 때마다 실행
useEffect(() => {
  console.log(`userId가 ${userId}로 변경됨`);
}, [userId]);

의존성 배열 안에는 Effect 함수 내부에서 사용하는 모든 반응형 값(state, props, 컴포넌트 내부에서 선언된 변수)을 포함해야 합니다. 빠뜨리면 오래된 값을 참조하는 버그가 발생할 수 있습니다.

반응형 값(Reactive Value): 렌더링 중에 계산되는 값. props, state, 컴포넌트 본문 안에서 선언된 변수가 해당됨. 변경되면 리렌더링을 트리거함.

Cleanup 함수

Effect 함수가 함수를 반환하면, React는 해당 Effect를 다시 실행하기 전과 컴포넌트가 언마운트될 때 이 Cleanup 함수를 호출합니다.

useEffect(() => {
  const intervalId = setInterval(() => {
    setCount((c) => c + 1);
  }, 1000);

  return () => {
    clearInterval(intervalId); // Cleanup: 인터벌 제거
  };
}, []);

Cleanup을 빠뜨리면 컴포넌트가 언마운트된 후에도 타이머, 이벤트 리스너, 서버 구독이 계속 실행되어 메모리 누수가 발생합니다.

Mount / Update / Unmount 시 동작

Mount:
  렌더링 완료 → Effect 함수 실행

Update (의존성 변경 시):
  리렌더링 완료 → 이전 Effect의 Cleanup 실행 → 새 Effect 함수 실행

Unmount:
  컴포넌트 제거 → 마지막 Effect의 Cleanup 실행

흔한 실수

의존성 배열에서 값 누락: Effect 내부에서 사용하는 state나 props를 의존성 배열에서 빠뜨리면 초기 렌더링 시의 값을 계속 참조하는 클로저 문제가 발생합니다.

// 버그: count가 항상 0으로 고정됨
useEffect(() => {
  const id = setInterval(() => {
    setCount(count + 1); // 초기값 0만 참조
  }, 1000);
  return () => clearInterval(id);
}, []); // count가 의존성에 없음

// 수정: 함수형 업데이트로 최신 값 참조
useEffect(() => {
  const id = setInterval(() => {
    setCount((c) => c + 1); // 항상 최신 state 참조
  }, 1000);
  return () => clearInterval(id);
}, []);

렌더링 중 Side Effect 실행: useEffect 없이 컴포넌트 본문에서 직접 API를 호출하면 렌더링마다 요청이 발생합니다. Side Effect는 반드시 useEffect 안에서 실행해야 합니다.

빈 의존성 배열 남용: 빈 배열을 쓰면 마운트 시 한 번만 실행되어 편해 보이지만, Effect 내부에서 사용하는 값이 변경되어도 Effect가 다시 실행되지 않아 오래된 값을 계속 참조하게 됩니다. 의존성은 정직하게 기입해야 합니다.

← 이전 글Hooks — 함수형 컴포넌트에서 상태와 생명주기를 사용하는 API
다음 글 →Rendering — React가 UI를 계산하여 화면에 반영하는 과정