리액트 useEffect 사용법과 주의사항

2026. 9. 22. 22:45ㆍIT & 개발/Java & Spring

반응형

리액트 useEffect 사용법과 주의사항

리액트에서 useState 다음으로 가장 많이 쓰는 Hook이 바로 useEffect입니다. 컴포넌트가 화면에 나타날 때 데이터를 불러오거나, 특정 값이 바뀔 때 어떤 동작을 실행하고 싶을 때 useEffect를 사용합니다. 이 글에서는 useEffect가 언제 실행되는지, 의존성 배열은 어떻게 쓰는지, 그리고 실수하기 쉬운 부분까지 정리했습니다.
📖 목차
1. useEffect란 무엇인가
2. 의존성 배열로 실행 시점 제어하기
3. 실전 예제와 정리 함수(cleanup)
4. 마무리

#1. useEffect란 무엇인가
1) 사이드 이펙트가 뭔가요?
리액트 컴포넌트의 역할은 기본적으로 데이터를 받아서 화면을 그리는 것입니다. 그런데 실제 앱을 만들다 보면 화면을 그리는 것 외에도 해야 하는 일들이 생깁니다.
예를 들어 컴포넌트가 열릴 때 서버에서 데이터를 가져와야 하거나, 특정 값이 바뀔 때 브라우저 타이틀을 업데이트하거나, 타이머를 설정해야 하는 경우입니다. 이런 작업들을 사이드 이펙트(Side Effect)라고 합니다. 화면을 그리는 것과 직접 관련이 없는 부수적인 동작이라는 의미입니다.
💡 카페에서 음료를 주문(컴포넌트 렌더링)할 때, 주문 외에 영수증 출력·재고 차감·포인트 적립이 같이 일어나는 것처럼 — 화면을 그리는 것 외에 함께 처리해야 하는 부수 작업이 사이드 이펙트입니다.
· · · · ·
2) useEffect의 기본 형태
useEffect는 React에서 제공하는 Hook으로, 컴포넌트가 렌더링된 이후에 실행할 코드를 등록할 수 있습니다. 기본 형태는 아래와 같습니다.
import { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // 렌더링 이후 실행할 코드
    console.log('컴포넌트가 화면에 나타났습니다.');
  });

  return <div>화면</div>;
}
위 코드는 컴포넌트가 렌더링될 때마다 실행됩니다. 하지만 이렇게 쓰면 매번 실행되어 불필요한 동작이 생길 수 있습니다. 실행 시점을 조절하는 방법이 바로 의존성 배열입니다.

#2. 의존성 배열로 실행 시점 제어하기
1) 의존성 배열의 세 가지 형태
useEffect의 두 번째 인자로 배열을 넘기면 실행 시점을 제어할 수 있습니다. 형태에 따라 동작이 달라집니다.
형태실행 시점
useEffect(() => { ... })렌더링될 때마다 실행
useEffect(() => { ... }, [])처음 한 번만 실행 (마운트 시)
useEffect(() => { ... }, [value])value가 바뀔 때마다 실행
실무에서 가장 많이 쓰는 패턴은 빈 배열 []을 넘기는 것입니다. 컴포넌트가 처음 화면에 나타날 때 한 번만 실행됩니다. API를 호출해서 데이터를 불러올 때 주로 이 방식을 씁니다.
import { useState, useEffect } from 'react';

function UserList() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    // 컴포넌트가 처음 나타날 때 한 번만 실행
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(res => res.json())
      .then(data => setUsers(data));
  }, []);

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}
· · · · ·
2) 특정 값이 바뀔 때 실행하기
의존성 배열에 변수를 넣으면 그 값이 바뀔 때마다 useEffect가 다시 실행됩니다. 검색어가 바뀔 때마다 결과를 다시 불러오는 상황을 예로 들어 보겠습니다.
import { useState, useEffect } from 'react';

