turin.blog
← 전체 글로 돌아가기

practical debugging note

React 검색창 디바운스를 넣고도 요청이 두 번 나가던 이유

검색창에 디바운스를 넣었는데도 네트워크 요청이 중복되던 원인을 useEffect 의존성과 초기 실행 기준에서 정리했습니다.

빠른 요약

문제를 발견한 장면 검색창에 300ms 디바운스를 넣었는데 개발자 도구 Network 탭에는 같은 검색어 요청이 두 번씩 찍혔다.

이 글에서 확인할 것
  • 문제를 발견한 장면
  • 중복을 만든 코드 모양
  • 고친 방향
  • 확인 방법
  • 짧은 회고

문제를 발견한 장면

검색창에 300ms 디바운스를 넣었는데 개발자 도구 Network 탭에는 같은 검색어 요청이 두 번씩 찍혔다. 처음에는 React Strict Mode 때문이라고 넘기려 했지만, 운영 빌드에서도 일부 상황에서 중복 요청이 보였다.

직접 헷갈렸던 부분은 debouncedKeyword가 바뀔 때 요청하는 effect와, 페이지 번호가 바뀔 때 요청하는 effect를 따로 둔 점이었다. 검색어를 바꾸면 페이지를 1로 돌리고, 그 변화가 다시 요청을 만들었다.

중복을 만든 코드 모양

대략 이런 흐름이었다.

useEffect(() => {
  setPage(1);
}, [debouncedKeyword]);

useEffect(() => {
  fetchItems({ keyword: debouncedKeyword, page });
}, [debouncedKeyword, page]);

검색어가 바뀌면 첫 번째 effect가 page를 바꾸고, 두 번째 effect가 검색어 변화와 page 변화에 각각 반응했다.

고친 방향

검색 조건을 하나의 객체로 보고, 실제 요청을 만드는 기준을 한 군데로 모았다. 검색어 변경 이벤트에서 page를 같이 정리하고, effect는 최종 조건만 바라보게 했다.

const [query, setQuery] = useState({ keyword: "", page: 1 });

function handleKeywordChange(value: string) {
  setQuery({ keyword: value, page: 1 });
}

useEffect(() => {
  const controller = new AbortController();

  fetchItems(query, { signal: controller.signal });

  return () => controller.abort();
}, [query.keyword, query.page]);

여기에 디바운스된 값은 handleKeywordChange로 들어가기 전에만 적용했다. 상태를 여러 군데에서 고치지 않는 게 더 중요했다.

확인 방법

수정 뒤에는 단순히 화면이 맞는지만 보지 않았다.

  1. 검색어 한 글자 입력
  2. 빠르게 지우기
  3. 다른 검색어 입력
  4. 페이지 2로 이동
  5. 다시 검색어 변경

이 순서로 Network 탭에서 같은 URL이 연속으로 찍히는지 확인했다.

짧은 회고

디바운스는 요청 횟수를 줄여주지만, 상태 변경 경로가 두 갈래면 중복을 완전히 막아주지 못했다. 검색 조건처럼 서로 묶여 움직이는 값은 처음부터 한 덩어리로 다루는 편이 초보자가 읽기에도 덜 복잡하다.