[Next.js / Tanstack Query] 내 prefetchQuery가 작동하지 않는 이유

etc-image-0

 

청첩장 제작 프로젝트를 진행하며 메인 웨딩사진을 꾸밀 수 있는 스티커 기능을 담당했습니다. 이 때 스티커 에셋을 데이터베이스에서 받아오는 데 약 2초의 시간이 소요되었고, 이를 개선하기 위해 Tanstack Query의 prefetchQuery를 사용해 청첩장 제작 페이지 접속 전 데이터를 미리 가져오는 방식을 도입했습니다.

 

하지만 예상과 달리 prefetch를 위한 설정을 모두 끝내고 다시 스티커 목록을 확인해도 여전히 오랜 기다림이 필요했습니다. 이 글에서는 문제를 해결하기위한 과정과 방법을 소개하고자 합니다.

 

문제 상황 - 프리패치 적용 후에도 긴 로딩이 해결되지 않음

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60 * 60 * 24 * 1000,
    },
  },
});

await queryClient.prefetchQuery({
  queryKey: QUERY_KEYS.stickerImages(),
  queryFn: getCategorizedStickersWithMetadata,
});

return (
  <HydrationBoundary state={dehydrate(queryClient)}>
    <Header />
    <main>{children}</main>
  </HydrationBoundary>
);

 

queryClient를 생성하고 데이터를 받아온 후 dehydrate하는 설정을 모두 완료했음에도, 스티커 목록을 보여주는 컴포넌트가 마운트된 후에야 로딩이 시작되는 문제가 발생했습니다.

 

 

원인 파악을 위한 시도

1) 스티커 목록 컴포넌트 마운트 후 useQuery로 받아오는 데이터를 console.log로 출력해 확인

const { data: categorizedStickers, isLoading, error } = useGetCategorizedStickers();
console.log(categorizedStickers);

 

- 데이터를 받아왔지만, 다른 과정에서 지연이 생기는 건지 데이터를 받아오지 못한 건지 확인하기 위해 컴포넌트가 마운트 됐을 때 console.log를 이용해 useQuery로 받아오는 데이터를 출력했습니다.

- 처음에는 unefined가 출력되고, 로딩이 완료된 후에야 받아온 데이터가 출력되는 것을 확인해 프리패치 자체가 제대로 작동하지 않은 것을 확인했습니다.

 

2) fetchQuery를 사용해 에러 확인

etc-image-1

- prefetch는 에러가 발생하더라도 별도의 예외를 던지지 않는데, 에러 발생 시 명시적으로 에러를 알려주는 fetchQuery가 있다는 것을 알게되어 활용해보았습니다.

- Image가 선언되지 않았다는 에러를 확인했습니다!

 

 

+ ) prefetchQuery와 fetchQuery는 어떨 때 활용하면 좋을까?

- prefetchQuery: 이 게시글에서의 사례처럼, 데이터를 미리 가져와 캐싱하는 목적으로 사용됩니다. 이미 캐시에 데이터가 있다면, 새로운 데이터를 가져오지 않습니다.

- fetchQuery: 캐시 여부에 상관없이 데이터를 받아옵니다. 데이터를 가져와 반환해주기 때문에 사용자가 특정 작업을 진행한 후 데이터 갱신이 필요한 경우 사용할 수 있습니다! 또한 에러를 던지기 때문에 지금처럼 데이터 fetch에대한 결과를 확인할 때도 활용할 수 있습니다. 

 

파악한 원인

queryFn에서 에셋 데이터를 처리하기위해 이미지의 원본 사이즈를 확인하는 작업이 있었는데, 여기서 브라우저 환경에서만 사용이 가능한 Image 객체를 사용하고 있었습니다.

즉, Image는 브라우저 환경에서만 사용할 수 있는 클라이언트 전용 API이기 때문에 서버 컴포넌트에서 실행되는 프리패치에서는 이 방식이 정상적으로 실행될 수 없던 것이었습니다.

 

 

개선 방법

서버 환경에서도 이미지를 처리할 수 있도록 라이브러리인 sharp를 도입하고, sharp를 활용해 이미지 메타데이터를 확인할 수 있도록 별도의 서버액션을 구현했습니다. prefetchQuery의 queryFn에는 서버 환경에서 확실히 동작할 수 있는 서버액션을 사용하도록 변경하여 프리패치를 진행했습니다.

 

 

결론

etc-image-2

성공! 프리패치가 성공적으로 작동하여 에셋을 기다리는 시간이 사라졌습니다.

 

프리패치의 목적부터가 서버에서 미리 데이터를 받아 클라이언트에서 사용할 수 있도록 하는 건데, 그 의미를 제대로 고려하지 못했던 것 같습니다. 클라이언트 환경과 서버 환경의 차이를 명확히 이해하고 활용하는 것의 중요성을 느낀 경험이었습니다.