← 전체 글로 돌아가기

Next.js

React 검색창의 디바운스를 취소하지 않아 오래된 결과가 덮어쓴 문제

입력마다 요청 수는 줄었지만 이전 검색 결과가 화면을 덮었다. 취소와 최신 요청 판별을 적용한 짧은 튜토리얼.

요청 수는 줄었는데 결과가 틀렸다

검색창에 디바운스 300ms를 넣고 나서 서버 부하는 줄었다. 하지만 rea를 입력했다가 빠르게 react까지 입력하면 가끔 rea 결과가 마지막에 보였다. 느린 이전 응답이 빠른 최신 응답보다 늦게 도착한 경우였다.

타이머와 네트워크 요청은 다른 문제다

clearTimeout은 아직 실행하지 않은 콜백만 지운다. 이미 시작한 fetch는 취소하지 않는다. 그래서 effect가 바뀔 때 AbortController를 만들고 정리 함수에서 중단했다.

useEffect(() => {
  if (!query.trim()) return

  const controller = new AbortController()
  const timer = setTimeout(async () => {
    try {
      const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`, {
        signal: controller.signal,
      })
      setResults(await res.json())
    } catch (error) {
      if ((error as Error).name !== 'AbortError') throw error
    }
  }, 300)

  return () => {
    clearTimeout(timer)
    controller.abort()
  }
}, [query])

빈 값과 로딩 상태도 따로 처리했다

처음에는 빈 입력일 때 이전 결과가 남았다. 요청을 보내지 않는 경우에도 결과와 로딩을 함께 초기화해야 화면의 상태가 맞는다. 또한 취소된 요청을 오류 토스트로 보여주지 않았다.

확인은 네트워크 탭에서 했다

  • 글자를 빠르게 추가·삭제할 때 마지막 쿼리만 화면에 남는가
  • 취소된 요청이 오류 UI를 띄우지 않는가
  • 한글 조합 중에는 필요하면 IME 조합 상태를 별도로 고려했는가

디바운스는 성능 기능이고, 응답 순서 보장은 별도 기능이라는 구분이 이 문제를 끝냈다.