- 최초로 프로젝트를 빌드하는 시점에 이미 DB에 존재하는 포스트들은 미리 pre-render 해둔다.
이 때, Next.js가 지원하는 getStaticPaths(https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation)를 사용하여 pre-render 할 페이지의 목록을 알려준다.
export async function getStaticPaths() {
// 뉴스(type === 1) 목록을 불러오는 graphql 쿼리
const { data } = await client.query({
query: gql`
query {
getPostsPage(input: { type: 1 }) {
result {
id
}
}
}
`,
});
const paths = data.getPostsPage.result.map((news: News) => ({ params: { id: String(news.id) } }));
return { paths, fallback: 'blocking' };
}
/** Next.js 공식문서
export async function getStaticPaths() {
return {
paths: [
{ params: { ... } } // See the "paths" section below
],
fallback: true, false, or 'blocking' // See the "fallback" section below
};
}
*/
뉴스의 id 속성을 paths 에 넘기면 해당 id들은 build time에 pre-render 된다.
fallback: 'blocking' 은 build time에 만들어지지 않은 페이지에 대한 요청이 들어왔을 때, 새로운 html을 만들기 전까지 페이지 이동을 blocking 하겠다는 뜻이다. (예를 들어 news/124 라는 새로운 페이지(build time에 pre-render하지 않은)에 접근했을 때, news/124 에 해당하는 html이 만들어지기 전까지는 뉴스 목록페이지에서 로딩 상태로 blocking 되어있다)
- path(url)가 만들어졌으므로 각 url마다 필요한 포스트 내용을 GET 한 뒤 pre-render 해야한다. Next.js가 지원하는 getStaticProps 를 사용하여 페이지에서 사용할 'props'를 return한다.(https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation)
이 때, 포스트가 존재하지 않는다면(data.getPost === null) notFound를 return하여 404페이지로 이동시킨다.
export async function getStaticProps({ params }: { params: { id: string } }) {
const id = Number(params.id);
const { data } = await client.query({
errorPolicy: 'ignore',
query: gql`
query {
getPost(id: ${id}) {
id
type
title
body
createdAt
}
getPrevPost(input: { type: 1, id: ${id} }) {
id
title
}
getNextPost(input: { type: 1, id: ${id} }) {
id
title
}
}
`,
});
if (data.getPost === null) {
return {
notFound: true,
};
}
return {
props: {
news: data.getPost,
prev: data.getPrevPost,
next: data.getNextPost,
},
};
}
/** Next.js 공식문서
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
*/
- 일단 html이 만들어지면 SSG 특성상 다음 요청부터는 만들어진 html을 그대로 응답한다. 하지만 배포 후 DB에서 포스트 내용이 수정될 경우, html을 갱신해야 올바른 페이지를 볼 수 있다.
return {
props: {
news: data.getPost,
prev: data.getPrevPost,
next: data.getNextPost,
},
revalidate: 10, // seconds
};
이때 revalidate 속성을 추가하면 html을 regenerate 할 수 있다.
html이 만들어지고 10초 동안은 사용자에게 같은 html파일을 그대로 응답한다. 10초가 지나고 GET 요청이 오면, 기존 html을 응답하면서 동시에 새로운 html을 regenerate 한다. 그리고 다음 GET 요청부터는 갱신된 html 문서를 응답한다.
- 포스트 상세페이지에 revalidate 옵션을 추가하여, 어드민 사이트에서 포스트 내용을 수정해도 홈페이지의 html이 자동 갱신되도록 했다!