밝을희 클태

[ Next.JS ] 배포 환경에서 주문형 재검증(On-demand Revalidation)이 안된다 본문

KEYNUT 프로젝트

[ Next.JS ] 배포 환경에서 주문형 재검증(On-demand Revalidation)이 안된다

huipark 2024. 8. 3. 21:04
Binaries:
  Node: 22.3.0
  npm: 10.8.1
  Yarn: 1.22.19
  pnpm: 8.14.0
Relevant Packages:
  next: 14.2.5 // Latest available version is detected (14.2.5).
  eslint-config-next: 14.2.3
  react: 18.3.1
  react-dom: 18.3.1
  typescript: 5.4.5

 

현재 개발 환경에서 테스트를 마치고, 배포 환경에서 프로젝트를 테스트 중인데, 주로 revalidateTag를 사용해서 재검증을 요청하는 방식으로 구현했다. 다른 페이지들은 전부 문제가 없었는데, 메인 페이지(’/’)에서 재검증 요청이 제대로 이루어지지 않는 문제가 발생했다.

 

검색해보니까 작년부터 나와 비슷한 사례들이 좀 있긴 한데, 딱히 이거다 하는 해결책은 없었다.

 

처음에는 Vercel 문제인가 싶어서 Serverless Functions의 리전도 바꿔보고 이것저것 만져봤는데, Vercel 문제가 아닌 것 같았다. 그래서 로컬에 배포 환경을 조성하고 테스트해보니 여전히 문제가 발생했다.

 

서버에 부담을 줄이기 위해 메인 페이지를 방문할 때마다 요청을 하는 게 아니라, 상품이 업로드되면 products 태그를 활용해 재검증 요청을 하도록 구현했다.

 CSR에 useQuery staleTime은 Infinity

import RenderHome from './_components/RenderHome';
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
import getRecentProducts from './_lib/getRecentProducts';

export default async function Home() {
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery({ queryKey: ['recentProducts'], queryFn: getRecentProducts, staleTime: 60 * 1000 });

  const dehydratedstate = dehydrate(queryClient);
  return (
    <HydrationBoundary state={dehydratedstate}>
      <RenderHome />
    </HydrationBoundary>
  );
}
const getRecentProducts = async () => {
  try {
    const baseUrl = process.env.NEXT_PUBLIC_BASE_URL;
    const res = await fetch(`${baseUrl}/api/products/recent`, {
      next: { tags: ['products'] },
    });
    const data = await res.json();
    return data;
  } catch (error) {
    throw error;
  }
};

export default getRecentProducts;

revalidateTag가 문제인가 싶어 revalidatePath로 변경해봤으나, 여전히 데이터 재검증은 일어나지 않았다. 그런데 신기한 건 revalidate, 메인 페이지의 CSR 환경에서 router.refresh()도 전혀 작동하지 않았다.

 

다른 페이지에서도 “products” 태그를 많이 사용하는 게 문제인가 싶어서 다른 태그를 줘봤지만, 이것도 해결책이 아니었다.

 

CSR에서 useQuerystaleTime을 0으로 설정하고 테스트를 해보았으나, 메인 페이지의 데이터는 여전히 빌드 시점의 데이터로 남아 변하지 않았다. 내 예상은 CSR 환경에서의 fetch는 기본 캐싱 전략이 “no-store”이기 때문에, SSR에서 캐시 데이터를 가져오더라도 CSR에서는 DB의 최신 데이터를 가져와야 한다는 것이었다.

 

의심되는 부분을 찾았다. next run build를 할 때 생성된 파일들을 보면 최상단의 라우트가 문제가 되는 부분인데, 해당 라우트만 정적 라우트로 생성된 것을 볼 수 있었다.


해당 라우트를 강제로 동적 라우트로 설정하기 위해 아래의 코드를 추가했다.

export const dynamic = 'force-dynamic';

결과적으로, 동적 라우트로 잘 생성되었고, 배포 환경에서도 재검증 타이밍에 맞춰 요청이 잘 보내졌다.

 

이번 경험을 통해 Vercel 문제로만 생각하고 수많은 커밋을 남겼던 점이 아쉽다. 빌드 과정에서 조금만 더 주의 깊게 살펴봤다면 빠르게 원인을 찾을 수 있었을 것이다.