Next.js Data Fetching 정리

NoName·2021년 12월 15일
0

Static Generation vs Server-side Rendering

공식문서에서 서술하는 가장 큰 차이점은 Static Generation은 빌드 타임에 HTML을 만들어 매 요청마다 미리 만들어둔 HTML을 재사용 한다는 것이고 Server-side Rendering은 매 요청마다 HTML을 만든다는 것입니다.


getStaticProps

정의

getStaticProps라는 비동기 함수를 export하면 Next.js는 이 페이지를 빌드타임에 이 함수에서 리턴한 값을 컴포넌트에 props로 전달합니다.

context 인자

params: 동적 라우트에서 사용하는 파라미터입니다. 예를들어, 파일명이 [id].js라면 params객체는 { id: ... } 형태로 구성됩니다. 이는 getStaticPaths와 함께 사용할 수 있습니다.

preview: true인 경우 페이지는 Preview Mode로 간주됩니다. (Preview Mode란?)

previewData: setPreviewData로 세팅한 데이터입니다. (Preview Mode 문서 참조)

locale: 사용할 locale입니다. (Internationalized Routing 사용시).

locales: 지원되는 모든 locale입니다. (Internationalized Routing 사용시).

defaultLocale: 기본값으로 설정한 locale입니다. (Internationalized Routing 사용시).

리턴하는 객체

props: 페이지 컴포넌트에서 받는 props입니다.

revalidate: 데이터의 최신 여부를 검사하여 페이지를 다시 업데이트할 주기(초단위)값입니다. false인 경우 페이지를 갱신하지 않는다는 의미이므로 빌드한 페이지가 다음 빌드 이전까지 그대로 캐싱됩니다.

revalidate를 10으로 설정했다고 가정해보면,
해당 페이지를 처음 요청하고 10초가 지나지 않았을 때는 캐싱된 페이지를 보여준다.
10초가 지나고 다시 페이지를 요청했을 때도 여전히 캐싱된 페이지를 보여주지만 Next.js는 백그라운드에서 페이지가 regeneration 하도록 한다.
한번 더 요청했을 때 regeneration에 성공했으면 업데이트된 페이지를 보여주고 실패했으면 캐싱된 페이지를 보여줍니다.

참고영상

notFound: 404 상태, 페이지를 리턴할 것인지 정합니다. 'fallback: false'일 땐 사용하지 않아도 됩니다. 'notFound: true'일 때는 만들어진 페이지가 이전에 있었더라도 404 페이지로 이동합니다. 이는 블로그에서 작성자가 글을 삭제했을 때와 같은 경우를 지원하기 위한 목적입니다.

export async function getStaticProps(context) {
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  if (!data) {
    return {
      notFound: true,
    }
  }

  return {
    props: { data }, // will be passed to the page component as props
  }
}

redirect: 내/외부 리소스로 redirect 할 때 사용합니다. { destination: string, permanent: boolean }의 형태로 정의해야 합니다. permanent 대신 statusCode 값을 사용할 수도 있습니다.

export async function getStaticProps(context) {
  const res = await fetch(`https://...`)
  const data = await res.json()

  if (!data) {
    return {
      redirect: {
        destination: '/',
        permanent: false,
      },
    }
  }

  return {
    props: { data }, // will be passed to the page component as props
  }
}

언제 사용할까?

  • 사용자가 요청과 무관하게 빌드타임에 사용할 수 있는 데이터로 렌더링되는 페이지 일 때
  • 데이터를 headless CMS에서 받아올 때 (headless CMS란?)
  • 데이터를 공용으로 캐싱할 수 있을 때 (사용자별로 캐싱되는 게 아니라)
  • SEO를 위해 프리렌더 되어야 하고 렌더링 속도가 빨라야 하는 페이지 일때 (getStaticProps는 HTML, JSON 파일을 만들고 이는 CDN에 캐싱될 수 있다)

예제

// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries. See the "Technical details" section.
export async function getStaticProps() {
  // Call an external API endpoint to get posts.
  // You can use any data fetching library
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts,
    },
  }
}

export default Blog;

