문제 발생
요청 경쟁 상태는 사용자가 검색어를 ‘자바’에서 ‘자바스크립트’로 빠르게 바꿀 때 드러납니다. 먼저 보낸 ‘자바’ 응답이 늦게 도착하면 최신 결과를 덮을 수 있으며, 요청 순서와 응답 순서는 같다고 보장되지 않습니다.
검색어가 빠르게 바뀌면 이전 response가 늦게 도착해 최신 state를 덮을 수 있어 abort나 request identity 확인이 필요합니다.
원인 분석
Effect가 다시 실행돼도 이미 시작한 비동기 작업은 자동 취소되지 않습니다. cleanup 없이 모든 응답에서 setResults를 호출하면 마지막으로 도착한 응답이 화면을 결정합니다. React 개발 환경의 Strict Mode가 setup과 cleanup을 한 번 더 실행하는 것은 이 수명주기 오류를 찾는 데 도움이 되며 운영 중복 요청의 원인으로 단정하면 안 됩니다.
해결 방안
오래된 결과가 state를 바꾸지 못하도록 cleanup에서 request identity를 무효화합니다. 지원되는 API라면 AbortController도 함께 사용해 불필요한 전송을 줄이되, abort가 서버에서 이미 반영된 작업까지 되돌린다고 가정하지 않습니다.
useEffect(() => {
let ignore = false;
const controller = new AbortController();
fetch(`/api/search?q=${encodeURIComponent(query)}`, {
signal: controller.signal,
})
.then((response) => {
if (!response.ok) throw new Error("검색 실패");
return response.json();
})
.then((data) => {
if (!ignore) setResults(data);
})
.catch((error) => {
if (!ignore && error.name !== "AbortError") setError(error);
});
return () => {
ignore = true;
controller.abort();
};
}, [query]);
느린 이전 응답과 빠른 최신 응답을 의도적으로 만들어 최신 결과만 남는지, unmount 뒤 state 변경이 없는지 확인합니다. framework가 제공하는 data fetching 기능이 있다면 cache, request deduplication과 Server Rendering 지원도 함께 검토합니다.
공식 문서
현재 React와 사용 중인 framework의 data fetching 계약에서 다시 확인합니다.