useEffect의 cleanup 함수 알아보기

2025. 10. 24. 18:20·Frontend/React

1. Cleanup 함수의 기본 역할

React를 다루다 보면 useEffect 내부에 return으로 전달하는 cleanup 함수(clean-up function)를 자주 보게 된다. cleanup 함수는 컴포넌트가 언마운트되거나, 의존성 배열(dependency array)에 변화가 생길 때 이전 이펙트를 정리하기 위해 동작한다. 이를 통해 메모리 누수나 불필요한 이벤트 리스너 잔존 문제를 방지한다.

useEffect(() => {
  const id = setInterval(() => {
    console.log('tick');
  }, 1000);

  // cleanup 함수
  return () => {
    clearInterval(id);
    console.log('cleanup!');
  };
}, []);

예를 들어 setInterval, addEventListener, WebSocket, Subscription 등 외부 리소스를 사용하는 경우, 이를 정리하지 않으면 메모리 누수나 중복 실행 문제가 생길 수 있다.

 

 

2. Cleanup 함수의 실행 타이밍

(1) 컴포넌트 언마운트 시

컴포넌트가 화면에서 사라질 때(unmount), cleanup 함수가 한 번 실행된다.

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

  return () => {
    clearInterval(timer);
    console.log('unmounted - cleanup!');
  };
}, []); // 빈 배열
  • []는 의존성이 없기 때문에 effect는 최초 마운트 시 한 번 실행한다.
  • 컴포넌트가 언마운트될 때 cleanup이 실행된다.

(2) 디펜던시 변경 시

dependency array에 값이 들어 있다면, 값이 바뀌기 직전에 cleanup이 먼저 실행된다.

useEffect(() => {
  console.log('effect 실행:', count);

  return () => {
    console.log('cleanup 실행:', count);
  };
}, [count]);

실행 순서 예시는 다음과 같다:

count = 0 → effect 실행: 0
count = 1 → cleanup 실행: 0 → effect 실행: 1
count = 2 → cleanup 실행: 1 → effect 실행: 2

 

즉, useEffect는 의존성 배열에 있는 값이 바뀌기 전에, 이전 effect에서 생성된 side effect(이전 effect가 남긴 부수작용)을 먼저 정리(cleanup)한다. 이렇게 함으로써 이전 effect와 새 effect가 동시에 영향을 주어 꼬이는 것을 방지하고, side effect가 안전하게 관리되도록 보장한다.

 

side effect 제거 예시

1. 타이머/Interval

useEffect(() => {
  const id = setInterval(() => console.log(count), 1000);

  return () => clearInterval(id); // cleanup
}, [count]);

count가 바뀔 때마다 새 interval이 생성되면 이전 interval이 그대로 남아 중복 실행될 수 있기 때문에 cleanup이 이전 interval을 제거해서 꼬임을 방지한다.

 

2. 구독/이벤트 리스너

useEffect(() => {
  const handleResize = () => console.log(window.innerWidth);
  window.addEventListener("resize", handleResize);

  return () => window.removeEventListener("resize", handleResize);
}, []);

 

컴포넌트가 언마운트되거나 dependency가 바뀔 때 이전 리스너를 제거하지 않으면 메모리 누수나 중복 호출이 발생할 수 있다.

 

3. API 요청 취소

useEffect(() => {
  const controller = new AbortController(); // 취소 토큰 생성
  fetch("/api/data", { signal: controller.signal });

  return () => controller.abort(); // 이전 요청 취소
}, [query]);

 

사용자가 빠르게 검색어를 바꾸면 이전 요청이 완료되지 않은 상태에서 새 요청이 발생한다. 이때, cleanup에서 이전 요청을 취소하면, 꼬이는 케이스나 불필요한 네트워크 요청을 막을 수 있다.

 

3. 주의해야 할 점

1. Cleanup이 없으면?

이벤트 리스너를 추가하는 예시에서 cleanup이 없으면, 이벤트가 계속 추가되며 점점 중복 실행된다.

useEffect(() => {
  window.addEventListener('scroll', handleScroll);
}, []);

이를 정리하지 않으면 컴포넌트가 사라져도 이벤트가 남아 있을 수 있다.

 

따라서, 아래와 같이 명시적으로 제거해줘야 한다. 

useEffect(() => {
  window.addEventListener('scroll', handleScroll);
  return () => window.removeEventListener('scroll', handleScroll);
}, []);

2. 의존성 배열 누락 시

useEffect의 의존성 배열을 잘못 설정하면 cleanup이 제대로 작동하지 않거나, 필요 이상으로 effect가 반복 실행된다.

React의 ESLint 플러그인(eslint-plugin-react-hooks)을 사용하면 문제를 자동으로 감지한다.

# 설치
npm install eslint-plugin-react-hooks --save-dev
// .eslintrc.json 예시
{
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/rules-of-hooks": "error", // 훅 사용 규칙
    "react-hooks/exhaustive-deps": "warn"  // 의존성 배열 체크
  }
}

 

