이번 글은 사이드 이펙트로 인하여 refetch에 관한 문제를 다뤄보려고 합니다
문제

이게 pull 받고 난 후 상황인디여
refetch를 컴포넌트 본문에서 호출하다 보니, 리렌더링이 일어날 때마다 서버에 요청이 쏟아졌습니다.

이렇게 결과는
- 버튼 하나만 눌러도 네트워크 탭에 수십 개의 요청이 찍히고,
- 서버는 불필요한 트래픽으로 과부하가 걸렸습니다.
- 화면도 계속 깜빡이면서 UX가 완전히 망가졌습니다.
왜 이런 문제가 생길까?
핵심은 React의 컴포넌트 렌더링 방식 때문입니다.
- React 컴포넌트는 상태나 props가 바뀌면 리렌더링 됩니다.
- 리렌더링될 때마다 함수 본문이 다시 실행됩니다.
- 그런데 그 안에서 refetch()를 호출하면?
→ 렌더링 = 네트워크 요청 이 되게 됩니다
즉, refetch는 조건부로 실행되어야 하는 함수인데, 본문에 넣어버리면 사실상 자동 호출이 여러번 되어버립니다.
사이드 이펙트 관점에서 보기
이 문제를 더 본질적으로 설명하면, 결국 사이드 이펙트(side effect) 문제입니다.
- React 컴포넌트는 기본적으로 **순수 함수(pure function)**처럼 동작해야 합니다.
- 같은 props와 state가 들어오면 항상 같은 UI를 반환해야 하죠.
- 하지만 refetch는 네트워크 요청을 발생시키는 함수 → 부수 효과에 해당합니다.
- 이런 부수 효과를 컴포넌트 본문에 직접 넣어버리면,
- 매 렌더링마다 네트워크 요청이 발생하고,
- 컴포넌트의 순수성이 깨져서 예측 불가능한 동작이 이어집니다.
즉, refetch는 컴포넌트 본문이 아니라 사이드 이펙트를 관리하는 영역(useEffect, 이벤트 핸들러 등) 안에서 실행해야 안전합니다.
해결 방법: useEffect + 의존성 배열
저는 이 문제를 해결한 방법은 useEffect를 택했어요
refetch가 특정 조건이 만족될 때만 실행되도록 제어하면 문제는 간단히 해결 할 수 있습니다
// 예시 코드
function UserProfile({ userId }: { userId: string }) {
const { data, refetch } = useQuery(['user', userId], () => fetchUser(userId));
// ✅ userId가 바뀔 때만 refetch 실행
useEffect(() => {
if (userId) {
refetch();
}
}, [userId, refetch]);
return <div>{data?.name}</div>;
}
이렇게 하면 userId가 바뀔 때만 refetch가 실행되고, 나머지 경우에는 쓸데없는 서버 요청이 발생하지 않습니다.
덕분에 서버 부하도 줄고, UI도 안정적으로 동작했습니다.
더 나은 대안: enabled 옵션
사실 많은 경우엔 refetch조차 필요 없을 때가 많습니다.
React Query는 enabled라는 옵션을 제공하는데 해당 옵션은 enable 조건이 있을때 알아서 refetch를 해준답니당
사용 예시는
const { data } = useQuery(['user', userId], () => fetchUser(userId), {
enabled: !!userId, // userId가 있을 때만 실행
});
이렇게 작성하면 userId가 없을 때는 요청 자체가 발생하지 않고, userId가 생기면 자동으로 fetch가 실행됩니다.
저는 이후 협업에서 이 방식을 더 권장하게 되었습니다.
정리
- refetch는 원래 “수동으로 데이터를 다시 불러오기” 위한 도구입니다.
- 그런데 컴포넌트 본문에 넣으면 리렌더링마다 호출되어 서버 요청이 폭발합니다.
- 반드시 useEffect와 의존성 배열을 통해 제어하거나, 아예 enabled 옵션으로 대체하는 게 좋습니다.
- 협업에서는 **“refetch는 반드시 조건부로만 사용한다”**라는 팀 규칙을 두는 게 안전합니다.
- 사용하실 경우엔 꼭 함수 안에서 사용해주세용
다들 오늘 하루도 고생 많으셨고 즐거운 코딩 하십시요~