React 비동기 데이터

useEffect 요청은 cleanup, abort, 최신성 gate가 한 묶음이다

dependency가 바뀔 때마다 새 fetch를 시작하는 것보다, 이전 요청을 어디서 끊고 늦게 도착한 응답을 어디서 버리는지 정하는 것이 더 중요하다.

요청 수명 주기 지도

race control
useEffect fetch cleanup과 stale response 차단 흐름 dependency 변경이 새 요청과 AbortController를 만들고 이전 요청은 cleanup에서 abort되며, response는 요청 ID와 mounted 상태 gate를 통과할 때만 state에 commit된다. dependency userId / filter effect run requestId + abort fetch in flight network latency fresh gate latest id 확인 setState commit cleanup previous abort stale response late arrival ignore branch no state write error branch AbortError 분리 cleanup 없는 요청은 unmount 이후에도 도착한다 최신성 gate를 통과한 응답만 화면에 반영한다
trigger dependency snapshot

userId, filter처럼 effect를 다시 실행할 값을 요청 ID와 함께 고정한다.

effect AbortController 생성

fetch에 signal을 넘기고 cleanup에서 같은 controller를 abort한다.

race 이전 응답이 늦게 도착

requestId가 최신이 아니면 성공 응답이어도 state commit을 건너뛴다.

commit fresh response만 setState

mounted, requestId, abort 상태가 맞는 응답만 화면에 반영한다.

상태 전이 레인

effect contract
loading
새 요청 시작 기존 error/data를 유지할지 비울지 UI 정책을 먼저 정한다.
setStatus
abort
cleanup에서 이전 요청 취소 AbortError는 사용자에게 보여줄 실패가 아니라 교체 신호다.
controller.abort()
fresh
최신 요청만 반영 느린 이전 응답이 최신 data를 덮어쓰지 못하게 한다.
requestIdRef
unmount
컴포넌트가 사라진 뒤 state 차단 cleanup 로그와 abort signal로 수명 주기 종료를 확인한다.
return cleanup

요청 방어 장치

state guard
AbortController 네트워크와 body parsing을 가능한 지점에서 중단한다.
request ID abort가 늦거나 무시되어도 최신성 판단을 유지한다.
status model loading, success, empty, error, aborted를 같은 boolean으로 섞지 않는다.
stale write 차단 성공 응답이라도 오래된 요청이면 data와 error 모두 쓰지 않는다.
stale 느린 이전 응답 사용자가 빠르게 조건을 바꿀 때 이전 결과가 최신 화면을 덮는다.
abort 취소와 실패 혼동 AbortError를 일반 error toast로 보여주면 정상 교체가 장애처럼 보인다.
unmount 사라진 화면에 setState 컴포넌트 수명이 끝난 뒤 state write가 경고와 메모리 누수 신호를 만든다.
cache 같은 요청 중복 dedupe나 query cache 없이 같은 dependency 요청을 반복하면 UX가 흔들린다.
검증 질문

요청 수명 주기는 “언제 fetch하는가”가 아니라 “어떤 응답을 믿는가”로 점검한다.

  • dependency가 바뀔 때 이전 요청이 cleanup에서 실제로 abort되는가?
  • AbortError와 네트워크 실패가 UI 상태에서 구분되는가?
  • 늦은 성공 응답이 최신 data와 error를 덮지 못하게 막는가?