본문으로 바로가기

어제에 이어 또 라우트 핸들러가 중심이 되는 블로그를 쓰게 되었네요...

이번엔 사용 이유와 관련된 내용이 아닌 라우트 핸들러를 사용하며 주의해야 할 점을 살펴 보려 합니다.

 

그 전에 React-Query에 Hydration을 적용하며 발견한 문제이기 때문에 간단하게 Hydration이 무엇인지에 대해 집고 넘어가겠습니다.

Hydration이란?

hydration은 서버에서 렌더링된 HTML에 React와 같은 JavaScript 라이브러리를 사용해 동적으로 동작하는 인터페이스를 추가하는 과정입니다. 서버에서는 빠른 초기 렌더링을 위해 HTML을 반환하고, 클라이언트는 이를 받아와 JavaScript로 동적으로 페이지를 업데이트합니다. 마치 시든 꽃에 물을 주듯...

React Query와 Prefetch

React-Query에서 prefetch 기능은 데이터를 미리 요청하여 캐시를 inactive 상태로 보관하는 역할을 합니다. 이를 통해 서버에서 데이터를 로드할 때 클라이언트가 대기하는 시간을 최소화할 수 있으며, 실제로 데이터가 필요할 때 더 빠르게 로드됩니다.

 

Prefetching은 데이터를 미리 가져오는 과정으로 이는 특히 서버 사이드 렌더링(SSR) 환경에서 초기 페이지 로딩을 빠르게 할 수 있도록 돕습니다. 데이터를 가져와서 클라이언트의 상태로 저장하지만, 실제로 그 데이터를 사용하려는 시점까지 inactive 상태로 유지될 수 있습니다.

const RotationPage = async () => {
  const queryClient = new QueryClient();

  await queryClient.prefetchQuery({
    queryKey: [QUERY_KEY.ROTATIONKEYLIST],
    queryFn: () => getChampionRotation(),
    },
  });

  await queryClient.prefetchQuery({
    queryKey: [QUERY_KEY.CHAMPIONLIST],
    queryFn: () => getChampionList(),
,
  });

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <div>{/* <RotationChampionList /> */}</div>
    </HydrationBoundary>
  );
};

캐시가 inactive인 상태를 확인하기 위해 <RotationChampionList />를 주석 처리하였습니다.

🎯 문제 상황: 라우트 핸들러를 이용한 prefetchQuery가 React Dev Tools에 안 보이는 현상

이제 React Dev Tools에서 캐시가 잘 보관되어져 있는지 확인해봅시다.

getChampionRotation과 getChampionList 두 개의 prefetchQuery를 사용하려 했으나, 캐시된 데이터가 championList만 보이고 rotation 데이터는 보이지 않았습니다.

1️⃣ console을 통해 prefetchQuery가 실행되고 있는지 확인

await queryClient.prefetchQuery({
    queryKey: [QUERY_KEY.ROTATIONKEYLIST],
    queryFn: async () => {
      console.log("rotation prefetch");
      return await getChampionRotation();
    },
  });

prefetchQuery는 Next.js의 서버 환경에서 실행되기 때문에 개인 vscode 내의 터미널 창에서 확인할 수 있습니다.

prefetch는 문제 없이 일어나는 것을 알 수 있습니다.

2️⃣ console을 통해  getChampionRotation이 실행되고 있는지 확인

export const getChampionRotation = async () => {
  console.log('getChampionRotation 실행'); 
  const res = await fetch("/api/rotation");
  console.log('getChampionRotation 완료'); 
  
  if (!res.ok) {
    console.log("prefetch 실패");
    throw new Error("챔피언 로테이션을 받아오는데 실패하였습니다.");
  }
  const data = await res.json();

  return data;
};

getChampionRotation이 실행이 되고 완료는 안 뜨는 것으로 보아 fetch에 문제가 있는 것으로 보입니다.

(근데 왜 200이라는 정상적인 코드가...?) -> 잘 보면 /api/rotation이 아니라 /rotation으로 페이지에 대해서 200이 뜬 모습입니다^^

3️⃣ console을 통해  route handler가 실행되고 있는지 확인

// app/api/rotation/route.ts

export async function GET() {
  console.log('Route Handler 실행')
  ~~
}

라우트 핸들러에 접근을 못하고 있었습니다....!

✅ 서버에서 실행되는 api 통신 절대 경로로 작성

  const res = await fetch("http://localhost:3000/api/rotation");

이제 이렇게 prefetch가 된 모습을 확인할 수 있습니다!

 

여기서 여담으로....

만약에 처음부터 try catch를 썼다면....?

해당 라우트 핸들러를 이용한 통신을 try catch 구문으로 감싸줘 봅시다.

export const getChampionRotation = async () => {
  try {
    const res = await fetch(`/api/rotation`);

    if (!res.ok) {
      console.log("prefetch 실패");
      throw new Error("챔피언 로테이션을 받아오는데 실패하였습니다.");
    }
    const data = await res.json();
    return data;
  } catch (error) {
    console.error(error);
  }
};

바로 경로 문제라는 것을 알 수 있습니다...

 

react-query는 queryFn이나 mutationFn을 통해 알아서 오류를 잡아주니까 안 써도 되겠지...?라는 안일함을 한번에 늬우치게 해주는 상황이었습니다.

 

오늘의 교훈 : try catch를 통해 통신 관련 로직의 오류를 미리 방지하자!