Next.js 에서 prefetch 를 사용하면 미리 데이터가 로드되어 페이지 로드 시간이 단축된다는데 꼭 Next.js 의 Link 컴포넌트가 아니면 달성이 불가능한 것인가? SSR 인 경우는 어떻게 되는가? 미리 불러오면 안 될 텐데? 와 같은 호기심이 발생했습니다. 어떻게 동작하는지 확인해 봅시다!
개요
Next.js 에서는 큰 문제가 없는 이상 <a> 보다는 <Link> Component를 사용하는 것이 더 나아 보입니다.
그런데 prefetch 에 대해서 아래의 의문이 생겼습니다.
- Next.js의- Link컴포넌트가 아니면 달성이 불가능한 것인가?
- SSR인 경우는 어떻게 되는가? 미리 불러오면 안 될 텐데
궁금증을 해결하기 위해서 프로젝트를 만들어서 실험해 보았습니다.
실험한 프로젝트 코드는 parkgang/next.js-prefetch 에서 확인할 수 있습니다.
코드를 보고 실제로 어떻게 동작하는지, 여러 컨셉을 시도해볼 수 있습니다!
Insight
Prod 에서만 활성화 된다.
어쩐지 열심히 테스트해도 안되길래 뭐지 했는데 Prod 에서만 활성화된다고 합니다.
Link 컴포넌트 이외 router.prefetch 으로도 미리 로드할 수 있다
공식 문서 router.prefetch 에 작성되어 있습니다.
코드의 컨셉을 보면
- 로그인하는 페이지에 들어왔을 때 미리- Dashboard경로의 정보를- prefetch하고
- 로그인이 완료되면 빠르게- Dashboard으로 넘어갈 수 있도록 하는 것입니다.
import { useCallback, useEffect } from "react";
import { useRouter } from "next/router";
export default function Login() {
  const router = useRouter();
  const handleSubmit = useCallback((e) => {
    e.preventDefault();
    fetch("/api/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        /* Form data */
      }),
    }).then((res) => {
      // Do a fast client-side transition to the already prefetched dashboard page
      if (res.ok) router.push("/dashboard");
    });
  }, []);
  useEffect(() => {
    // Prefetch the dashboard page
    router.prefetch("/dashboard");
  }, [router]);
  return (
    <form onSubmit={handleSubmit}>
      {/* Form fields */}
      <button type="submit">Login</button>
    </form>
  );
}
prefetch 라고 해서 SSR 이 미리 되는 것은 아니다. prefetch 는 정적 자료 만 미리 불러오는 것이다.
prefetch 는 정적 자료 만 미리 가져오며 prefetch 라고 해서 getServerSideProps 함수를 미리 수행하지 않는다고 합니다.
실험 결과 prefetch 시 PageComponent 에서 미리 사용할 수 있는 형태로 JS 을 가공해 놓는 것을 볼 수 있었습니다.
import SlowImages from "@/components/SlowImages";
import type { GetServerSideProps } from "next";
type PageProps = {
  ssrFinalizedDisplayDate: string;
};
export const getServerSideProps: GetServerSideProps<PageProps> = async () => {
  // prefetch과 관련 없이 SSR이 잘 이뤄지는지 보기위해서 의도적으로 기다립니다.
  await new Promise((resolve) => {
    setTimeout(() => {
      resolve(null);
    }, 3000);
  });
  return {
    props: {
      ssrFinalizedDisplayDate: new Date().toLocaleString("ko-KR"),
    },
  };
};
export default function SsrPage({
  ssrFinalizedDisplayDate: nowDateIso,
}: PageProps) {
  return (
    <div>
      <h1>SSR Page!</h1>
      <p>
        SSR이 완료된 시점은 <strong>{nowDateIso}</strong> 입니다.
      </p>
      <SlowImages />
    </div>
  );
}
Image 까지는 미리 불러오지 않는다.
실험 시 image 도 미리 불러오는지 궁금해서 확인해 보았는데 그렇지 않았습니다.
Next.js 측에서 Image 의 경우 Image 컴포넌트로 최적화하는 방법으로 사용하라는 것이면서도 사실 Image 는 미리 불러와서 이득이 없다고 판단한 거 같습니다.
페이지에 이미지가 몇 개일 지 모르는데 그걸 어떤 기준으로 모두 확보하느냐, 그래서 화면에 표시되는 이미지만 미리 처리한다 이런 컨셉 같습니다.
실험한 프로젝트에서 보면 Next.js 의 Image 컴포넌트를 사용하면 Image 를 불러오는 이미지 URL이 http://localhost:3000/_next/image?url= 형식으로 바뀌면서 알아서 캐시 되더군요.
prefetch 시 미리 불러오는 데이터 형식은?
아래와 같습니다.
SSG 는 아래와 같은 느낌으로 불러오더군요.
import SlowImages from "@/components/SlowImages";
export default function SsgPage() {
  return (
    <div>
      <h1>SSG Page!</h1>
      <p>ssg 페이지 입니다.</p>
      <SlowImages />
    </div>
  );
}
결론
- prefetch으로 미리 불러오는 데이터는- CSS,- JS이다.- image의 경우 미리 불러오지 않는다.
- CSS도 미리 불러오는지까지는 확인하지 못함
 
- prefetch되었다고 해서- SSR안 되는 것이 아니다.- getServerSideProps은- prefetch단계에서 수행되지 않는다.
 
마무리
이렇게 Next.js 의 prefetch 에서 헷갈릴 만한 것들을 알아보았습니다.
단순하게 미리 불러온다는 라는 개념보다 어떤 경우, 어떻게 불러오는지 알 수 있어 더 섬세하게 사용할 수 있을 거 같습니다.
읽어주셔서 감사합니다.