Next.js
React에서 useEffect 배포 문제를 디버깅하기
useEffect는 로컬에서 완벽해도 배포 환경에서 다르게 작동할 수 있다. 환경 차이를 먼저 확인해야 한다.
React의 useEffect는 강력하지만, 부작용(side effect)이 여러 번 실행되거나 예상과 다르게 작동할 수 있다. 특히 로컬 환경과 배포 환경에서 다르게 보일 때가 있다.
프로덕션 빌드로 재현
먼저 프로덕션 빌드 환경에서 문제를 재현해야 한다:
npm run build
npm start # 또는 프로덕션 환경 서버 실행
Dev 모드에서는 Strict Mode로 인해 effect가 두 번 실행된다. 이것은 의도적이지만, 실제 프로덕션에서는 한 번만 실행된다. 만약 프로덕션에서만 문제가 생기면, 이 동작 차이 때문일 가능성이 높다.
실제 API 응답 확인
로컬에서는 mock API를 사용하지만, 배포 환경에서는 실제 API 응답이 다를 수 있다:
useEffect(() => {
console.log('데이터 요청 시작');
fetch(API_ENDPOINT)
.then(res => res.json())
.then(data => {
console.log('받은 데이터:', data);
setData(data);
});
}, [API_ENDPOINT]);
브라우저 개발자 도구의 Network 탭에서 실제 API 응답을 본다. 응답 시간이 로컬보다 오래 걸리면, effect가 여러 번 실행되거나 race condition이 발생할 수 있다.
의존성 배열 검토
Effect의 의존성이 정확한지 확인한다:
// 문제: 렌더링할 때마다 실행됨
useEffect(() => {
fetchData();
}); // 의존성 배열 없음
// 올바름: 한 번만 실행
useEffect(() => {
fetchData();
}, []); // 빈 배열
// 조건부 실행
useEffect(() => {
if (userId) {
fetchUserData(userId);
}
}, [userId]);
의존성 배열을 비우면 컴포넌트 마운트 시에만 실행되고, 배열에 값을 넣으면 그 값이 바뀔 때마다 실행된다. 특정 조건에서만 effect를 실행해야 하면, 조건을 effect 내부에 넣는다.
모바일 환경에서의 네트워크 흐름
모바일에서는 네트워크 상황이 예측 불가능하다:
- 초기 로딩 후 네트워크가 끊길 수 있다.
- API 응답이 예상보다 오래 걸릴 수 있다.
- 요청이 여러 번 중복으로 보내질 수 있다.
타임아웃을 설정하고, 이전 요청을 정리하는 cleanup 함수를 항상 포함한다:
useEffect(() => {
let isMounted = true;
const controller = new AbortController();
fetch(API_ENDPOINT, { signal: controller.signal })
.then(res => res.json())
.then(data => {
if (isMounted) setData(data);
});
return () => {
isMounted = false;
controller.abort();
};
}, []);
단계별 확인
- 증상을 정확히 기록한다. 어떤 UI가 예상과 다른가?
- 브라우저 콘솔과 Network 탭에서 요청/응답을 추적한다.
- 로컬 개발 환경과 프로덕션 빌드 환경에서의 동작을 비교한다.
마무리
Effect의 렌더링 흐름을 완벽히 이해하면, 배포 후 버그를 크게 줄일 수 있다. 특히 프로덕션 환경에서 실제 데이터로 테스트하는 습관이 중요하다.