카테고리 없음

[트러블슈팅]reactQuery를 도입해서 api요청해서 받아온 데이터를 캐싱해보자

아보카도 있었어! 2023. 4. 21. 16:53

submit 이벤트가 발생할 때마다 네트워크 요청

 

 

[문제 분석]

1. 현재 onChange와 onSubmit 함수는 제대로 작동해서 input 값에 들어오는 값을 잘 감지하고 있다.

새로 바뀔 값(onChange)과 데이터를 요청할 값(onSubmit)이 동일하기 때문이다.

또한, 새로운 queryKey 또한, 그에 맞춰 잘 생성됨을 볼 수 있다.

 

gethub라는 입력값을 타이핑했을 때,

 

gethub에 대한 qeuryKey 생성 및 api 요청 성공적으로 받아옴(값이 stale인 이유는 캡쳐 타이밍 문제)

 

문제가 되는 부분은 검색결과이다. console에 찍힌 검색결과는 이전 queryKey값인 'stale'을 입력했을 때의 결과이다.

입력값을 stale로 하고 데이터를 요청했을 때의 데이터와 console이 동일한 것을 확인할 수 있다.

 

결과적으로 이런 현상이 발생하게 된다.

(프로필 이미지는 캡쳐를 위해 일부러 삭제했다.)

이전 'stale'에 대한 결과 값을 출력하는 현상

 

즉, api 요청해서 해당하는 데이터를 받아오는 것도, 캐시하는 것도 문제가 없지만, 이 데이터를 상태에 업데이트 시켜주는 부분에서 문제가 생긴 것이다.

 

[하고자 하는 것]

캐시된 데이터를 받아오면 해당 데이터를 상태끌어올려주는 함수 getUsers()의 인자로 전달해서 Main컴포넌트에 출력되는 값을 업데이트시키고자 한다.

 

  • 수정 이전의 코드

 

  1. useRef hook을 사용하여 searchWordRef라는 전역 변수를 지정하여, handleSubmit 함수 내에서 값을 변경할 수 있도록 하고 있다. => searchWordRef.current를 의존하는 코드
  2. searchWord는 리액트쿼리의 queryKey로, 검색어가 변경될 때마다 84째 줄에서 새로운 쿼리를 생성하도록 했다.

 

useQuery 훅

 

  1. queryKey는 searchWordRef.current를 포함한 배열이다.
  2. queryKey, 즉 검색어와 일치하는 캐싱 데이터가 없다면 queryFn의 비동기 함수를 실행하여 네트워크 요청을 한다.

 

[간과한 것]

  1. 리액트 쿼리의 queryFn의 실행 시점에 대한 이해가 부족했다.
  2. useRef에 대한 이해가 부족해 searchWordRef.current 의 값을 변경하는 것만으로 queryKey가 변경되는 줄 알았다. => searchWordRef.current의 값을 변경시키는 것만으로는 queryKey가 생성되지 않았다.
    • 사실은 handleSubmit 내에서 prop으로 받아온 함수의 94째 줄 getUsers()를 호출해서 부모 컴포넌트를 업데이트 시키면서 리렌더링 되는 거였고, 리렌더되면서 useQuery가 재호출되는 것이었다. 해당부분을 주석 처리하면, 리렌더링이 일어나지 않게 되면서 새로운 queryKey가 생성되지 않는다. 
  3. 이렇게 되면 당연히 이전의 검색어가 queryKey로 생성되면서 해당 검색어에 대한 네트워크 요청을 하게 되고, 이전의 검색 결과가 Main 컴포넌트에 전달되는 것이다.
  4. 또한 refetch 는 호출될 때 무조건 네트워크 요청을 한다.(새로운 데이터를 받아온다)

useState로 관리하고 있는 input값이 변경되면서 리렌더링이 일어나 이전 검색어에 대한 queryKey 생성

 

  • 수정 후 코드

useQuery 훅

 

  1. 우선 useQuery hook에 onSuccess 옵션을 추가했다.
    • new Data에 대한 네트워크 요청(캐싱되지 않은 새로운 검색어에 대한 결과 데이터)이 성공적으로 수행되었을 때 실행하는 함수로, 요청에 대한 결과 값을 인자(data)로 받는다.
    • 이 함수는 검색어에 대한 결과 값이 존재하는지 36째 줄에서 유효성 검사를 하고, 41째 줄에서 getUsers를 호출해 Main 컴포넌트에 결과 값을 전달한다.
    • 새로운 검색어에 대한 결과 값을 요청할 경우 해당 결과 값에 대해서는 컴포넌트의 업데이트가 정상적으로 수행된다.
  2. queryKey를 전역변수 searchWordRef.current  가 아닌 상태변수 searchWord로 관리한다.
    • gethub 프로젝트에서는 타이핑이 끝난 후 submit 이벤트가 일어나지 않아도, 해당 검색어에 대한 결과 값을 출력하는 경우와 submit 이벤트가 발생해도 결과 값을 출력하는 식으로 검색어가 들어오는 경우의 수가 두 가지이다. 두 가지 경우의 입력 값을 하나로 관리해야 했기 때문에, 상태변수를 활용하여 queryKey로 설정했다.
  3. setsearchWord의 상태변화 함수는 handleSubmit에서 호출된다.

setSearchWord 호출부

 

 

 

 

4. isLoading 변수로 현재 검색어의 결과에 대한 데이터가 캐싱 되었는지 아닌지 확인한다.

  • isLoading 변수는 queryKey에 대해 캐싱된 데이터가 있다면 로딩 여부에 상관없이 false를 반환한다.
  • 처음 실행하는 쿼리가 아니라면, getQueryData로 검색어와 일치하는 queryKey를 가진 캐싱된 데이터를 받아와서 getUsers를 호출해 Main 컴포넌트에 전달한다.

 

[개선해야 할 점]

  1. setSearchWord가 비동기 함수이기 때문에, isLoading의 값에 영향을 줄 것 같다는 생각이 든다. 통제되지 않은 비동기 함수이기 때문에, 오류가 날 수도 있다.
  2. isLoading에 대해 더 공부해 봐야할 것 같다.

 

이제 타이핑이 끝나든, submit 이벤트가 발생하든 네트워크 요청은 단 한 번만 하는 것을 확인할 수 있다.