트러블 슈팅 - 검색 기능

skyblue·2023년 6월 29일
0

프로젝트

목록 보기
4/9

검색 기능 로직은, 입력값이 변하면 API에 요청해서 데이터를 받아오고, 재렌더링하는 방식이었다.

test를 검색할 땐, t, te, tes를 제외한 test 검색 결과만 렌더링하면 된다.

그렇지 않았을 땐, 성능면에서도 시각적으로도 좋지 않다.

따라서, setTimeout 함수를 활용했다.
200밀리초 이전에 입력값이 변하면 clearTimeout 함수로 타이머를 취소했다.

이는 잘 동작하는 것처럼 보였으나, 추후 버그를 발견했다.

test1을 검색했을 때, 최종으로 t에 대한 검색 결과가 렌더링되었다.

console.log로 원인을 찾아보니, 200밀리초가 지나고 setTimeout이 실행된 경우, 함수가 취소되지 않기 때문이다.

이게 왜 문제가 될까?
검색 결과가 많은 순은 t>te>tes>test다.
따라서, API로 데이터를 받아오는 속도가 t가 test보다 느리며, test 검색 결과 요청보다 t검색 결과 요청이 먼저였어도, t검색 결과 데이터가 더 늦게 도착하는 경우가 발생했다. 비동기 통신이기 때문이다.
처음엔 어떻게 하면 test 이전 입력값에 대한 API요청 함수를 모두 취소할 수 있을까?를 계속 고민했다.
그러나 방법을 찾지 못해, 다르게 접근했다.
현재 입력값과 API 요청에 보낸 검색 키워드가 다를 경우, 얼리리턴을 했다.

이를 구현하면서 리액트를 좀 더 이해하게 되었다.
현재 시점에 useState 값이 바뀌었어도, 함수 안에서 값은 여전히 그 함수가 실행된 시점의 값이다.
그래서 keyword와 현재 인풋의 value를 비교하면 됬다.

const onTyping = () => {
  // 생략(fetch)
  if (keyword !== searchInp.current.value) {
    return;
  }
  // 생략
}

useEffect(() => {
  const timeout = setTimeout(() => {
    onTyping();
  }, 200);

  return () => {
    clearTimeout(timeout);
  };
}, [keyword]);

return (
  <input
  	ref={searchInp}
	value={keyword}
	onChange={(e) => setKeyword(e.target.value)}
	/>
)

핵심 코드는(많이 생략한 코드) 위와 같다.

자세한 코드는 아래 링크에서 볼 수 있다.
https://github.com/FRONTENDSCHOOL5/final-08-Off-field-baseball/pull/220

0개의 댓글