안녕하세요. 이번 포스팅에서는 지금 다니고 있는 회사에서 react-query를 왜 도입했고 어떻게 사용하고 있는지에 대해 공유해볼까 합니다.
도입한 이유
기존 프로젝트에서는 상태관리를 하기 위해 redux-toolkit을 사용했습니다. 서버 데이터를 불러오기 위해 내장된 redux-thunk를 사용했는데요. 비동기 상태에 대한 코드를 아무래도 직접 관리해줘야 하니, 앱의 규모가 커지면 커질수록 작성해야 하는 코드가 늘어나는 불편함이 있었습니다.
이를 해결하기 위해 react-query를 도입하게 됐습니다.
적용 방법
react-query를 잘 사용하기 위해 했던 고민들 중에 몇가지를 공유해보겠습니다.
- custom hook
- selector
Custom Hook
react-query에서 제공해주는 기본적인 API를 컴포넌트에서 직접 사용하지 않고 custom hook으로 추상화해서 사용했습니다. 이렇게 사용하면 구현을 숨길 수 있으며, 도메인에 대한 문맥을 제공해줄 수 있습니다.
const useTodosQuery = () => {
return useQuery(todosQueryKey.list, fetchTodos)
}
const useCreateTodoMutation = () => {
return useMutation((newTodo) => createTodo(newTodo))
}
const SomeComponent = () => {
const todos = useTodosQuery()
const createTodo = useTodoCreateMutation()
const handleClick = () => {
createTodo.mutate(todo)
}
if (todos.isLoading) {
return <div>loading...</div>
}
if (todos.error) {
return <div>error...</div>
}
return todos.map(todo => {
//...
})
}
아 그리고 destructuring을 하지 않은 것에 대해 의아할 수도 있을 것 같은데요. 처음 react-query를 사용할 때는 destructuring을 해서 이름을 다시 붙여주는 방식으로 사용했었는데요. 다수의 query custom hook을 사용할 때 변수가 충돌되지 않도록 매번 이름을 다시 붙여주는 번거로움이 있었습니다.
const SomeComponent = () => {
const { data: todos, isLoading: isTodosLoading, isError: isTodosError} = useTodosQuery()
const { data: user, isLoading: isUserLoading, isError: isUserError} = useUserQuery()
const { data: billing, isLoading: isBillingLoading, isError: isBillingError} = useBillingQuery()
}
그래서 destructuring을 하지 않고 사용하는 것으로 리팩토링을 했습니다.
const SomeComponent = () => {
const todos = useTodosQuery()
const user = useUserQuery()
const billing = useBillingQuery()
}
Selector
redux에서는 selector 함수와 useSelector를 사용해 원하는 데이터를 가져올 수 있죠. react-query에서는 서버 데이터를 cache에 저장하기 때문에 캐시된 데이터를 쉽게 꺼내올 수 있는 인터페이스가 필요했습니다.
이를 위해 useQuery에서 옵션으로 제공해주는 select 를 활용했습니다. redux에서 사용하는 방식처럼 원하는 데이터를 추출해주는 selector함수를 query custom hook의 인자로 넘겨서 사용하게끔 설계했습니다.
const useTodosQuery = <T extends (todos: Todo[]) => any>(selector?: T) => {
return useQuery(todosQueryKey.list, fetchTodos, {
select: todos => selector?.(todos) ?? fetchTodosDto
})
})
type Todo = {
text: string;
done: boolean;
}
const selectIsTodosAllDone = (todos: Todo[]) => todos.every(todo => todo.done)
const SomeComponent = () => {
const isTodosAllDone = useTodosQuery(selectIsTodosAllDone)
}
마치며
react-query는 실무에 적용하기 전부터 관심있게 지켜봤던 기술입니다. 다른 회사의 컨퍼런스 영상을 보면 react-query를 도입해서 비동기 처리를 하는 코드를 간소하게 리팩토링을 했다는 얘기를 많이 들었는데요. 실제로 사용해보니 확실히 비동기 처리를 간편하게 만들어주는 라이브러리인 것 같습니다.
사실 react-query를 도입하는 과정이 쉽지는 않았습니다. "아직 학습이 된 상태가 아니기 때문에 좀 부담스럽다", "새로운 기술을 도입하면서 기한 내에 프로젝트를 끝낼 수 없을 것 같다"는 부정적인 의견도 있어 팀원들의 공감이 필요했기 때문입니다. 하지만 장기적으로 봤을때 도움이 될 거라는 것에 대한 확신이 있었고 적극적으로 의견을 주장했습니다.
기술 도입에 대한 설득, 실제로 도입해서 이를 성공적으로 이끄는 경험을 처음해보았는데요. 프로젝트 회고때 많은 팀원들이 react-query를 도입한 것에 대해 만족한다는 얘기를 해줬을때 정말 뿌듯했습니다.
기술 도입을 할때 중요한 것은 기술이 해결하는 문제가 무엇인지 아는 것도 중요하지만, 팀원들의 공감이 선행되어야 한다는 것을 배웠던 경험이었습니다.
'Frontend' 카테고리의 다른 글
Next.js 13 버전을 알아보자 - Turbopack, Layouts, Server Components... (0) | 2022.10.29 |
---|---|
Cypress를 GitHub Actions에 연동해보자! (0) | 2022.10.07 |
Throttle와 Debounce (0) | 2022.08.26 |
Code Splitting 테스트해보자 (0) | 2022.08.26 |
React Native 절대경로 추가하기 (0) | 2022.08.26 |