스넙 블로그

Data-fetching

Next.js의 getStaticPropsgetStaticPaths api를 사용하면 static 페이지를 생성할 수 있다.

  • getStaticProps: 빌드 시 데이터를 fetch하여 static 페이지를 생성
  • getStaticPaths: pages/**/[id].tsx 형태의 동적 라우팅 페이지 중, 빌드 시에 static하게 생성할 페이지를 정함

getServerSideProps라는 것도 있는데, static이 아니라 server side rendering이므로 제외함

getStaticProps

페이지.tsx에서 export async function getStaticProps(context) 형태로 사용.

context.params로 동적 라우팅의 경로 이름을 가져온다.

pages/posts/[id].tsx 페이지를 /posts/123으로 접근했다면 context.params{ id: "123" }이 된다.

{ props: object }를 리턴하면, export default한 리액트 컴포넌트의 parameter로 넘겨진다.

import type { GetStaticProps, InferGetStaticPropsType } from "next";
// GetStaticProps는 getStaticProps의 타입
// InferGetStaticPropsType은 컴포넌트에 사용되는 제네릭 타입

type Post = {
  title: string;
}

export const getStaticProps: GetStaticProps = async (context) => {
  // async여야 함

  const response = await fetch("https://example.com");
  const post: Post = await response.json();
  // Next.js가 폴리필하는 Fetch API
  // https://nextjs.org/docs/basic-features/supported-browsers-features#polyfills

  return {
    props: {
      post
    }
    // props는 현재 페이지의 default export 컴포넌트의 parameter로 넘겨짐
  }
}

const function Blog = ({ post }: InferGetStaticPropsType<typeof getStaticProps>) => {
  // { post: Post }로 타입 지정됨
  return (
    <div>{post.title}</div>
  )
}

export default Blog;

getStaticProps가 필요할 때

When should I use getstaticprops?

  • 페이지에 필요한 데이터가 빌드 시에 사용 가능할 때
  • 데이터를 headless CMS에서 가져올 때
  • 모든 사용자에게 같은 데이터를 보여줄 때
  • SEO를 위해서 속도 빠른 페이지가 필요할 때
  • 공식 문서에는 없지만 Node api(path, fs 등)을 사용해야 할 때
import path from "path";

export const getStaticProps = () => {
  const postDirectory = path.resolve(process.cwd(), '/posts');
  // process.cwd()는 Next.js 폴더를 가리킴(package.json이 있는 폴더)
  // ... 
}

getStaticPaths

동적 라우팅을 사용할 때, 어떤 페이지를 미리 Static으로 빌드할 지 정하는 api

import type { GetStaticPaths } from "next";
// GetStaticPaths는 getStaticPaths의 타입

export const getStaticPaths: GetStaticPaths = async () => {
  // getStaticProps처럼 async
  // ...

  return {
    paths: [
      { params: {} }
    ],
    // { parmas: {} }[] 형태로 paths 리턴해야 함
    // 빌드 시에 해당 페이지들을 static으로 생성
    fallback: true | false | 'blocking'
    // fallback을 리턴해야 함
  }

  // 예시
  return {
    paths: [
      { params: { id: "1" }},
      { params: { id: "2" }}
    ]
    // pages/posts/[id].tsx라고 가정
    // pages/posts/1과 pages/posts/2를 static으로 생성
  }

  // 예시 2
  return {
    paths: [
      {
        params: {
          id: "1",
          title: "first post"
        }
      },
      {
        params: {
          id: "2",
          title: "second post"
        }
      }
    ]
    // pages/posts/[id]/[title].tsx라고 가정
    // pages/posts/1/first post와 pages/posts/2/second post/를 static으로 생성
  }
}

fallback(required)의 리턴 값에 따른 차이

fallback: false

getStaticPaths에서 리턴하지 않은 페이지는 모두 404로 연결

fallback: true

getStaticPaths에서 리턴하지 않은 페이지에 접속 시,

  1. 먼저 사용자에게 fallback 페이지를 보여줌
  2. 서버에서 static하게 페이지를 생성함
  3. 해당 페이지를 사용자에게 보여줌
  4. 다음부터 해당 페이지로 접속하는 사용자에게는 static한 페이지를 보여줌
// pages/posts/[id].tsx
import { useRouter } from "next/router";
// fallback 페이지를 위한 useRouter import

const Post = ({ post }) => {
  cont router = useRouter();

  if (router.isFallback) {
    return <div>Loading...</div>
    // fallback = true 시, 접속한 페이지를 생성할 때 보여주는 페이지
  }

  return (
    <div>{post.title}</div>
  )
}

export default Post;

export const getStaticPaths = async () => {
  return {
    paths: [
      { params: { id: 1} }
    ],
    fallback: true
  }
}

export const getStaticProps = async ({ params }) => {
  const response = await fetch(`https://example.com/${params.id}`);
  const post = response.json();

  return {
    props: { post }
  }
}

많은 static 페이지를 생성해야 하지만 빌드 시간이 너무 오래 걸릴 때 사용

fallback: "blocking"

getStaticPaths에서 리턴하지 않은 페이지에 접속 시,

  1. 사용자에게 server side rendering한 static 페이지를 보여줌
  2. 다음부터 해당 페이지로 접속하는 사용자에게는 server side rendering한 static 페이지를 보여줌

fallback 페이지나 로딩 화면이 없다.

동적 라우팅 페이지를 static 페이지로 제공해야 할 때 사용