Next.js
React 글로벌 로딩 경계에서 늦게 온 응답을 막은 기준
늦게 도착한 검색 응답이 화면을 덮어쓴 현상에서 상태를 분리해 해결한 기록입니다.
화면이 멈춘 것처럼 보였던 시작
검색 페이지에 전환 로딩을 붙인 뒤, 빠르게 a 다음 ab를 입력하면 가끔 a 결과가 마지막에 남았습니다. 로딩 UI가 있으니 요청도 안전할 것이라 생각한 것이 헷갈린 지점이었습니다. 로딩은 표시일 뿐, 먼저 시작한 요청을 취소하지 않습니다.
재현부터 단순하게 만들기
개발자 도구 Network에서 Slow 3G를 걸고 입력을 두 번 바꿨습니다. 응답 순서가 바뀌는 것을 확인한 뒤 최신 요청 번호만 결과를 반영했습니다.
const requestId = useRef(0);
async function search(q: string) {
const id = ++requestId.current;
setPending(true);
const data = await fetch(`/api/search?q=${encodeURIComponent(q)}`).then(r => r.json());
if (id === requestId.current) setItems(data.items);
if (id === requestId.current) setPending(false);
}
AbortController도 함께 쓴 이유
번호 비교만으로 화면은 맞지만 서버에는 예전 요청이 남습니다. 입력이 바뀔 때 이전 fetch를 취소하니 로그와 비용도 덜 흔들렸습니다. 단, 취소 예외를 오류 토스트로 보이지 않게 분리해야 했습니다.
controller.current?.abort();
controller.current = new AbortController();
await fetch(url, { signal: controller.current.signal });
다음 폼에 적용할 점
- 로딩 상태와 최신 데이터 여부를 같은 의미로 두지 않는다.
- 느린 네트워크에서 응답 순서를 실제로 바꿔 본다.
- 취소된 요청은 사용자 오류로 기록하지 않는다.
이 경우에는 전역 로딩 경계를 더 크게 만드는 대신 요청 하나의 생명주기를 관리하는 쪽이 수정 범위가 작았습니다.