리액트 네이티브 - Movies app : React-Query (useQuery) 적용
React-Query (useQuery) 리팩토링 과정 - 서버 스테이트 관리
1. 리액트 쿼리 (React Query) 란?
Server State, 비동기 데이터를 관리하기 위한 라이브러리 이며, useQuery hook 안에서 API 요청을 담당하고, API 요청 결과 및 다양한 상태들을 바로 사용할 수 있다. (redux thunk 사용 필요 X)
한 번 API 요청 시 queryKey 가 API 요청결과를 cache 에 저장하고, caching된 API 요청은 다시 서버에 하지 않고 cache memory에서 더 빠르게 불러 온다.
그래서 불필요한 서버 요청을 알아서 막아주게 된다!
2-1. 프로젝트 세팅
우선 명령어로 install을 진행합니다.
npm i react-query
# or
yarn add react-query
Installation | TanStack Query Docs
2-2. Setup App.js
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<Root />
</QueryClientProvider>
);
}
queryClient변수 선언으로QueryClient()함수를 가져온다. (import 필요)- 컴포넌트 안에서 최상위의 요소에
<QueryClientProvider/>라는 태그로 감싸준다! (import 필요) QueryClientProvider의client속성에는 1 에서 만든 변수queryClient를 넣어준다.
3. useQuery 사용하기
get, fetch 요청을 할 때, useQuery라는 훅을 사용하게 된다. useQuery 를 사용하는 컴포넌트 파일에 import가 되어야 하고,
const fetcher = () =>
fetch("https://api.github.com/repos/tannerlinsley/react-query").then((res) =>
res.json()
);
const { isLoading, error, data } = useQuery("repoData", fetcher);
위 예시와 같이 사용하면 된다. 리엑트 쿼리는 기본적으로 '쿼리 키' 와 fetch를 진행하는 '함수' 이렇게 두 가지 인자만 있으면 되고, 나머지 옵션들도 존재한다.
- data : fetch 함수가 받아온 response 값!
- isLoading : 서버 스테이트의 상태!
- error : 에러 처리를 할 수 있는 객체!
4. Refactoring
기존에는 setState로 받아온 데이터들을 클라이언트 state로 받아오고, isLoading도 상태변경을 직접 관리했다..
const [nowPlayings, setNowPlayings] = useState([]);
const [topRated, setTopRated] = useState([]);
const [upcoming, setUpcoming] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useQuery를 사용하면, 이렇게 간단하게 데이터와 로딩상태를 모두 useState가 아닌 useQuery가 관리하게 만들어 주었다.
const { data, isLoading } = useQuery("NowPlaying", getNowPlaying);
const { data, isLoading } = useQuery("TopRated", getTopRated);
const { data, isLoading } = useQuery("Upcoming", getUpcoming);
여기서 우리가 useQuery의 데이터와 isLoading 상태가 같을 수 없으니 별칭을 넣어줘야 한다.
const {
data: nowPlayingData,
isLoading: isLoadingNP,
} = useQuery(["Movies", "NowPlaying"], getNowPlaying);
const {
data: topRatedData,
isLoading: isLoadingTR,
} = useQuery(["Movies", "TopRated"], getTopRated);
const {
data: upComingData,
isLoading: isLoadingUC,
} = useQuery(["Movies", "Upcoming"], getUpcoming);
또한 하나의 키로 3개의 useQuery를 불러오기 위해 Movies라는 동일한 key값도 만들어줬다. key는 배열의 형태로 html / css 에서 사용하는 class라고 이해하면 편하다!
const onRefresh = async () => {
/* 스크롤 아래로 당겨서 새로고침 */
setIsRefreshing(true);
await queryClient.refetchQueries(["Movies"]);
setIsRefreshing(false);
};
그렇게 공용으로 사용하는 key인 Movies 를 이용해 전역에서 사용하는 queryClient에 접근하여 refetchQueries를 진행하면 Movies 키를 가지고 있는 useQuery들은 refetch가 진행된다.
5. 기존에 state 데이터를 받아온 값은?
{nowPlayingData.results.map((card) => (
<Slide key={card.id} card={card} />
))}
여기서도 그렇게 복잡하지 않다!
- 기존 :
{state.map()} - 리펙토링 :
{data.results.map()}
nowPlayingData라고 할당한 useQuery의 서버 데이터를 가지고 있는 녀석은 .results 라는 곳에서 다 가지고 있다!
6. isLoading 일괄 처리
const isLoading = isLoadingNP || isLoadingTR || isLoadingUC;
위에서 만들어준 isLoading의 별칭을 이용해 셋의 데이터가 로딩이 다 완료되면 보여줄 수 있도록 isLoading 변수에 or 연산자를 넣어 일괄처리를 했다.
- Server State, 비동기 데이터를 관리하며 엄청난 퍼포먼스 향상을 위해
useQuery를 사용! - redux, RTK를 사용할 때와 같이 전역상태관리를 위해 최상단 컴포넌트인 Root 컴포넌트 위에
<QueryClientProvider/>태그를 감싸고client에QueryClient()를 넣는다. - 리엑트 쿼리는 기본적으로 '쿼리 키' 와 fetch를 진행하는 '함수' 이렇게 두 가지 인자를 받고, 나머지 옵 션들도 존재한다.
useQuery에 넣는 텍스트 형태의 key는 html에서 사용하는 class 라고 이해하자. 배열형태로 넣을 수 있고,[고유 key, 통합 key]를 할당해서 보다 free하게 사용할 수 있다.useQuery를 사용한 data의 response 값은data.results에 들어있다.isLoading은 일괄처리하여 간편하게 사용할 수 있다.
