React 18 업그레이드 후 useEffect 두 번 실행 문제
문제 상황
지난주 React 18로 업그레이드를 진행했다. 배포 후 개발 환경에서 API 호출이 두 번씩 발생하는 이슈를 발견했다.
useEffect(() => {
fetchUserData(userId);
}, [userId]);
개발 서버에서 이 코드가 마운트 시 두 번 실행되면서 불필요한 네트워크 요청이 발생했다.
원인 분석
React 18의 Strict Mode가 컴포넌트를 의도적으로 마운트-언마운트-재마운트하는 동작을 추가했다. 향후 도입될 기능들(Offscreen API 등)을 대비한 변경사항이었다.
공식 문서를 확인하니 개발 환경에서만 발생하는 의도된 동작이었다. 프로덕션에는 영향이 없다.
해결 방법
1. Cleanup 함수 추가
useEffect(() => {
let cancelled = false;
fetchUserData(userId).then(data => {
if (!cancelled) {
setUser(data);
}
});
return () => {
cancelled = true;
};
}, [userId]);
2. AbortController 활용
useEffect(() => {
const controller = new AbortController();
fetch(`/api/users/${userId}`, {
signal: controller.signal
})
.then(res => res.json())
.then(setUser)
.catch(err => {
if (err.name !== 'AbortError') {
console.error(err);
}
});
return () => controller.abort();
}, [userId]);
교훈
React 18 이전에도 cleanup 함수를 작성하는 게 권장사항이었지만, 실제로 제대로 구현하지 않은 코드가 많았다. 이번 변경으로 side effect 관리의 중요성을 다시 느꼈다.
팀 내 다른 프로젝트들도 비슷한 이슈가 있을 것으로 예상되어 가이드 문서를 작성해 공유했다.