참고사항

  • getStaticProps는 server-side에서만 동작합니다. client-side에서 동작하지 않기 때문에 JS 번들에 포함되지 않습니다.
  • 파일을 읽을때는 __dirname 대신 process.cwd()를 사용해야 합니다.
  • getStaticProps는 page 컴포넌트에서만 사용할 수 있습니다.
  • next dev로 실행했을 때 getStaticProps는 매 요청마다 호출됩니다.


getStaticPaths

정의

페이지에 동적 라우트가 있고 getStaticProps를 사용하는 경우 빌드시 HTML로 렌더링해야하는 경로들을 정의해야합니다. 동적 경로를 사용하는 페이지에서 getStaticPaths라는 비동기 함수를 export하면 지정한 모든 경로를 정적으로 프리렌더합니다.

리턴하는 객체

paths: paths는 프리렌더할 경로들을 정의합니다.

// pages/posts/[id].js
return {
  paths: [
    { params: { id: '1' } },
    { params: { id: '2' } }
  ],
  fallback: ...
}

// pages/posts/[postId]/[commentId].js
return {
  paths: [
    { params: { postId: '1', commentId: '1' } },
    { params: { postId: '1', commentId: '1' } }
  ],
  fallback: ...
}

// pages/posts/[...slug].js
return {
  paths: [
    { params: { slug: ['foo', 'bar'] } },
  ],
  fallback: ...
}

// pages/posts/[[...slug]].js (optional)
return {
  paths: [
    { params: { slug: ['foo', 'bar'] } },
    { params: { slug: null | [] | undefined | false } } // redirect to '/'
  ],
  fallback: ...
}

fallback
fallback: false인 경우, getStaticPaths에서 리턴받지 않은 경로는 모두 404페이지로 리다이렉트된다. 프리렌더해야하는 경로가 적은 경우에 주로 사용한다(모든 경로가 빌드 시간 내에 정적으로 생성될 수 있기 때문). 새로운 페이지가 자주 추가되지 않는 경우에 유용하다. 새로운 경로가 추가됐다면(데이터가 변해서) 다시 빌드해야한다.

export async function getStaticPaths() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // Get the paths we want to pre-render based on posts
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // We'll pre-render only these paths at build time.
  // { fallback: false } means other routes should 404.
  return { paths, fallback: false }
}

fallback: true는 정적인 페이지가 매우 많은 경우에 사용합니다(거대한 e-commerce 같은). 모든 페이지를 프리렌더하려고 하면 빌드가 영원히 끝나지 않을지도 모릅니다. 대신 일부분의 페이지만 정적으로 생성하고 나머지는 fallback: true를 사용할 수도 있습니다. 생성되지 않은 페이지는 로딩을 거쳐 렌더링 될 것입니다.

fallback: true인 경우 getStaticProps는 아래와 같이 동작하게 됩니다.
1. getStaticPaths에서 리턴된 경로는 getStaticProps에 의해 빌드타임에 HTML로 렌더됩니다.
2. 빌드타임에 생성되지 않은 경로는 404페이지를 보여주지 않고 fallback page를 보여줍니다.
3. 백그라운드에서는 요청받은 경로에 대한 HTML과 JSON을 정적으로 생성하며, 이 과정에서 getStaticProps도 동작합니다.
4. 이 과정이 끝나면 브라우저는 생성된 경로를 JSON으로 받으며 이 파일은 필요한 props와 함께 페이지를 렌더하는데 사용됩니다. 이 때가 fallback page가 온전한 페이지로 대체되는 때입니다.
5. 동시에 Next.js는 프리렌더 페이지 목록에 이 경로를 추가합니다. 이후 들어오는 요청에서는 프리렌더 된 페이지와 동일하게 보여질 것 입니다.

// pages/posts/[id].js
import { useRouter } from 'next/router'

function Post({ post }) {
  const router = useRouter()

  // If the page is not yet generated, this will be displayed
  // initially until getStaticProps() finishes running
  if (router.isFallback) {
    return <div>Loading...</div>
  }

  // Render post...
}

// This function gets called at build time
export async function getStaticPaths() {
  return {
    // Only `/posts/1` and `/posts/2` are generated at build time
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    // Enable statically generating additional pages
    // For example: `/posts/3`
    fallback: true,
  }
}

// This also gets called at build time
export async function getStaticProps({ params }) {
  // params contains the post `id`.
  // If the route is like /posts/1, then params.id is 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // Pass post data to the page via props
  return {
    props: { post },
    // Re-generate the post at most once per second
    // if a request comes in
    revalidate: 1,
  }
}

