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
프로퍼티로 커버한다.
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
시간의 경우, 데이터가 업데이트되는 시간을 기준으로 잡는다. 너무 길게 잡으면 유저가 구 데이터를 오랫동안 봐야할 지 모르고, 너무 짧게 잡으면 비용 낭비가 심해질 수도 있다. 고민해서 선택할 부분이다.
next@12.2.0
부터는 베타가 아니지만, 아직 사용방법을 몰라 베타 버전인next@12.1.5
를 기준으로 작성한다.
getStaticProps
에서 revalidate
를 10
으로 설정했다면, 모든 유저는 최초의 10초 동안 같은 페이지를 보게 될 것이다. 그 사이 새 글이 100개가 올라와도 마찬가지다. ODR
은 api
로부터 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한다.
getStaticPaths
가 return하는 fallback
은 false
, true
, blocking
이 있다.
로딩 화면을 노출하기 싫어 [id].jsx
등과 같은 페이지를 정적으로 생성한다면, 데이터가 많을 때 문제가 될 수 있다. 예를 들어, 1만 개의 페이지를 정적으로 미리 빌드한다는 것은 매우 비효율적인 일이다. 이럴 때는 getStaticPaths
의 return을 paths:[]
, fallback:"blocking"
으로 설정해 빌드한다.
export function getStaticPaths() {
return {
paths: [],
fallback: "blocking",
};
빌드 결과, paths:[]
, fallback:"blocking"
의 경우 [id]
임에도 미리 생성된 정적 페이지가 없음을 알 수 있다. 유저가 해당 페이지를 방문하면 그때 필요한 페이지를 build
한다. 이후 접근하는 유저는 build
없이 볼 수 있다.
export function getStaticPaths() {
return {
paths: [],
fallback: false,
};
fallback:false
는 미리 생성된 정적 페이지가 없으면 404
페이지를 반환한다.
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
는 페이지가 빌드되는 동안 유저가 로딩 화면을 보게끔 설정할 수 있다.
깔끔한 정리 너무 감사합니다!