요즘 이직을 위해 react 를 독학하며 여러 프로그램들을 만들어보고 있다.
역시 직접 만들다보니 더 깊게 알아가게 되고 다른 라이브러리들도 많이 사용해볼 수 있게 되었다.
최근 원티드 프리온보딩 프론트엔드 과정을 통해 React Query 라는 라이브러리를 학습하게 되었다.
개념을 숙지하는 것에 많이 헷갈려서 포스팅을 통해 개념을 정리해보고자 한다.
Vuex
공식문서 : https://v3.vuex.vuejs.org/kr/
3년 전 학원을 수료하면서 프론트엔드에 사용했던 기술은 vue.js 였다.
전역관리를 위해 Vue.js 애플리케이션에 대한 상태관리 패턴의 라이브러리인 Vuex 를 사용했는데, 당시에도 javascript jsp를 열심히 배우다가 SPA 프레임워크의 개념을 익히려니 헷갈리고 많이 어려웠던 것 같다.
Vuex에서는 Action에서 비동기처리를 같이 하여 서버에서 받은 데이터를 store에서 처리하였었다. (링크)
Redux
공식문서 : https://ko.redux.js.org/
Redux의 기본 개념인 reducer 는 순수함수이다. 동일한 파라미터를 주었을 때 항상 같은 값을 리턴하고, 외부상태를 변경하지 않는 함수를 순수함수라고 한다.
이러한 개념 때문에 비동기처리는 React-saga, React-thunk 등의 미들웨어를 필수로 사용해야 한다.
Redux를 적용하던 중..!
비동기처리를 위한 미들웨어를 학습하기 전이라 view 단에서 api를 모두 넣어 구현하고 있었고, api를 분리하는 것을 필요로 하고 있었다. SWR이나 react query를 학습해보기를 권해주신 분이 계셔서 공부하려던 찰나, 원티드 프리온보딩에 참여하게 되어 좋은 기회에 react query 를 학습해볼 수 있었다.
React Query
처음에 React Query 를 접하고 다른 사람들의 소스도 보고 적용해보려고 하였으나 쉽지가 않았다.그 이유는 내가 Redux 에서 api를 관리하는 미들웨어 중에 하나라는 착각을 가지고 바라보았기 때문이다. 개념을 조금 더 깊게 알아보았더라면 이러한 오류는 발생하지 않았을 것이다.
Redux + middleware vs react query
redux는 순수함수, 단일 store 를 가지고 있으며, action을 일으켜서 middleware 를 통해 가지고 온 서버 데이터를 redux에 저장하여 사용한다. 또한, 이 데이터의 영속성을 위해 react-persist 를 사용하기도 한다.
하지만 이미 그 데이터는 서버에 저장되어있는 (데이터 베이스에 이미 담겨있는) 데이터이다. 단일 store (진실한 정보의 원천) 이라는 개념과도 맞지 않다. 또한, 혹시 서버에서 변경된 데이터가 다시 클라이언트 store에 저장하기 위해서는 Action 이 일어나 store 의 상태 값을 바꿔주어야 최신화 된 데이터를 볼 수 있을 것이다.
서버에서 주기적으로 데이터를 가지고 온다면, 클라이언트에 굳이 데이터를 담아 사용할 필요도 없고 최신 데이터를 유지할 수 있을 것이다. React Query 에서는 이러한 단점을 보완하기 위해 클라이언트의 상태와 서버의 상태를 따로 관리하도록 해준다.
특징
1. 캐싱 : 자주 사용하는 데이터를 저장해둠.
2. 동일한 데이터에 대한 중복된 요청을 제거
3. 데이터가 오래되었는지를 파악하고 최신 데이터로 업데이트
4. 데이터가 update 될 경우 자동으로 데이터를 가져오는 api를 실행
5. 보일러 플레이트 코드가 줄어듬
6. 종속성이 없어 쉽게 적용이 가능
이상의 특징은 사용해보면서 하나하나 익혀가보아야겠다.
사용법
$ npm i @tanstack/react-query
# or
$ yarn add @tanstack/react-query
공식문서에 있는 간단한 예제를 첨부한다.
//_app.tsx (전역 셋팅)
import {
useQuery, //데이터를 가져오기 위한 api
useMutation, //데이터를 바꾸기 위한 api
useQueryClient,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import { getTodos, postTodo } from '../my-api'
// Create a client //클라이언트 생성
const queryClient = new QueryClient()
function App() {
return (
// Provide the client to your App //provider 셋팅
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}
//todos.tsx (쿼리를 사용하는 곳)
function Todos() {
// Access the client //클라이언트 접근
const queryClient = useQueryClient()
// Queries (['todos']는 query key 이다. getTodos가 api 함수이다.)
// useQuery(queryKey, queryFn, options)
const query = useQuery(['todos'], getTodos)
// Mutations (postTodo가 api함수이다.)
// useMutation(mutationKey, mutationFn, options)
const mutation = useMutation(postTodo, {
onSuccess: () => {
// Invalidate and refetch //이전 데이터는 무효화, 데이터 다시 get
queryClient.invalidateQueries(['todos'])
},
})
return (
<div>
<ul>
{query.data?.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
<button
onClick={() => {
mutation.mutate({
id: Date.now(),
title: 'Do Laundry',
})
}}
>
Add Todo
</button>
</div>
)
}
render(<App />, document.getElementById('root'))
getTodos는 이러한 형태의 API 일 것이다.
const clientApi = axios.create({
baseURL: "http://localhost:8080",
});
const getTodos = async (): Promise<{ data: ITodo[] }> => {
const { data } = await clientApi.get(`/todos`, {
headers: {
Authorization: getToken(),
},
});
return data;
};
도대체 api로 가져오는 소스랑 redux는 어떻게 연결되는거야!? 에서 막혔었다. (우선 적용해보자는 무모한 용기의 잘못된 사용.) api로 가져오는 데이터와 client의 순수한 데이터를 분리하려고 react query를 쓰는 거라고 과거의 나의 귓속에 속삭여본다.
redux는 이제 완전히 client의 데이터만을 제공하게 되었다. 예를 들면, 다크모드와 같은 UI에 대한 상태가 client만을 위한 상태 데이터일 것이다.
참조
카카오페이 프론트엔드 개발자들이 React Query를 선택한 이유
원티드 프리온보딩 프론트엔드 과정 강의
'주말에 쓰는 개발일기 > react' 카테고리의 다른 글
[React Query] 적용기2 (0) | 2022.08.27 |
---|