처한 환경마다 다르겠지만, 이미 다른 서비스에서 리액트 쿼리를 활용하고 있었기 때문에 러닝 커브를 최소화하기 위해 리액트 쿼리를 적용했습니다
한 사람이 각자의 개별 데이터를 다루는 것이 아니라 여러 사람이 동일한 데이터를 업데이트 할 수 있었기 때문에 경우에 따라 캐싱이나 데이터 최신화가 필요했습니다.
여기에 더해 로딩과 오류 처리를 일일이 하는 것이 까다로운 작업이었고, 이를 간단히 해 줄 라이브러리가 필요했습니다.
리액트 쿼리를 적용하기 전엔 서버에서 모든 데이터를 가져와서 전부 props로 내려주는 형태였습니다. 하지만 이렇게 하면 새로운 컴포넌트가 중간에 들어가거나 삭제되는 경우에 props를 다 drilling 해야하기 때문에 개발에 피로도가 느껴졌습니다.
그래서 이를 필요한 데이터에 따라 훅으로 나누고자 했고 react-query가 적합하다고 생각했습니다.
라이브러리의 자세한 사용 방법은 공식 문서에 잘 나와있기 때문에 따로 기입하지 않겠습니다.
만약 이미 있는 프로젝트의 라이브러리에 리액트 쿼리를 적용해야 할 경우 프로젝트 안에서 구조를 어떻게 나누어야 하는 지에 집중해서 작성합니다.
리액트 쿼리에선 useQuery
라는 함수가 있습니다
const {data} = useQuery(queryKey, queryFn)
첫번째 인수는 queryKey
는 캐싱을 위해 사용하는 키 값입니다. 두번째 인수는 실제 데이터를 패칭하는 함수를 넣습니다.
실제 프로젝트에선 그대로 사용하면 어떨까요?
import getUser from './api'
function Component(){
...
const {data} = useQuery('user', getUser)
...
return <div></div>
}
별 차이가 없어보일 수 있습니다. 하지만 더 깔끔해질 방법이 있을 것 같습니다.
만약 이 컴포넌트 안에서 여러 정보를 불러와야 하는 경우는 어떨까요?
import getUser from './api'
import getCustomSetting from './api'
function Component(){
...
const {data} = useQuery('user', getUser)
const {data} = useQuery('custom Setting', getCustomSetting)
...
return <div></div>
}
한 컴포넌트가 아니고 여러 컴포넌트에서 위와 같이 사용할 경우 아래와 같은 단점이 생길 수 있습니다.
1. 쿼리 키가 중복되거나 오용될 수 있습니다. 만약 똑같은 api 함수에 다른 작업자가 모르고 다른 쿼리 키를 사용한 경우 원하던 동작이 이루어지지 않을 수 있습니다.
2. 코드를 봤을 때 useQuery
가 제일 눈에 띄는데 막상 의미가 있는 부분은 그 뒷부분이 더 큰 것 같습니다.
위 문제들을 개선하면 좀 더 가독성 있는 코드가 될 것 같습니다.
쿼리 키를 관리하는 데에도 여러 가지 방법이 있습니다. 앞으로 나올 내용들은 실제 프로젝트에 쓰인 방식은 아니었지만, 작성하는 시점에 더 좋은 방법으로 적습니다.
모든 쿼리는 배열로 관리합니다. 물론 리액트 쿼리에선 모두 문자열로 바꾸어 쓰긴 하지만, 문자열로 쓰면 쿼리키에 넣는 내용들을 문자열로 바꿔주기 번거롭다는 생각이 듭니다.
queryKeys.js
파일을 생성하고 아래와 같이 만들어볼 수 있을 것 같습니다.
export const userKey = () => ['user']
export const customSettingKey = () => ['customSetting']
export const userProfileKey = (id) => ['user', 'profile', id]
쿼리 키를 한 파일에서 관리한다면 이름이 겹칠 일이 없을 겁니다. 만약 리스트의 일부 항목이나 여러 데이터 중 하나를 가져오는 경우 userProfile
과 같이 id
를 인수로 받아 활용하는 것도 좋습니다.
위에 useQuery
대신 useUser
또는 useCustomSetting
과 같은 이름으로 쓰인다면 훨씬 함수의 의미를 파악하기 쉬울 것입니다. 또한 API 함수를 컴포넌트에서 부르지 않아도 되니 컴포넌트 파일이 더 깔끔해질 것을 기대할 수 있습니다.
커스텀 훅을 만든다면 src/hooks
하단에 모아두면 좋을 것 같습니다. 예를 들어 사용자의 정보를 가져오는 훅을 작성합니다
import { userKey } from 'src/queryKey'
import getUser from 'src/api'
const useUser = () => {
const result = useQuery(userKey(), getUser)
return result
}
export default useUser
타입스크립트를 사용한다면 useQuery의 인수에 맞는 타입을 인수에 선언합니다.
커스텀 훅을 작성하고 컴포넌트 파일에 커스텀 훅을 적용해봅니다.
import useUser from 'src/hooks/useUser'
import useCustomSetting from 'src/hooks/useCustomSetting'
function Component(){
...
const {data} = useUser()
const {data} = useCustomSetting()
...
return <div></div>
}
아까보다 코드가 간결하고 컴포넌트 렌더링과 관련이 없는 정보들을 숨길 수 있습니다.
보통 라이브러리들에 대한 설명이나 사용 방법, 장∙단점에 대해선 잘 나와있지만, 이를 프로젝트에 어떻게 정리할 것인가에 대한 글이 없는 것 같아 작성하였습니다.
라이브러리를 적용할 때는 여러 곳에서 사용할 경우도 함께 고려하면 좋을 것 같습니다.