Data Fetching in Next.js - ISR

cansweep·2022년 9월 7일
2
post-thumbnail

Incremental Static Regeneration

Next.js allows you to create or update static pages after you’ve built your site. Incremental Static Regeneration (ISR) enables you to use static-generation on a per-page basis, without needing to rebuild the entire site. With ISR, you can retain the benefits of static while scaling to millions of pages.

Next.js는 사이트를 빌드한 후 정적 페이지를 생성하거나 업데이트하는 것을 허용한다.
ISR을 사용하면 전체 사이트를 재빌드할 필요없이 페이지별로 정적 생성할 수 있다.
또 수백만 페이지로 확장하면서도 정적의 이점을 유지할 수 있다.

To use ISR, add the revalidate prop to getStaticProps:

ISR을 사용하기 위해, getStaticPropsrevalidate prop을 추가한다.

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    // Next.js will attempt to re-generate the page:
    // - When a request comes in
    // - At most once every 10 seconds
    revalidate: 10, // In seconds
  }
}

// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// the path has not been generated.
export async function getStaticPaths() {
  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: blocking } will server-render pages
  // on-demand if the path doesn't exist.
  return { paths, fallback: 'blocking' }
}

export default Blog

When a request is made to a page that was pre-rendered at build time, it will initially show the cached page.

  • Any requests to the page after the initial request and before 10 seconds are also cached and instantaneous.
  • After the 10-second window, the next request will still show the cached (stale) page
  • Next.js triggers a regeneration of the page in the background.
  • Once the page generates successfully, Next.js will invalidate the cache and show the updated page. If the background regeneration fails, the old page would still be unaltered.

build time에 사전 렌더링된 페이지에 대한 요청이 들어오면 처음에는 캐시된 페이지를 보여준다.

  • 최초 요청 이후, 설정한 시간(위 코드에서는 10초) 전에 페이지에 대한 요청이 들어오면 캐시된 페이지를 보여준다.
  • 설정한 시간이 지나도 다음 요청에 대해서는 여전히 캐시된 페이지를 보여준다.
  • Next.js는 백그라운드에서 페이지 재생성을 트리거한다.
  • 페이지가 성공적으로 생성되면 Next.js는 캐시되었던 페이지를 무효화하고 새로 업데이트된 페이지를 보여준다.
    만약 페이지 재생성이 실패했다면 이전에 캐시되었던 페이지를 보여준다.

    정리하자면, 설정한 시간이 지나기 전까지는 페이지에 대한 요청이 들어오면 캐시된 페이지를 보여준다.
    설정한 시간이 지나고 요청이 들어오면 캐시되어있던 페이지를 보여준 뒤 백그라운드에서 페이지를 재생성한다.
    그리고 페이지 생성이 완료되면 새로 만든 페이지로 갈아끼운다.

When a request is made to a path that hasn’t been generated, Next.js will server-render the page on the first request. Future requests will serve the static file from the cache. ISR on Vercel persists the cache globally and handles rollbacks.

아직 생성되지 않은 경로에 대한 요청이 있을 때, Next.js는 첫번째 요청에서 서버 렌더링을 한다.
이후 요청은 캐시에서 정적 파일을 제공한다.
Vercel의 ISR은 캐시를 전역적으로 유지하고 롤백을 처리한다.

On-demand Revalidation

If you set a revalidate time of 60, all visitors will see the same generated version of your site for one minute. The only way to invalidate the cache is from someone visiting that page after the minute has passed.

만약 재검증 시간을 60초로 지정했다면 모든 방문자는 1분 동안 동일한 버전의 사이트를 보게 된다.
캐시를 무효화하는 유일한 방법은 누군가 1분이 지난 후 페이지를 방문하는 것이다.

Starting with v12.2.0, Next.js supports On-Demand Incremental Static Regeneration to manually purge the Next.js cache for a specific page. This makes it easier to update your site when:

  • Content from your headless CMS is created or updated
  • Ecommerce metadata changes (price, description, category, reviews, etc.)

Next.js의 12.2.0버전부터 특정 페이지에 대해 Next.js의 캐시를 수동으로 제거하는 On-Demand Incremental Static Regeneration을 지원한다.


이 방식은 다음의 경우에 당신의 사이트를 더 쉽게 업데이트해줄 수 있다.

  • headless CMS의 content가 생성되거나 업데이트될 때
  • 전자상거래 메타데이터 변경(가격, 설명, 카테고리, 리뷰 등)