ESLint 경고 예시

React Hook useEffect has missing dependencies: 'count' and 'name'. Either include them or remove the dependency array. (react-hooks/exhaustive-deps)

 

4. 실무에서의 팁

  • Subscription / Timer / EventListener → 반드시 cleanup을 작성한다.
  • 단순 API 호출만 하는 경우 cleanup은 필수는 아니지만, 실시간 검색처럼 사용자가 계속 입력을 바꾸며 새로운 쿼리가 발생하는 상황에서는 이전 요청을 취소하기 위해(취소 토큰 관리를 위해) cleanup을 활용할 수 있다.
  • React.StrictMode에서는 개발 환경에서 effect가 두 번 실행되므로 cleanup이 정확히 되어 있는지 검증할 수 있다.

 

React.StrictMode 에서의 문제 발생 케이스: cleanup 누락

import { useEffect, useState } from 'react';

function ScrollTracker() {
  const [scroll, setScroll] = useState(0);

  const handleScroll = () => {
    setScroll(window.scrollY);
    console.log('스크롤 이벤트 실행');
  };

  useEffect(() => {
    // cleanup을 작성하지 않음
    window.addEventListener('scroll', handleScroll);
  }, []);

  return <div>현재 스크롤 위치: {scroll}</div>;
}

 

스크롤 이벤트 실행
스크롤 이벤트 실행
  • StrictMode에서 effect가 두 번 실행되기 때문에, 이벤트 리스너가 두 번 등록됨
  • 결과적으로 스크롤을 한 번 움직여도 handler가 두 번 호출됨
  • 실제 배포 환경에서는 한 번만 실행되지만, 개발 중에는 중복 문제를 발견할 수 있도록 도와준다.

 

5. 요약 및 마무리

상황 cleanup 실행 시점
[] (빈 배열) 언마운트 시 한 번
[deps] (의존성 있음) deps 변경 시마다, 다음 effect 실행 전에
생략 시 cleanup 없음 — 메모리 누수 가능성 ↑
StrictMode 개발환경에서 두 번 호출되어 정상 동작 여부 테스트

cleanup 함수는 “효과를 없애는 함수”가 아니라, 이전 상태를 정리하고 새로운 상태를 준비하는 과정이다. React는 이를 통해 side effect를 예측 가능한 순서로 관리한다.

'Frontend > React' 카테고리의 다른 글

상태(stateful) & 비상태(stateless) 컴포넌트  (0) 2025.10.25
React에서 비동기 처리 최적화하기  (0) 2025.10.25
useRef 자세히 알아보기  (0) 2025.10.24
React 메모이제이션과 최적화  (0) 2025.10.24
Virtual DOM  (0) 2025.10.16
'Frontend/React' 카테고리의 다른 글
  • React에서 비동기 처리 최적화하기
  • useRef 자세히 알아보기
  • React 메모이제이션과 최적화
  • Virtual DOM
JTB
JTB
웹/앱 개발 정보를 공유하고 있습니다.
  • JTB
    JTechBlog
    JTB
  • 전체
    오늘
    어제
    • All About Programming;)
      • Computer Science
        • Terminology and Concepts
        • Network
        • Operating System
        • Database
        • Data Structure
        • Web Development
      • Frontend
        • Javascript Essentials
        • Perfomance Optimization
        • JS Patterns
        • React
        • Next.js
        • Flutter
        • Testing
      • Backend
        • Node.js
      • DevOps
        • Docker & Kubernetes
      • Coding Test
        • LeetCode
        • Programmers
      • Tech Books & Lectures
        • Javascript_Modern JS Deep d..
        • Network_IT 엔지니어를 위한 네트워크 입문
      • Projects
        • PolyLingo_2025
        • Build Your Body_2024
        • JStargram_2021
        • Covid19 Tracker_2021
        • JPortfolio_2021
      • BootCamp_Codestates
        • TIL
        • TILookCloser
        • Pre Tech Blog
        • IM Tech Blog
        • Daily Issues and DeBugging
        • First Project
        • Final Project
        • Sprint Review
        • Good to Know
        • Socrative Review
        • HTML &amp; CSS
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 글쓰기
    • 관리
  • 공지사항

  • 인기 글

  • 태그

    Time complexity and Space complexity
    Data Structure
    VoiceJournal
    How memory manage data
    자바스크립트 딥다이브
    스코프
    Shared resources
    structure of os
    자바스크립트
    프론트엔드 성능 최적화 가이드
    딥다이브
    Network
    Threads and Multithreading
    TCP/IP
    이벤트
    Binary Tree BFS
    need a database
    CPU scheduling algorithm
    모던 자바스크립트 Deep Dive
    mobile app
    DOM
    database
    Operating System
    testing
    Javascript Essentials
    커리어
    indie hacker
    polylingo
    leetcode
    js pattern
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
JTB
useEffect의 cleanup 함수 알아보기
상단으로

티스토리툴바