useEffect의 의존성 배열과 상태(State) 업데이트 로직이 잘못 맞물릴 때 발생합니다.불안정한 의존성 (Unstable Dependencies):
useEffect의 의존성 배열에 객체나 함수가 포함될 경우, 이들은 컴포넌트가 리렌더링될 때마다 새로운 참조(메모리 주소)를 갖게 됩니다.useEffect는 의존성의 참조가 변경되었다고 인식하여, 실제 내용이 같더라도 콜백 함수를 불필요하게 재실행합니다. 이 콜백 함수가 다시 상태를 변경하면 무한 루프가 발생합니다.상태 업데이트 로직의 부작용:
useEffect 내부에서 상태를 업데이트하는 로직이, 다시 그 useEffect를 촉발시키는 의존성 값을 변경하는 경우 무한 렌더링이 발생합니다.정리되지 않은 이벤트 리스너/타이머:
useRef를 이용한 플래그(Flag) 관리:loadingRef: API 호출이 시작될 때 true로 설정하고, 완료되면 false로 설정하는 "잠금(Lock)" 역할을 하는 ref를 둡니다. API 호출 전에 이 플래그를 확인하여, 이미 호출이 진행 중이면 새로운 호출을 막습니다.lastFetchedRef: 마지막으로 성공적으로 데이터를 가져온 조건(e.g., 연도, 월)을 ref에 저장합니다. 다음 요청 시, 이 ref의 값과 현재 요청 조건이 동일하면 API 호출을 건너뛰어 캐싱(Caching)과 유사한 효과를 냅니다.useState가 아닌 useRef인가?: ref의 값 변경은 리렌더링을 유발하지 않으므로, 이러한 메타데이터(플래그, 캐시 정보)를 관리하는 데 이상적입니다.useEffect와 useCallback 최적화useEffect가 꼭 필요할 때만 실행되도록 제어합니다.useCallback으로 함수 메모이제이션:
useEffect의 의존성 배열에 포함시켜야 하는 함수는 useCallback으로 감싸서, 해당 함수가 의존하는 값이 변경될 때만 함수가 재생성되도록 합니다. 이를 통해 useEffect에 안정적인 참조를 제공할 수 있습니다.의존성 배열 최소화:
useEffect의 의존성 배열에는 반드시 필요한 최소한의 값만 포함해야 합니다. 불필요한 의존성을 제거하여 의도치 않은 재실행을 방지합니다.이벤트 리스너 정리 (Cleanup Function):
useEffect에서 window나 document에 이벤트 리스너를 등록했다면, 반드시 반환(return)하는 정리 함수 내에서 removeEventListener를 호출해야 합니다. 이는 컴포넌트가 언마운트되거나 useEffect가 재실행되기 전에 이전 리스너를 제거하여 메모리 누수와 중복 실행을 방지합니다.React.memo: props가 변경되지 않은 자식 컴포넌트의 불필요한 리렌더링을 방지하는 가장 기본적인 최적화 도구입니다.useMemo: 복잡하고 비용이 큰 계산 결과를 메모이제이션하여, 의존성이 변경될 때만 재계산하도록 합니다. 필터링이나 정렬된 리스트를 파생 상태로 만들 때 유용합니다.react-window, react-virtualized) 초기 렌더링 속도를 획기적으로 개선합니다.useEffect에 포함된 함수/객체)과 상태 업데이트 로직의 충돌로 인해 발생합니다.useRef를 플래그로 활용하여 API 호출의 중복을 방지하고, useCallback으로 함수를 메모이제이션하여 useEffect에 안정적인 의존성을 제공하는 것이 핵심 해결책입니다.React.memo, useMemo, 가상화는 애플리케이션의 규모가 커졌을 때 고려할 수 있는 추가적인 성능 최적화 도구입니다.