CRA에서 Vite로 옮기고 React 18로 올린 프로젝트인데요, 컴포넌트 마운트 시 데이터 받아오려고 useEffect 안에서 fetch를 하는데 네트워크 탭을 보면 요청이 딱 두 번씩 나갑니다. 뎁스 배열은 빈 배열 []로 넣었고요.
이상한 건 npm run build로 빌드해서 띄우면 요청이 한 번만 나가요. 개발 서버로 돌릴 때만 두 번입니다. 처음엔 제 로직이 어디서 두 번 호출하나 싶어서 콘솔 찍어보고 별짓 다 했는데 코드상으로는 분명 한 번이거든요.
useEffect(() => {
fetch(`/api/orders/${id}`)
.then(r => r.json())
.then(setData)
}, [])혹시 StrictMode랑 관련이 있나 싶어서 검색은 해봤는데 정확히 왜 이러는지, 그리고 실무에서 이걸 그냥 둬도 되는 건지 판단이 안 서네요. POST 같은 거였으면 큰일 날 뻔했는데 😅
댓글 2
이게 버그가 아니라 의도된 동작입니다. "이 effect가 여러 번 마운트돼도 안전하냐(=cleanup을 제대로 짰냐)"를 개발 단계에서 강제로 검사해주는 거예요. 그래서 대응 방향은 두 번 호출을 막는 게 아니라, cleanup을 제대로 넣어서 두 번 실행돼도 문제가 없게 만드는 겁니다.
fetch라면 AbortController를 쓰는 게 정석이에요. effect 안에서 controller 만들고 signal을 fetch에 넘긴 다음, cleanup에서 controller.abort() 호출하면 첫 요청이 취소되고 마지막 요청 결과만 반영됩니다. 이렇게 해두면 컴포넌트가 빠르게 마운트/언마운트될 때 응답 순서가 꼬여서 옛 데이터가 최신을 덮어쓰는 race condition도 같이 막아줘요.
참고로 StrictMode를 그냥 꺼버리는 분들도 있는데, 개발 편의로 눈속임하는 거라 권하진 않습니다. 실제로 이 이중 호출이 드러내주는 버그(구독 정리 안 함, 타이머 안 지움 같은)가 프로덕션에서 터지는 경우가 많거든요. POST처럼 부수효과가 있는 건 애초에 effect보다 사용자 액션(onClick 등) 핸들러에 두는 게 맞습니다.