useQuery({
queryKey: ['repoData'],
queryFn: () =>
fetch('https://api.github.com/repos/tannerlinsley/react-query').then(
(res) => res.json(),
),
})
첫번째 인자로는 리액트 쿼리가 데이터를 저장하는데 사용하는 고유 queryKey, 두번째 인자로는 데이터를 불러올때마다 실행하는 queryFn을 받는다.
우리는 이러한 훅을 컴포넌트 내부에서 사용함으로써 데이터를 렌더링하고 쿼리가 가질 수 있는 상태를 사용한다.
queryFn부분에서는 axios
라는 라이브러리가 작성되어 있고, 이는 리액트 쿼리가 데이터 패치를 어디서 어떻게 하는지 관여하지 않는걸 의미한다.
리액트 쿼리가 관여하는 부분은 queryFn의 실행결과로 fullfilled, rejected상태인 promise객체가 반환되는 이후 뿐이다.
useQuery({
queryKey: ['repoData'],
queryFn: () =>
Promise.resolve([
{ name: 'hong' },
{ age: 22 }
]),
})
리액트 쿼리에 알맞지 않은 질문들
1. 리액트 쿼리에서 BaseURL을 어떻게 정의하나요?
2. 리액트 쿼리에서 Response header들은 어떻게 접근하나요?
3. 리액트 쿼리에서 graphQl요청은 어떻게 생성할 수 있나요?
새로운 의문점
Q: 데이터 패칭 라이브러리가 아니라면 리액트 쿼리는 도대체 무엇인가요?
A: 비동기 상태 관리자 (Async State Manager)입니다.
그동안 상태를 "어디에 보관 해둘지"대해서만 고민했고, "모두 동일한 전역상태"로 취급했다.
지금부터 "어떠한 종류의 상태인지" 고민해야 한다
예를들어 사용자가 브라우저 탭을 열어놓고 30분후 해당 탭으로 돌아갔을때 페이지 내 데이터가 자동으로 갱신되어 있다면 얼마나 좋을까?
그래서 데이터를 자동으로 최신화 하는 작업을 비롯해, 라이프사이클 (에러, 로딩) 관리하는 작업들, 비동기 상태를 더 잘 관리하기 위해 리액트 쿼리를 사용하기만 하면 된다.
이제부터 상태 관리자가 무엇이고, 리액트 쿼리가 왜 상태 관리자 중 하나인지 이해하는 것.
일반적인 상태 관리자들은 앱 내에서 상태를 효율적으로 이용하게 해준다
효율적인 업데이트 = 업데이트를 하되 너무 많이 하지 않도록 하기
예를들어 useIssues
훅을 서로 다른 컴포넌트 3개에서 사용한다면, 동일한 API에 대해 여러번 중복해서 불러와지는것을 보게될 것이다
중복요청을 막기 위해 refetch와 관련된 모든 옵션을 비활성화하게 될 수도 있다.
이러려고 리액트 쿼리를 썼나? 자책할 수도 있다.
지금부터 왜 리액트 쿼리는 이렇게 많은 요청을 보내는가?
비동기 상태는 언제든 만료될 수 있기 때문에 특정 시점에 업데이트가 필수적이다.
이러한 업데이트는 아래의 상황에서 진행된다
이러한 이벤트가 발생하면 자동으로 쿼리를 다시 패치한다. 중요한것은 이러한 업데이트를 모든 Query들에 대해 하지는 않는다. 오로지 stale 하다고 여겨지는 Query에 대해 진행한다.
stale은 매우 중요한 개념이다.
리액트 쿼리는 데이터 동기화 도구이기는 하나, 무자비하게 모든 Query를 백그라운드 단에서 불러오지 않는다. 다시 패치할지 말지를 결정하는 기준은 staleTime에 의해 정해진다
staleTime은 데이터가 stale해지는 데 까지 걸리는 시간
stale의 반대말은 fresh인데, fresh라고 여겨지는 데이터는 다시 불러오지 않고 미리 캐시된 데이터를 클라이언트에게 전달된다.
fresh한 상태가 아니라면 클라이언트는 캐시된 데이터를 얻음과 동시에 다시 데이터를 불러오는 과정이 진행된다.
staleTime은 기본적으로 0으로 설정된다. 리액트 쿼리는 별다른 설정이 없다면 모든 Query를 즉시 stale한 상태로 여긴다. 이는 엄청나게 반복적인 데이터 패칭을 야기할 수 있지만, 상황에 맞게 개발자가 정의하는게 필요하다.
따라서 리액트 쿼리를 활용하는데 있어 매우 중요한 부분은 staleTime을 알맞게 조절하는 것이다. 위처럼 전역적으로 기본값을 설정해두고, 필요할때마다 덮어쓰는 방식을 선호한다.
비동기 상태에 대한 요구사항들을 다시 살펴보면, 리액트 쿼리에서는 데이터가 stale한 상태가 되거나, 위의 이벤트 중 하나가 발생하면 캐시를 최신화 시킨다. 여기서 중요한 점은 queryKey가 변화하는 이벤트 이다.
그럼 지금부터 queryKey가 변하는 이벤트는 언제 가장 많이 발생하는지 알아보자
위와 같이 filters라는 파라미터를 정의해서 queryFn함수에서 패치 요청을 하고 있다면, queryKey에도 filters라는 변수를 추가해줘야 한다.
또한 이러한 규칙을 잘 준수할 수 있도록 eslint plugin도 있으니 권장한다.
위의 예제처럼 queryKey, queryFn에 메모이제이션 훅을 사용하는 행위는 불필요하다.
위의 예제처럼 filters가 커스텀 훅의 파라미터로 들어오는 대신 스토어로 직접들어오는 경우 강력함을 보여준다
useQuery를 통해 관리되는 서버 샅애와 useStore에 의해 관리되는 클라이언트 상태의 극명한 차이를 느낄 수 있다.
요약하자면
- 리액트 쿼리는 데이터 패칭 라이브러리가 아닌 비동기 상태 관리 라이브러리
- staleTime은 여러분의 가장 좋은 친구
- 파라미터를 의존성으로 생각해야 하며, lint rule을 꼭 사용해야 한다.