[Next.js] ISR

찐새·2022년 6월 30일
2

next.js

목록 보기
31/41
post-thumbnail

Incremental Static Regeneration(ISR)

SWR은 레이아웃이 아닌 데이터를 보여주기까지 로딩 시간이 생긴다. 클라이언트는 로딩이 완료될 때까지 레이아웃만 구경할 수 있다.

SSR(ServerSideRendering)은 레이아웃과 데이터를 로딩 없이 한 번에 보여줄 수 있지만, 백엔드에 문제가 생겼을 때 빈 화면만 보여주게 된다.

각각 장단점이 있는데, 이러한 단점들을 상쇄하는 기능이 ISR이다. 사이트를 빌드할 때 보여질 페이지를 정적 페이지로 pre-render하게끔 빌드한다.

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

export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()
  
  return {
    props: {
      posts,
    },
  }
}

유저가 데이터가 로드될 때까지 기다릴 필요도 없고, 미리 렌더링 되었으므로 서버에 문제가 생겨도 페이지는 보여진다. 하지만 단점도 있다. 정적 페이지이기 때문에 최신 데이터는 로드하지 못한다. 이런 부분은 revalidate 프로퍼티로 커버한다.

revalidate

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
  }
}

주석을 보면,

Next.js는 요청이 들어왔을 때 최소 10초에 한 번씩 re-generate를 시도한다.

라고 되어있다. 만약 유저 A가 처음에 들어왔다면 최초 데이터가 생성된 정적 페이지 1을 보게 된다. 8초에 들어온 유저 B 역시 A가 보는 페이지를 본다. 딱 10초에 들어온 C도 1을 보지만, 10초가 지났으므로 Next.js는 페이지를 다시 빌드한다. 11초 후에 들어온 D는 업데이트된 페이지 2를 보는 것이다.

revalidate 시간의 경우, 데이터가 업데이트되는 시간을 기준으로 잡는다. 너무 길게 잡으면 유저가 구 데이터를 오랫동안 봐야할 지 모르고, 너무 짧게 잡으면 비용 낭비가 심해질 수도 있다. 고민해서 선택할 부분이다.

On Demand Revalidation(ODR)

next@12.2.0부터는 베타가 아니지만, 아직 사용방법을 몰라 베타 버전인 next@12.1.5를 기준으로 작성한다.

getStaticProps에서 revalidate10으로 설정했다면, 모든 유저는 최초의 10초 동안 같은 페이지를 보게 될 것이다. 그 사이 새 글이 100개가 올라와도 마찬가지다. ODRapi로부터 revaildate를 수동으로 설정할 수 있게 해준다.

// /api/posts.js
import { NextApiRequest, NextApiResponse } from "next";

export default async function api(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method === "POST"){
    const posts = await db.posts.create({});
  	await res.unstable_revalidate("/posts");
	res.json({
      status: 200,
      posts,
    });
  }
}

next@12.2.0에서는 unstable_revalidate()revalidate()로 바뀌었다.

POST 요청으로 게시물을 작성하면 곧장 revalidate를 실행해 최신 데이터로 re-build한다.

fallback

getStaticPaths가 return하는 fallbackfalse, true, blocking이 있다.

fallback: "blocking"

로딩 화면을 노출하기 싫어 [id].jsx 등과 같은 페이지를 정적으로 생성한다면, 데이터가 많을 때 문제가 될 수 있다. 예를 들어, 1만 개의 페이지를 정적으로 미리 빌드한다는 것은 매우 비효율적인 일이다. 이럴 때는 getStaticPaths의 return을 paths:[], fallback:"blocking"으로 설정해 빌드한다.

export function getStaticPaths() {
  return {
    paths: [],
    fallback: "blocking",
  };

빌드 결과, paths:[], fallback:"blocking"의 경우 [id]임에도 미리 생성된 정적 페이지가 없음을 알 수 있다. 유저가 해당 페이지를 방문하면 그때 필요한 페이지를 build한다. 이후 접근하는 유저는 build없이 볼 수 있다.

fallback: false

export function getStaticPaths() {
  return {
    paths: [],
    fallback: false,
  };

fallback:false는 미리 생성된 정적 페이지가 없으면 404페이지를 반환한다.

fallback: true

export default function Blog({ posts }) {
    if (router.isFallback) {
    return (
      <div>
        <span>I love you</span>
      </div>
    );
  }
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

export function getStaticPaths() {
  return {
    paths: [],
    fallback: true,
  };

fallback:true는 페이지가 빌드되는 동안 유저가 로딩 화면을 보게끔 설정할 수 있다.


참고
노마드 코더 - 캐럿마켓 클론코딩
Next.js Docs - ISR

profile
프론트엔드 개발자가 되고 싶다

1개의 댓글

comment-user-thumbnail
2022년 7월 13일

깔끔한 정리 너무 감사합니다!

답글 달기