React 18 업그레이드 후 useEffect 두 번 실행 이슈
문제 상황
프로젝트를 React 18로 업그레이드한 후, 개발 환경에서 useEffect 내부의 API 호출이 두 번씩 발생하는 것을 확인했다. 로그를 찍어보니 마운트/언마운트/재마운트 패턴으로 실행되고 있었다.
useEffect(() => {
console.log('mount');
fetchUserData();
return () => console.log('unmount');
}, []);
// 출력:
// mount
// unmount
// mount
원인
React 18의 Strict Mode가 강화되면서, 컴포넌트의 재사용성을 테스트하기 위해 의도적으로 마운트/언마운트를 시뮬레이션하도록 변경되었다. 이는 향후 추가될 기능(컴포넌트 상태 보존)을 대비한 것으로, 개발 환경에서만 발생한다.
해결 방법
1. cleanup 함수 제대로 구현
useEffect(() => {
const controller = new AbortController();
fetchUserData({ signal: controller.signal })
.then(data => setUser(data))
.catch(err => {
if (err.name !== 'AbortError') {
console.error(err);
}
});
return () => controller.abort();
}, []);
2. ref로 중복 실행 방지 (비추천)
const fetched = useRef(false);
useEffect(() => {
if (fetched.current) return;
fetched.current = true;
fetchUserData();
}, []);
이 방식은 Strict Mode의 의도를 우회하는 것이라 권장되지 않는다. cleanup 함수를 제대로 구현하는 것이 올바른 접근이다.
결론
처음엔 버그인 줄 알았지만, React 팀이 의도한 동작이었다. 덕분에 기존 코드에서 cleanup을 제대로 안 하던 부분들을 찾아낼 수 있었다. 프로덕션에서는 발생하지 않으니 당황하지 말고, 이 기회에 side effect 처리를 점검하면 좋겠다.