← 전체 글로 돌아가기

웹 개발

검색창 디바운스를 넣었는데도 요청이 두 번씩 나가던 구조

디바운스된 검색어와 페이지 번호를 따로 관리하면서 동시에 두 effect가 트리거되던 문제를 상태를 묶어 해결했다.

문제 발견

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

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

중복을 만든 구조

대략 이런 흐름이었다.

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

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

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

검색 조건을 하나로 통일했다

검색 조건을 하나의 객체로 보고, 실제 요청을 만드는 기준을 한 군데로 모았다.

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이 연속으로 찍히는지 확인했다.

배운 점

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