iam102 2023. 11. 25. 21:36

React Query를 도입하여 사용 중에 Query Key에 대한 관리의 필요성을 느꼈습니다. 팀 내부에선 Query Key를 Array 형태로 사용하기로 컨벤션이 잡혀 있었는데 사용자가 Array 내부의 key 요소를 잘 못 입력을 하거나 order가 잘 못 되는 경우가 발생할 수 있었기 때문입니다. 그리고 아래의 예시처럼 특정 ID를 key의 요소로 사용하거나 key의 요소가 많을 경우 그러한 실수는 더 많이 발생할 수 있습니다.

...

export const useGetUserDetail = (userID: string) => {
  const context = useQuery<{ user: UserDetail }, Error>(
    // 지정할 key의 요소가 많은 케이스
    ['user', 'detail', 'info', userID],
    async () => {
      const result = await getUserDetail(userID);
      return result.data;
    },
  );
  return context;
};

...

이러한 실수를 줄이기 위해 Query Key를 generate 하는 Query Key Factory를 소개하려고 합니다.

 

Key Factory를 만들어 사용하는 case

Query Key를 생성하는 Factory를 만들어 사용하는 case입니다. feature 단위로 Query Key Factory의 단위를 나누어 사용하는 것이 유지보수나 사용성에 있어 좋습니다.

// Query Example
// /src/features/user/query/userQuery.ts

...

const userKeys = {
  default: ['user'] as const,
  list: () => [...userKeys.default, 'list'] as const,
  details: () => [...userKeys.default, 'detail'] as const,
  detail: (userId: number) => [...userKeys.details(), userId] as const,
}

export const useGetUserDetail = (userId: number) => {
  const context = useQuery<UserDetail, Error>({
    // query key factory 활용
    queryKey: userKeys.detail(userId),
    queryFn: async () => {
      const result = await getUserDetail(userId);
      return result.data;
    },
  });
  return context;
};

...

  // query key가 쓰이는 곳에서 다 사용 가능
  queryClient.invalidateQueries(userKeys.default);

...

 

@lukemorales/query-key-factory  사용하는 case

Query Key의 생성 및 관리를 도와주는 @lukemorales/query-key-factory 라이브러리가 있습니다. React Query 공식문서에서도 찾아볼 수 있는 라이브러리이며 간단하게 사용 방법을 확인하겠습니다.

 

각 환경에 맞게 @lukemorales/query-key-factory 라이브러리를 설치합니다.

npm install @lukemorales/query-key-factory

# or

yarn add @lukemorales/query-key-factory

 

앞선 예제와 같은 케이스로 확인하겠습니다.

// Query Example
// /src/features/user/query/userQuery.ts

import { createQueryKeys } from '@lukemorales/query-key-factory';

export const userKeys = createQueryKeys('user', {
  list: null,
  detail: (userId: number) => [userId],
});

export const useGetUserDetail = (userId: number) => {
  const context = useQuery<UserDetail, Error>({
    // query key factory 활용
    ...userKeys.detail(userId),
    queryFn: async () => {
      const result = await getUserDetail(userId);
      return result.data;
    },
  });
  return context;
};

...

  // query key가 쓰이는 곳에서 다 사용 가능
  // query key => ['user']
  queryClient.invalidateQueries(userKeys._def);
  // query key => ['user', 'detail']
  queryClient.invalidateQueries(userKeys.detail._def);
  

...

 

createQueryKeys를 통해 생성된 key factory를 mergeQueryKeys를 통해 필요에 따라 통합하여 사용할 수 있습니다.

// /src/query/queryKeys.ts
import { mergeQueryKeys } from '@lukemorales/query-key-factory';

import { userKeys } from '@/features/user/query/userQuery';
import { todoKeys } from '@/features/todo/query/todoQuery';

const queryKeys = mergeQueryKeys(
  userKeys,
  todoKeys,
);

export default queryKeys;
...

import queryKeys from '@/query/queryKeys';

const user = queryClient.getQueryData([...queryKeys.user._def]);
const userDetail = queryClient.getQueryData([...queryKeys.user.detail(userId)]);

...

 

마무리

React Query를 도입하여 사용하다 보면 Query key에 대한 관리의 필요성을 많이 느끼게 됩니다. 그래서 Query Key Factory에 대해 찾아보았고 현재 프로젝트에선 위의 두 가지 방법 중 @lukemorales/query-key-factory 라이브러리를 도입하여 사용 중입니다. 기존에 발생했던 Query Key로 인한 오류와 Query Key를 작성하는 데 있어 피로도를 많이 줄일 수 있었습니다. React Query의 활용성이 커지는 만큼 Query Key 관리에도 힘을 써야겠습니다.

 


참고 자료

https://tkdodo.eu/blog/effective-react-query-keys

https://github.com/lukemorales/query-key-factory#readme