function Search() {
  const [keyword, setKeyword] = useState('');

  useEffect(() => {
    if (keyword.trim() === '') return;

    // keyword가 바뀔 때마다 실행
    console.log(keyword + ' 검색 중...');
    // 실제 API 호출로 교체 가능
  }, [keyword]);

  return (
    <input
      value={keyword}
      onChange={e => setKeyword(e.target.value)}
      placeholder="검색어 입력"
    />
  );
}
⚠️ 의존성 배열에 넣어야 할 값을 빠뜨리면 오래된 값을 참조하는 버그가 생길 수 있습니다. useEffect 안에서 사용하는 state나 props는 기본적으로 배열에 넣는 것이 원칙입니다. ESLint의 react-hooks/exhaustive-deps 규칙이 이 실수를 잡아줍니다.

#3. 실전 예제와 정리 함수(cleanup)
1) 정리 함수가 필요한 이유
useEffect 안에서 타이머를 설정하거나 이벤트 리스너를 등록하면, 컴포넌트가 화면에서 사라질 때 이것들을 직접 해제해야 합니다. 해제하지 않으면 이미 사라진 컴포넌트가 계속 동작하려고 해서 메모리 누수나 오류가 생깁니다.
이때 사용하는 것이 정리 함수(cleanup function)입니다. useEffect의 콜백 함수 안에서 함수를 반환하면, 컴포넌트가 사라지기 직전 또는 다음 effect가 실행되기 전에 그 함수가 호출됩니다.
💡 방을 쓰고 나서 불을 끄고 나가는 것처럼, 컴포넌트가 사라질 때 사용했던 자원(타이머, 이벤트 리스너, 구독 등)을 정리하는 역할을 합니다.
· · · · ·
2) 타이머 예제로 보는 정리 함수
1초마다 숫자가 올라가는 타이머를 예로 들면, 컴포넌트가 사라질 때 타이머를 멈추지 않으면 오류가 발생합니다.
import { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    // 1초마다 seconds를 1씩 증가
    const timer = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    // 컴포넌트가 사라질 때 타이머 해제
    return () => clearInterval(timer);
  }, []);

  return <p>경과 시간: {seconds}초</p>;
}
return () => clearInterval(timer) 부분이 정리 함수입니다. 컴포넌트가 화면에서 제거될 때 자동으로 실행되어 타이머를 멈춥니다.
· · · · ·
3) useEffect 흐름 한눈에 보기
useEffect의 전체 실행 흐름을 정리하면 다음과 같습니다.
단계시점설명
1. 렌더링컴포넌트 화면 출력화면을 먼저 그린다
2. effect 실행렌더링 직후useEffect 안 코드 실행
3. 정리 함수 실행다음 effect 전 또는 언마운트반환된 함수 실행
4. 재실행의존성 값 변경 시3 → 2 순서로 반복
렌더링이 먼저 일어나고 그 다음에 useEffect가 실행된다는 점이 중요합니다. useEffect 안에서 setState를 호출하면 렌더링이 한 번 더 일어나므로, 꼭 필요한 경우에만 사용하는 것이 좋습니다.

#4. 마무리
useEffect를 처음 배울 때 헷갈리는 포인트는 크게 세 가지입니다.
첫째, 의존성 배열에 따라 실행 시점이 달라진다는 것.
둘째, 배열이 비어 있으면([]) 처음 한 번만 실행된다는 것.
셋째, 타이머나 이벤트 리스너를 등록할 때는 반드시 정리 함수를 반환해야 한다는 것.
이 세 가지를 기억하면 useEffect를 안전하게 사용할 수 있습니다.
실무에서는 데이터 페칭, 브라우저 이벤트 등록, 외부 라이브러리 연동 등 거의 모든 사이드 이펙트를 useEffect로 처리합니다. 예제 코드를 직접 실행해보면서 각 상황에 맞는 의존성 배열 패턴을 익혀두면 이후 개발이 훨씬 수월해집니다.
긴 글 읽어주셔서 감사합니다.
끝.

반응형