본문 바로가기
framework_library/react

useEffect는 왜 위험한가

by 죄니안죄니 2026. 1. 9.
useEffect는 왜 위험한가useEffect는 왜 위험한가useEffect는 왜 위험한가
 
 
 

useEffect는 왜 위험한가

렌더링 이후에 실행된다는 사실이 만드는 함정들

React 초반 학습에서 useEffect는 마치 만능처럼 등장한다.
API 호출, 이벤트 등록, 상태 동기화… 전부 여기로 몰아넣는다.

문제는 이거다.

useEffect는 문제를 해결해주기보다
구조 문제를 가려주는 경우가 훨씬 많다.

이번 글에서는
“언제 쓰면 되는지”보다
**“언제 쓰면 안 되는지”**를 먼저 정리한다.


1️⃣ useEffect의 정확한 실행 위치

먼저 순서를 다시 고정한다.

  1. state / props 변경
  2. 컴포넌트 함수 실행 (렌더링)
  3. DOM 반영 (commit)
  4. useEffect 실행

즉,

useEffect는
렌더링을 만들 수는 있지만
렌더링 안에서 실행되지는 않는다.

이게 모든 문제의 출발점이다.


2️⃣ 가장 흔한 실수: 상태를 다시 만드는 effect

실무에서 정말 자주 보는 코드다.

 
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]);

  return <div>{user?.name}</div>;
}

이 코드는 겉보기엔 정상이다.
하지만 구조적으로는 위험 신호를 포함하고 있다.

왜냐하면 이 컴포넌트는 사실 이렇게 말하고 있기 때문이다.

“이 컴포넌트의 상태는
props(userId)로부터 다시 만들어진다”

그럼 질문이 생긴다.

👉 그럼 이 상태는 왜 컴포넌트 안에 있는가?


3️⃣ useEffect가 구조 문제를 숨기는 패턴

이 패턴을 기억해두면 좋다.

useEffect(() => {
  setX(f(props));
}, [props]);

이건 사실상 이걸 의미한다.

const x = f(props);

차이점은 하나다.

  • 위: 렌더링 중 계산
  • 아래: 렌더링 이후 재조정

즉, 불필요한 2단계 렌더링을 만들고 있는 셈이다.

실무에서 이 패턴이 많아질수록:

  • 렌더 타이밍이 꼬이고
  • 디버깅이 어려워지고
  • “왜 값이 늦게 바뀌지?” 같은 말이 나온다

4️⃣ 진짜로 위험한 코드: 렌더링 루프

이건 거의 사고 사례다.

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

  useEffect(() => {
    setCount(count + 1);
  }, [count]);

  return <div>{count}</div>;
}

흐름을 그대로 따라가 보자.

  1. 렌더 → count = 0
  2. effect 실행 → setCount(1)
  3. 렌더 → count = 1
  4. effect 실행 → setCount(2)
  5. 무한 반복

이건 문법 문제가 아니다.
사고 모델이 잘못된 것이다.

effect는
“상태를 만들어내는 장소”가 아니다.


5️⃣ 그럼 useEffect는 언제 쓰는 게 맞나

이 기준 하나만 지키면 사고의 80%는 사라진다.

useEffect는
React 외부 세계와 연결할 때만 쓴다

써도 되는 경우

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

  return () => clearInterval(id);
}, []);
 
useEffect(() => {
  window.addEventListener("resize", onResize);
  return () => window.removeEventListener("resize", onResize);
}, []);
 
useEffect(() => {
  fetchData();
}, []);

공통점이 있다.

  • DOM
  • 타이머
  • 네트워크
  • 브라우저 API

👉 React가 직접 관리하지 않는 영역


6️⃣ effect가 많다는 건 구조가 흔들린 신호다

실무 기준으로 보면 이렇다.

  • effect 1~2개 → 정상
  • effect 4~5개 → 구조 점검 필요
  • effect가 상태 간 동기화 역할 → 거의 확실히 잘못됨
 
useEffect(() => {
  setTotal(price * count);
}, [price, count]);

이건 effect 문제가 아니라
state 설계 문제다.

 
const total = price * count;
const total = price * count;

이게 맞다.


7️⃣ 기억해야 할 한 문장

이 문장 하나로 useEffect 판단이 끝난다.

렌더링 결과를 만들기 위해 effect를 쓰고 있다면
그 컴포넌트는 이미 설계가 틀어졌다

useEffect는

  • “계산”이 아니라
  • **“연결”**을 위한 도구다.

다음 글 예고

다음 글에서는 이걸 다룬다.

👉 “상태는 어디에 둬야 하는가”

  • state를 위로 올리는 기준
  • props drilling이 언제 문제인지
  • 전역 상태가 왜 마지막 선택지인지

React에서 제일 어려운 결정이자
제일 많은 버그를 만드는 주제다.

댓글