React Hook useEffect has a missing dependency 완벽 해결법
React 개발을 하다 보면 ‘React Hook useEffect has a missing dependency‘라는 경고 메시지를 자주 마주하게 됩니다. 이 경고는 ESLint의 exhaustive-deps 규칙에 의해 발생하며, useEffect 훅 내부에서 사용하는 변수나 함수를 의존성 배열에 포함시키지 않았을 때 나타납니다. 많은 개발자들이 이 경고를 무시하거나 잘못된 방법으로 해결하려고 시도하지만, 제대로 이해하고 해결하지 않으면 예기치 않은 버그와 성능 문제로 이어질 수 있습니다. 이 글에서는 이 에러의 원인부터 해결 방법, 그리고 예방법까지 상세하게 다루어 보겠습니다.
🤖 AI 에러 분석 도우미
이 에러는 다음과 같은 상황에서 주로 발생합니다:
- 코드 문법 오류가 있을 때
- 라이브러리나 의존성 문제
- 환경 설정이 잘못된 경우
- 타입 불일치 문제
💡 위 해결법을 순서대로 시도해보세요. 90% 이상 해결됩니다!
에러 상세 분석
🔗 관련 에러 해결 가이드
React Hook useEffect has a missing dependency 경고는 React 팀이 개발자들이 흔히 저지르는 실수를 방지하기 위해 만든 안전장치입니다. useEffect는 컴포넌트의 생명주기와 상태 변화에 반응하여 부수 효과(side effect)를 수행하는 훅입니다. 이 훅의 두 번째 인자인 의존성 배열은 React에게 ‘어떤 값이 변경되었을 때 이 effect를 다시 실행할지’를 알려주는 역할을 합니다.
문제는 effect 내부에서 외부 스코프의 변수나 함수를 사용하면서 의존성 배열에 포함시키지 않을 때 발생합니다. 이 경우 effect는 최초 렌더링 시의 값만 참조하게 되어(클로저), 변수가 업데이트되어도 effect는 오래된 값을 계속 사용하게 됩니다. 이를 ‘stale closure’ 문제라고 하며, 예측하기 어려운 버그의 원인이 됩니다. React의 ESLint 플러그인은 이러한 잠재적 문제를 미리 감지하여 경고를 발생시킵니다.
발생 원인 5가지
1. Props나 State를 의존성 배열에 누락
가장 흔한 원인은 useEffect 내부에서 사용하는 props나 state를 의존성 배열에 포함시키지 않는 것입니다. 컴포넌트가 리렌더링되면서 이러한 값들이 변경되어도 effect는 이전 값을 계속 참조하게 됩니다.
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, []); // userId가 누락됨
}
2. 컴포넌트 내부 함수를 의존성에서 제외
컴포넌트 내부에서 정의한 함수를 useEffect에서 호출하면서 의존성 배열에 포함시키지 않는 경우입니다. 함수 내부에서 state나 props를 참조한다면 반드시 의존성에 포함되어야 합니다.
function Counter() {
const [count, setCount] = useState(0);
const logCount = () => {
console.log(count);
};
useEffect(() => {
const timer = setInterval(logCount, 1000);
return () => clearInterval(timer);
}, []); // logCount가 누락됨
}
3. 객체나 배열의 속성 접근
객체나 배열의 특정 속성만 사용하면서 전체 객체를 의존성에 포함시키지 않는 경우입니다.
4. useCallback이나 useMemo로 감싸지 않은 함수/값
매 렌더링마다 새로 생성되는 함수나 객체를 의존성으로 사용할 때 메모이제이션하지 않으면 불필요한 effect 재실행이 발생합니다.
5. Context 값 사용
useContext로 가져온 값을 useEffect에서 사용하면서 의존성 배열에 포함시키지 않는 경우입니다.
해결방법 7가지 (코드 포함)
방법 1: 의존성 배열에 모든 의존성 추가
가장 기본적이고 권장되는 방법입니다. ESLint가 제안하는 대로 모든 의존성을 배열에 추가합니다.
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]); // userId 추가
}
방법 2: useCallback으로 함수 메모이제이션
함수를 의존성으로 사용해야 할 때 useCallback으로 감싸서 불필요한 재생성을 방지합니다.
function Counter() {
const [count, setCount] = useState(0);
const logCount = useCallback(() => {
console.log(count);
}, [count]);
useEffect(() => {
const timer = setInterval(logCount, 1000);
return () => clearInterval(timer);
}, [logCount]); // 메모이제이션된 함수 포함
}
방법 3: 함수를 useEffect 내부로 이동
함수가 useEffect에서만 사용된다면 내부로 옮겨서 의존성 문제를 해결할 수 있습니다.
function DataFetcher({ apiUrl }) {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch(apiUrl);
const json = await response.json();
setData(json);
};
fetchData();
}, [apiUrl]); // fetchData는 내부 함수이므로 의존성에 불필요
}
방법 4: 함수형 업데이트 사용
setState의 함수형 업데이트를 사용하면 현재 state를 의존성에서 제외할 수 있습니다.
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1); // count를 의존성에서 제외 가능
}, 1000);
return () => clearInterval(timer);
}, []); // count 불필요
}
방법 5: useMemo로 객체/배열 메모이제이션
객체나 배열을 의존성으로 사용할 때 useMemo로 감싸서 참조 동일성을 유지합니다.
function SearchResults({ filters }) {
const [results, setResults] = useState([]);
const memoizedFilters = useMemo(() => filters, [
filters.category,
filters.priceRange,
filters.sortBy
]);
useEffect(() => {
searchAPI(memoizedFilters).then(setResults);
}, [memoizedFilters]);
}
방법 6: useRef로 최신 값 참조
의존성에 포함시키지 않으면서도 최신 값을 참조해야 할 때 useRef를 활용합니다.
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
const latestMessage = useRef(message);
useEffect(() => {
latestMessage.current = message;
});
useEffect(() => {
const connection = createConnection(roomId);
connection.on('receive', (msg) => {
// latestMessage.current로 최신 값 접근
console.log('Current:', latestMessage.current);
});
return () => connection.disconnect();
}, [roomId]); // message를 의존성에서 제외
}
방법 7: ESLint 규칙 비활성화 (최후의 수단)
정말 필요한 경우에만 특정 라인에 대해 규칙을 비활성화합니다. 하지만 이는 권장되지 않으며, 주석으로 이유를 명확히 설명해야 합니다.
useEffect(() => {
// 초기 마운트 시에만 실행되어야 하는 로직
initializeApp();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
예방법과 베스트 프랙티스
1. ESLint 플러그인 활용: ‘eslint-plugin-react-hooks’를 프로젝트에 설치하고 활성화하여 자동으로 경고를 받으세요.
2. 의존성을 최소화: useEffect 내부에서 사용하는 변수를 최소화하면 의존성 관리가 쉬워집니다. 필요한 것만 effect 내부에서 사용하세요.
3. 단일 책임 원칙: 하나의 useEffect는 하나의 목적만 가지도록 작성하세요. 여러 로직을 분리하면 의존성 관리가 명확해집니다.
4. Custom Hook 활용: 복잡한 로직은 커스텀 훅으로 추출하여 재사용성과 가독성을 높이세요.
5. 타입스크립트 사용: TypeScript를 사용하면 의존성 누락으로 인한 타입 불일치를 컴파일 시점에 발견할 수 있습니다.
마무리
React Hook useEffect has a missing dependency 경고는 단순한 코드 스타일 문제가 아니라 잠재적인 버그를 예방하는 중요한 안전장치입니다. 이 경고를 무시하거나 임시방편으로 해결하려 하지 말고, 근본적인 원인을 이해하고 올바른 방법으로 해결해야 합니다. 의존성 배열을 정확하게 관리하면 예측 가능하고 안정적인 React 애플리케이션을 구축할 수 있습니다. useCallback, useMemo, useRef 등의 훅을 적절히 활용하고, effect의 책임을 명확히 분리하는 습관을 들이세요. 처음에는 번거롭게 느껴질 수 있지만, 장기적으로는 유지보수가 쉽고 버그가 적은 코드를 작성하는 데 큰 도움이 됩니다.
📚 함께 읽으면 좋은 글
Warning: Each child in a list should have a unique “key” prop 완벽 해결법 – 원인부터 예방까지
📅 2025. 11. 20.
🎯 Warning: Each child in a list should have a unique “key” prop
React Hook useEffect has a missing dependency 완벽 해결법 – 원인부터 예방까지
📅 2025. 11. 20.
🎯 React Hook useEffect has a missing dependency
Cannot read properties of undefined (reading ‘map’) 완벽 해결법 – 원인부터 예방까지
📅 2025. 11. 19.
🎯 Cannot read properties of undefined (reading ‘map’)
Cannot read properties of undefined (reading ‘map’) 완벽 해결법 – 원인부터 예방까지
📅 2025. 11. 18.
🎯 Cannot read properties of undefined (reading ‘map’)
Warning: Each child in a list should have a unique “key” prop 완벽 해결법 – 원인부터 예방까지
📅 2025. 11. 17.
🎯 Warning: Each child in a list should have a unique “key” prop
💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!
📢 이 글이 도움되셨나요? 공유해주세요!
여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨
🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏
💬 여러분의 소중한 의견을 들려주세요!
이 글에서 가장 도움이 된 부분은 어떤 것인가요?
⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨
🔔 블로그 구독하고 최신 글을 받아보세요!
🌟 React 에러부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨
📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!