useQuery에서 RTK Query로 데이터 가져오기
퀵뉴스라는 프로젝트를 만들면서, 오픈 API의 데이터를 가져오기 위해서 나는 "useQuery"를 사용했었다.
이 이전에는 "axios.get()"으로 데이터를 가져왔으나, 조금 더 나은 방법으로 실무에서도 자주 사용하고 있는 "useQuery"를 사용해서 데이터를 가져오도록 했다.
그러나, 이번에 "RTK Query"에 대해 알게 되면서 "둘 중에 무엇이 더 나을까?"에 대해 알아보기 시작했고, 데이터를 "RTK Query"로 가져오는 것이 "useQuery"로 가져오는 것보다 장점이 더 많고, 실무에서도 더 자주 사용하고 있다고 하는 정보를 알게 됐다.
간단하게 "useQuery"보다 "RTK Query"가 가지고 있는 장점에 대해 말하자면,
- useQuery보다 더 간결한 코드
- 자체적인 캐싱 기능
- 에러 처리
- 등등
그러나, 개인적인 생각으로 에러 처리는 "useQuery"에서도 처리할 수 있기 때문에 "RTK Query" 만의 장점은 아니라고 생각한다.
"RTK Query"를 사용하기 전에 "useQuery"로 데이터를 가져온 방법은 아래와 같았다.
useQuery로 데이터 가져오기
"useQuery"로 데이터를 가져오기 위한 방법을 구글 검색, ChatGPT를 통해 알게 됐다.
정답이 아닌, 그냥 간단하게 예시 코드로 아래처럼 작성하여 사용했었다.
const fetchData = async (newCategory: string | undefined) => {
let url = `https://newsapi.org/v2/top-headlines?country=kr}&pageSize=10&apiKey=${process.env.NEXT_PUBLIC_NEWS_API_KEY}...`
const response = await axios.get<CategoryNewsLists>(url)
return response.data.articles
}
export default function newsFetch(
category: string | undefined,
pageSize: number,
) {
const { data: articles, isLoading } = useQuery(
['news', category],
() => fetchNews(category),
{
refetchOnWindowFocus: false,
cacheTime: 30 * 60 * 1000,
},
)
const visibleNews = articles?.articles.slice(0, pageSize)
return { visibleNews, isLoading }
}
데이터를 가져오려고 하는 오픈 API의 URL을 "Axios"로 데이터를 가져오고, Object 형식으로 담겨진 데이터를 내보낸다.
위에 "useQuery"로 가져온 코드를 확인해보자.
- "useQuery" 훅을 사용해 데이터를 가져오는데, 쿼리 결과와 로딩 상태를 반환한다.
- 'news'라는 쿼리의 고유한 식별자를 정의하고, 이를 캐시 키로 사용하도록 한다. 그리고 쿼리를 실행하기 위해 'category'의 매개변수를 사용한다.
- 'category'를 인자로 넘긴 'fetchNews' 함수로 데이터를 가져온다.
- 창이 포커스될 때마다 새로고침하지 않도록 설정한다.
- 30분의 캐시 유효시간을 설정한다.
생각보다 코드의 길이도 길지 않고, 간단하게 데이터를 가져올 수 있다. 또한, 필요한 설정들을 직접 추가함으로써 간단하게 처리할 수도 있다.
이보다 더 간단하고 설정을 쉽게 설정할 수 있다는 "RTK Query"로 데이터를 가져오는 방법이 무엇인지 찾아봤다.
RTK Query로 데이터 가져오기
우선, "RTK Query"로 데이터를 가져오기 위해서는 Redux의 "Provider"와 "Store"를 설정 및 추가해야 한다.
// store.ts
import { configureStore } from '@reduxjs/toolkit'
import { newsApi } from '../fetch/CreateApi'
export const store = configureStore({
reducer: {
[newsApi.reducerPath]: newsApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(newsApi.middleware),
})
export default function App({ Component, pageProps }: AppProps) {
if (pageProps.statusCode && pageProps.statusCode !== 200) {
return <Error {...pageProps} />
}
return (
<Provider store={store}>
<Layout>
<div>
<GlobalFontStyle />
<Component {...pageProps} />
</div>
</Layout>
</Provider>
)
}
위 처럼, "Provider"와 "Store"를 설정해주고 나면 "RTK Query"를 사용할 수 있는 환경이 된다.
설정을 완료했다면, 데이터를 가져오기 위해 "createApi"를 선언해야 한다.
import { MAX_PAGE_COUNT } from '@/constants/CommonVariable'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
export const newsApi = createApi({
reducerPath: 'newsApi',
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (builder) => ({
fetchNews: builder.query({
query: (newCategory: string | undefined) => ({
url: `https://newsapi.org/v2/top-headlines?country=kr&pageSize=10&apiKey=${
process.env.NEXT_PUBLIC_NEWS_API_KEY
}`,
}),
}),
}),
})
export const { useFetchNewsQuery } = newsApi
...
대략적으로, 위의 코드처럼 데이터를 불러올 수 있다. 간단하게 설명하자면,
- "createApi" 함수를 사용해 API를 생성한다.
- 생성된 API의 리듀서 경로를 지정하는데 여기서는 "newsApi"라는 리듀서 경로를 설정한다. (Redux의 Store에서 해당 API 상태 관리에 사용)
- API에서 사용할 기본 쿼리를 정의하는데 "fetchBaseQuery" 함수를 사용해 기본 쿼리를 생성하고 "baseUrl" 옵션을 설정하여 기본 URL을 지정한다.
- API의 엔드포인트를 정의하는데, "endpoints" 객체를 통해 여러 개의 엔드포인트 정의가 가능하다; (실제로 데이터를 가져오는 데 사용되는 쿼리 함수를 정의한다)
- "builder.query"를 사용해 새로운 쿼리 함수를 정의한다.
- 위에 선언한 쿼리를 외부에서 사용할 수 있도록 "useFetchNewsQuery"로 내보낸다.
외부에서 위의 쿼리를 사용하는 방법은 아래와 같다. "useFetchNewsQuery"처럼 함수로 내보내진 것을 가져와 사용하는 것이다.
const { data, isLoading, error } = useFetchNewsQuery(category)
"RTK Query"를 사용하게 되면 설정해야 하는 것들이 있지만, 이는 "Redux"를 동시에 사용할 수 있다는 장점이 있기 때문에 단점이라고 생각되지 않는다.
또한, 코드를 보면 "useQuery"와 달리 "createApi"로 API 호출을 위한 설정을 한 번에 처리할 수 있어 더 개선된 모습을 볼 수 있었다. 그리고 알아서 캐싱이 되기 때문에 따로 캐시 설정을 해주지 않아도 된다.
무엇보다, 쿼리를 요청할 때, 타입을 설정할 수 있다는 것이 장점 중 하나인 것 같다.
실행을 하게 되면, 아래의 이미지처럼 Object 형식으로 데이터가 반환되는 것을 확인할 수 있다.
데이터 호출에 대한 서버 상태와 데이터 값 그리고 총 갯수 값을 반환받는다.
"useQuery"와 "RTK Query"를 둘 다 사용해본 경험으로 사실 큰 차이는 없다고 생각한다. 그러나, "RTK Query"는 자동으로 캐시를 처리해주고, 타입스크립트를 지원하며, Redux와 함께 사용할 수 있어 더 나은 점이 있다.
각자의 환경과 상황에 따라 판단하여 사용하면 되지만, 자동으로 캐시 처리를 해주고, 타입스크립트를 지원한다는 점에서 성능 최적화가 가능한 "RTK Query"를 사용해보는 것이 더 괜찮을 것 같다는 생각이 들었다.
또한, "createApi" 하나로 모두 처리할 수 있으니 코드 개선에도 더 나은 모습을 보여줄 수 있다고 생각한다. 이번에는 비교적 굉장히 간단하게 사용해 아쉬운 부분이 있다. 나중에 더 넓은 범위에서 사용할 수 있었으면 좋겠다는 생각이 든다.