export default Post

fallback: blocking인 경우, getStaticPaths에서 리턴받지 못한 새로운 경로는 Server-side 렌더링과 동일하게 HTML이 생성될 때까지 기다렸다가 이후 요청을 위해 캐싱될 것입니다. 그렇기 때문에 이는 경로별로 한번만 발생합니다.

fallback: blocking인 경우 getStaticProps는 아래와 같이 동작하게 됩니다.
1. getStaticPaths에서 리턴된 경로들은 빌드타임에 getStaticProps에 의해 HTML로 렌더됩니다.
2. 빌드타임에 생성되지 않은 경로들에 대해서 404 페이지를 보여주지는 않습니다. 대신 Next.js는 첫 번째 요청에 Server-side 렌더링을 진행하고 생성된 HTML을 리턴합니다.
3. 작업이 끝나면 브라우저는 생성된 경로의 HTML을 전달 받습니다. 사용자 입장에서는 브라우저가 페이지를 요청하면 페이지가 모두 로드된 상태로 보입니다. 로딩 때문에 깜빡이거나 Fallback Page가 보이는 일은 없습니다.
4. 동시에 Next.js는 프리렌더 페이지 목록에 이 경로를 추가합니다. 이후 들어오는 요청에서는 프리렌더 된 페이지와 동일하게 보여질 것 입니다.

참고영상

참고사항

  • 동적 라우트와 getStaticProps를 사용한다면 반드시 getStaticPaths를 사용해야 합니다.
  • getStaticPaths는 server-side에서 빌드 타임에만 동작합니다.
  • getStaticPaths는 page 컴포넌트에서만 사용할 수 있습니다.
  • next dev로 실행했을 때 getStaticPaths는 매 요청마다 호출됩니다.


getServerSideProps

정의

getServerSideProps라는 비동기 함수를 페이지에서 export할 경우 Next.js는 이 페이지를 매 요청마다 getServerSideProps로부터 리턴 받은 데이터를 사용하여 프리렌더합니다.

context 인자

params: 동적 라우트에서 사용하는 파라미터입니다. 예를들어, 파일명이 [id].js라면 params객체는 { id: ... } 형태로 구성됩니다. 이는 getStaticPaths와 함께 사용할 수 있습니다.

req: HTTP IncomingMessage 객체

res: HTTP response 객체

query: 쿼리스트링

preview: true인 경우 페이지는 Preview Mode로 간주됩니다. (Preview Mode란?)

previewData: setPreviewData로 세팅한 데이터입니다. (Preview Mode 문서 참조)

locale: 사용할 locale입니다. (Internationalized Routing 사용시).

locales: 지원되는 모든 locale입니다. (Internationalized Routing 사용시).

defaultLocale: 기본값으로 설정한 locale입니다. (Internationalized Routing 사용시).

resolvedUrl: 클라이언트 전환을 위해 _next / data 접두사를 제거하고 원래 쿼리 값을 포함하는 정규화된 URL 버전이다.

리턴하는 객체

props: 페이지 컴포넌트에서 받는 props입니다.

notFound: 404 상태, 페이지를 리턴할 것인지 정합니다. 'fallback: false'일 땐 사용하지 않아도 됩니다. 'notFound: true'일 때는 만들어진 페이지가 이전에 있었더라도 404 페이지로 이동합니다. 이는 블로그에서 작성자가 글을 삭제했을 때와 같은 경우를 지원하기 위한 목적입니다.

언제 사용할까?

  • 데이터를 가져와야만 하는 프리렌더 페이지일 때
  • 데이터를 프리렌더할 필요가 없다면 클라이언트 측에서 데이터를 가져오는 방법을 고려해보아야 합니다.

예제

function Page({ data }) {
  // Render data...
}

// This gets called on every request
export async function getServerSideProps() {
  // Fetch data from external API
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  // Pass data to the page via props
  return { props: { data } }
}

export default Page

참고사항

  • getServerSideProps는 server-side에서 빌드 타임에만 동작합니다.
  • getServerSideProps는 page 컴포넌트에서만 사용할 수 있습니다.
profile
I don't know js

1개의 댓글

comment-user-thumbnail
2021년 12월 16일

굳!

답글 달기