개발 ‐ api 추가 방법 - ConSeat/frontend GitHub Wiki

🤔 컨벤션을 지켜야 하는 이유

  • SSR에서는 Tanstack Query의 useQuery 훅을 사용할 수 없습니다. 대신 prefetch도 제공하고 있어서 먼저 여기서 api를 호출하고 Hydration하여 CSR에서 추가 api 요청없이 사용할 수 있습니다.
  • prefetchQuery는 {queryKey, queryFn}를 받아 실행되기 때문에 이를 모아 관리하고 있습니다. key와 fn이 잘못 매칭되는 휴먼 에러를 방지하기 위함입니다.

🎯 api 추가 방법

1. endpoints 파일에 endpoint를 추가합니다.

2. queryKeys는 factory 형식으로 만들어두었습니다.
이를 참고하여 각 파일(member, review, stadium)에 .api와 .query를 추가하면 됩니다.

3. __.api.ts에는 api 반환, 요청 type과 요청 함수를 작성합니다.

api.public은 access token이 필요 없는 요청, api.secure는 access token이 필요한 요청입니다. 스웨거의 좌물쇠 여부를 확인하고 작성합니다.

export interface StadiumListResponse {
  totalReviewCount: number;
  active: StadiumInfo[];
  inactive: StadiumInfo[];
}

export const getStadiumList = async () => {
  const { data } = await api.public.get<StadiumListResponse>({ // 콘서트장 리스트는 api.public
    endpoint: API_ENDPOINTS.STADIUMS,
    errorMessage: MESSAGES.ERROR.GET_STADIUMS,
  });

  return { data: data.body };
};

4. ___.query.ts는 react-query를 사용하기 위한 queryKey와 queryFn을 작성하는 곳입니다.

export const stadiumQueries = {
  list: {
    queryKey: stadiumKeys.all,
    queryFn: getStadiumList,
  },
};

5. CSR을 위한 useQuery를 작성합니다.
hooks/queries/useFetch___.ts로 형식입니다. get 요청 외에는 hooks/mutations/useMutate___.ts로 작성합니다.

export const useFetchStadiumList = () => {
  return useQuery(stadiumQueries.list);
};
const useMutateAuth = () => {
  const postLoginAndRefreshMutation = useMutation<{ accessToken: string }, Error, void, unknown>({
    mutationFn: postLoginAndRefresh,
    meta: { silent: true },
  });

  return { postLoginAndRefreshMutation };
};

6. SSR에서도 stadiumQueries.list를 활용해 prefetch 합니다.
createPrefetchedQueryClient 함수를 만들어 dehydratedState를 모듈화하였습니다. 사용처에서는 아래와 같이 쓰면 됩니다.

export default async function HomePage() {
  const { dehydratedState } = await createPrefetchedQueryClient([stadiumQueries.list]);

  return (
    <HydrationBoundary state={dehydratedState}>
      <HomeClient />
    </HydrationBoundary>
  );
}

🌈 QueryProvider 설명

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        retry: false, // 한 번 실패했다면 바로 errorboundary로 보내 사용자가 다시 시도 하게함(기본: 3번)
        throwOnError: true,
        staleTime: 60 * 1000, // SSR에서 불러온 정보를 CSR에서 하이드레이션하여 바로 사용하기 위함
        networkMode: 'online', // 오프라인일 때 로딩 -> 네트워크 연결되면 바로 완성된 화면 보임
      },
      mutations: {
        networkMode: 'always', // 오프라인일 때 mutation 동작 error로 간주 & 막음
      },
    },
    queryCache: new QueryCache({
      onError: (error) => {
        alert(error.message); // error 처리
      },
    }),
    mutationCache: new MutationCache({
      onError: (error) => {
        alert(error.message); // error 처리
      },
    }),
  });
}

let browserQueryClient: QueryClient | undefined = undefined;

function getQueryClient() {
  if (isServer) {
    return makeQueryClient();
  } else {
    if (!browserQueryClient) browserQueryClient = makeQueryClient()
    return browserQueryClient; // CSR일 때 매번 makeQueryClient 하는 것 막음
  }
}