Inside getStaticProps, you do not need to specify revalidate to use on-demand revalidation. If revalidate is omitted, Next.js will use the default value of false (no revalidation) and only revalidate the page on-demand when revalidate() is called.

getStaticProps 안에서, on-demand revalidation을 사용하기 위해 revalidate를 명시할 필요가 없다.
revalidate가 생략되었다면 Next.js는 디폴트값인 false(no revalidation)을 사용하고 revalidate() 함수가 호출되었을 때에만 페이지를 재검증할 것이다.

Note: Middleware won't be executed for On-Demand ISR requests. Instead, call revalidate() on the exact path that you want revalidated. For example, if you have pages/blog/[slug].js and a rewrite from /post-1 -> /blog/post-1, you would need to call res.revalidate('/blog/post-1').

미들웨어는 On-Demand ISR 요청에 대해 실행되지 않는다.
대신에 재검증하고자 하는 정확한 경로에 revalidate() 함수를 호출할 수 있다.
예를 들어, pages/blog/[slug].js가 있고 /post-1의 경로를 /blog/post-1로 rewrite하고 싶다면 res.revalidate('/blog/post-1')를 호출할 수 있다.

Using On-Demand Revalidation

First, create a secret token only known by your Next.js app. This secret will be used to prevent unauthorized access to the revalidation API Route. You can access the route (either manually or with a webhook) with the following URL structure:

  1. Next.js 앱에서만 알 수 있는 비밀 토큰을 만든다.
    이 비밀 토큰은 재검증할 API 경로에 대해 무단 접근을 방지하는데 사용된다.
    다음 URL 구조를 사용하여 경로에 접근할 수 있다.
https://<your-site.com>/api/revalidate?secret=<token>

Next, add the secret as an Environment Variable to your application. Finally, create the revalidation API Route:

  1. 비밀 토큰을 환경변수에 저장한다.
  2. 재검증 API 경로를 생성한다.
// pages/api/revalidate.js
export default async function handler(req, res) {
  // Check for secret to confirm this is a valid request
  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  try {
    // this should be the actual path not a rewritten path
    // e.g. for "/blog/[slug]" this should be "/blog/post-1"
    await res.revalidate('/path-to-revalidate')
    return res.json({ revalidated: true })
  } catch (err) {
    // If there was an error, Next.js will continue
    // to show the last successfully generated page
    return res.status(500).send('Error revalidating')
  }
}

Testing on-Demand ISR during development

When running locally with next dev, getStaticProps is invoked on every request. To verify your on-demand ISR configuration is correct, you will need to create a production build and start the production server:

$ next build
$ next start

Then, you can confirm that static pages have successfully revalidated.

next dev를 사용해 로컬에서 실행할 때, getStaticProps는 모든 요청에 대해 호출된다.
on-demand ISR 설정이 올바른지 확인하기 위해서는 프로덕션 빌드를 생성하고 프로덕션 서버를 시작해야 한다.

$ next build
$ next start

그러면 정적 페이지가 성공적으로 재검증되었는지 확인할 수 있다.

Error handling and revalidation

If there is an error inside getStaticProps when handling background regeneration, or you manually throw an error, the last successfully generated page will continue to show. On the next subsequent request, Next.js will retry calling getStaticProps.

백그라운드에서 페이지 재생성을 처리할 때 getStaticProps 내부에서 error가 발생하거나 수동으로 error를 던진 경우, 페이지를 만드는데 성공한 마지막 페이지를 보여준다.
이후 요청이 들어오면 Next.js는 getStaticProps를 다시 호출한다.

export async function getStaticProps() {
  // If this request throws an uncaught error, Next.js will
  // not invalidate the currently shown page and
  // retry getStaticProps on the next request.
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  if (!res.ok) {
    // If there is a server error, you might want to
    // throw an error instead of returning so that the cache is not updated
    // until the next successful request.
    throw new Error(`Failed to fetch posts, received status ${res.status}`)
  }

  // If the request was successful, return the posts
  // and revalidate every 10 seconds.
  return {
    props: {
      posts,
    },
    revalidate: 10,
  }
}

관련 링크

Next.js 공식문서
++) ISR self hosting 나중에 추가 예정!

profile
하고 싶은 건 다 해보자! 를 달고 사는 프론트엔드 개발자입니다.

0개의 댓글