페이지에서 정적 생성(Static Generation)을 사용하는 경우 페이지 HTML은 빌드(build) 시 생성된다. 즉, production에서는 next build를 실행할때 HTML이 생성된다. 이 HTML은 각 요청마다 재사용 된다. CDN으로 캐시할 수 있다.
Next.js에서는 데이터 있거나 없는 페이지를 정적으로 생성할 수 있다. 각각의 경우를 살펴보자.
기본적으로 Next.js는 데이터를 가져오지 않고 정적 생성을 사용하여 페이지를 사전 렌더링(pre-rendering)한다. 다음은 예시이다.
function About() {
return <div>About</div>
}
export default About
이 페이지는 사전 렌더링하기 위해 외부 데이터를 가져올 필요가 없다. 이런 경우 Next.js는 빌드 시간 동안 페이지당 단일 HTML 파일을 생성한다.
일부 페이지에서는 사전 렌더링을 위해 외부 데이터를 가져와야 한다. 두 가지 시나리오가 있으며 하나 또는 둘 다 적용될 수 있다. 각 경우에 Next.js가 제공하는 함수를 사용할 수 있다.
페이지 콘텐츠가 외부 데이터에 따라 다르다면 getStaticProps를 사용.
페이지 경로(path)는 외부 데이터에 따라 다르다면 getStaticPaths사용(일반적으로 getStaticProps와 함께)
Scenario 1: 페이지가 콘텐츠가 외부 데이터에 따라 달라짐
예시: 블로그 페이지는 CMS(content management system)에서 블로그 게시물 목록을 가져와야 할 수 있다.
// TODO: Need to fetch `posts` (by calling some API endpoint)
// before this page can be pre-rendered.
export default function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
사전 렌더링 시 이 데이터를 가져오기 위해서 NEXT.JS를 사용하면 동일한 파일에 getStaticProps라는 비동기 함수를 export할 수 있다.
이 함수는 빌드 시 호출되며 사전 렌더링 시 가져온 데이터를 페이지 props에 전달할 수 있다.
export default function Blog({ posts }) {
// Render posts...
}
// This function gets called at build time
export async function getStaticProps() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// { props: { post } }를 반환함으로써 Blog 컴포넌트는 빌드 시 'posts'를 prop으로 전달 받는다.
return {
props: {
posts,
},
}
}
Scenario 2: 페이지 경로(path)가 외부 데이터에 따라 달라짐
Next.js를 사용하면 동적 경로가 있는 페이지를 만들 수 있다. 예를 들어 /pages/posts/[id].js라는 파일을 만들어 id를 기반으로 단일 블로그 게시물을 표시할 수 있다. 이렇게 하면 posts/1에 접근할 때 id가 1인 블로그 게시물을 표시할 수 있다.
그러나 빌드 시 사전 렌더링하려는 id는 외부 데이터에 따라 달라질 수 있다.
예: 데이터베이스에 id가 1인 하나의 블로그 게시물만 추가했다고 가정하자. 이 경우 빌드 시 posts/1만 사전 렌더링 하면 된다.
나중에 id가 2인 두 번째 게시물을 추가할 수 있다. 그런 다음 posts/2도 사전 렌더링을 하고 싶을 것이다.
따라서 사전 렌더링된 페이지 경로는 외부 데이터에 따라 달라진다. 이를 처리하기 위해Next.js를 사용하면 동적 페이지(pages/posts/[id].js)에서 getStaticPaths라는 비동기 함수를 export할 수 있다. 이 함수는 빌드 시 호출되며 사전 렌더링할 경로를 지정할 수 있다.
// 함수는 빌드 시에 호출
export async function getStaticPaths() {
// 게시물 데이터를 얻기 위해 외부 데이터 호출
const res = await fetch('https://.../posts')
const posts = await res.json()
// 게시물에 기반하여 사전 렌더링 할 paths 얻음
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// 이러한 경로들만 사전 렌더링
// { fallback: false } 은 다른 경로들은 404라는 의미
return { paths, fallback: false }
}
또한 pages/posts/[id].js에서 getStaticProps를 export해야 이 id가 있는 게시물에 대한 데이터를 가져오고 사전 렌더링할 수 있다.
export default function Post({ post }) {
// Render post...
}
export async function getStaticPaths() {
// ...
}
// 빌드 시 호출
export async function getStaticProps({ params }) {
// params post 'id'를 포함
// 경로가 /posts/1과 같은 경우 params.id는 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// props 을 통해 게시물 데이터를 페이지에 전달
return { props: { post } }
}
페이지가 한번 만들어지고 CDN에 의해 제공될 수 있으므로 가능하면 정적 생성을 사용하는 것을 권장한다. 서버가 모든 요청에 대해 페이지를 렌더링하도록 하는 것보다 훨씬 빠르다.
다양한 유형의 페이지에 정적 생성을 사용할 수 있다.
사용자의 요청에 앞서 페이지를 미리 렌덩할 수 있다면 정적 생성을 선택해야 한다.
반면에, 사용자 요청에 앞서 페이지를 렌더링할 수 없다면 정적 생성은 좋은 생각이 아니다. 페이지에 자주 업데이트 되는 데이터가 표시되고 요청이 있을 때마다 페이지 콘텐츠가 변경될 수 있다.
이런 경우에는 다음 중 하나를 수행할 수 있다.
클라이언트 측 데이터 fetching과 함께 정적 생성을 사용: 페이지의 일부 부분을 사전 렌더링 하지 않고 클라이언 측 JavaScript를 사용하여 채울 수 있다.
서버 측 렌더링(SSR) 사용: Nextjs는 각 요청마다 페이지를 사전 렌더링한다. CDN에서 페이지를 캐시할 수 없기 때문에 속도가 느려지지만 페이지는 항상 최신 상태로 유지된다.
요약