🔍 검색 키워드: React useEffect 무한루프, useEffect 의존성 배열, React hooks 성능 최적화, useEffect 콘솔 로그, 클로저 이슈
React useEffect 무한루프 문제 해결하기
useEffect는 강력하지만, 잘못 사용하면 무한 루프에 빠질 수 있습니다. 특히 의존성 배열이 없거나 잘못 설정되면 매번 렌더링 후에 effect가 실행되어 상태를 계속 변경하는 악순환에 빠집니다. 이 글에서는 증상부터 원인, 그리고 해결 방법까지 실제 코드로 설명하겠습니다.
1. 증상: 콘솔 로그가 무한정 출력된다
의존성 배열 없이 useEffect를 작성하면 다음과 같이 작동합니다.
useEffect(() => {
console.log('effect 실행됨');
setCount(count + 1);
});
이 코드를 실행하면 콘솔에는:
- effect 실행됨
- effect 실행됨
- effect 실행됨
- (무한반복...)
이렇게 계속 출력되며, 브라우저가 느려지거나 먹통이 됩니다.
2. 원인 분석
의존성 배열이 없는 경우
useEffect(() => {
setCount(count + 1); // 상태 변경
}, []); // ❌ 의존성 배열 누락
의존성 배열을 생략하면 매번 렌더링 후마다 effect가 실행됩니다.
- 컴포넌트 렌더링
- useEffect 실행 → setCount 호출
- 상태 변경 → 컴포넌트 리렌더링
- useEffect 다시 실행
- 무한 반복...
의존성에 상태가 포함된 경우
useEffect(() => {
setCount(count + 1);
}, [count]); // ❌ count가 의존성에 포함됨
이 경우도 같은 문제가 발생합니다:
- count 변경 → effect 실행
- setCount(count + 1) → count 업데이트
- count 의존성이 변경됨 → effect 다시 실행
- 무한 루프...
3. 해결 방법
방법 1: 빈 의존성 배열 사용
마운트 시에만 한 번 실행하려면:
useEffect(() => {
// API 호출, 리스너 등록 등
fetchData();
}, []); // ✅ 빈 배열: 마운트 시에만 실행
방법 2: 정확한 의존성 명시
실제 변경 감지할 항목만:
useEffect(() => {
setDerivedValue(count * 2);
}, [count]); // ✅ count 변경 시에만 effect 실행
방법 3: 상태 업데이트 함수 패턴
이전 상태에 기반해 업데이트:
useEffect(() => {
setCount(prev => prev + 1);
}, []); // ✅ 의존성 없이도 안전 (단, 무한루프 원하지 않으면 피하기)
방법 4: useCallback으로 함수 메모이제이션
함수가 의존성이 되는 경우:
const handleUpdate = useCallback(() => {
setCount(count + 1);
}, [count]);
useEffect(() => {
handleUpdate();
}, [handleUpdate]); // 필요한 경우만 실행
방법 5: useRef로 초기 실행 방지
조건부 effect 실행:
const isFirstRender = useRef(true);
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
setCount(count + 1); // 마운트 후에만 실행
}, [count]);
4. 빠른 진단 가이드
| 증상 | 원인 | 해결책 |
|---|---|---|
| 무한 콘솔 로그 | 의존성 배열 누락 | 빈 배열 추가 [] |
| 특정 상태 변경 시 무한 루프 | 자신을 변경하는 상태가 의존성에 포함됨 | 의존성에서 제거하거나 함수 패턴 사용 |
| 마운트 후 특정 시점에만 실행 원함 | 조건 체크 없음 | useRef 또는 상태 플래그로 조건 추가 |
| 객체/배열 의존성으로 매번 실행됨 | 새 객체/배열 매번 생성됨 | useMemo 또는 상수로 변경 |
5. 실전 예제
API 호출 + cleanup 패턴 (무한루프 없음):
useEffect(() => {
let isMounted = true;
fetchUserData(userId).then(data => {
if (isMounted) setUser(data);
});
return () => {
isMounted = false; // cleanup
};
}, [userId]); // userId 변경 시에만 새로 fetch
의존성 배열 체크
[]→ 마운트 시에만 1회 실행[dep1, dep2]→ dep1 또는 dep2 변경 시 실행- 없음 → 매번 렌더링 후 실행 (⚠️ 위험)
useEffect의 무한루프는 React 개발에서 가장 흔한 실수입니다. 항상 의존성 배열을 명시하고, 그 안에는 실제 변경을 감지해야 할 항목만 넣으세요. 이 규칙만 지켜도 대부분의 성능 문제를 예방할 수 있습니다.
댓글 없음:
댓글 쓰기