Next.js 기본 사용법(6) - Data Fetching | getStaticPaths
이번 포스트에서는 getStaticPaths에 대해 알아본다.
getStaticPaths는 페이지가 getStaticProps를 사용하고 동적 경로(Dynamic Routing)를 사용한다면 빌드 시에 path들이 필요하다. 동적 라우팅을 구현하기 위해 필요한 것이 getStaticPaths 라는 비동기함수로, 사전 렌더링될 때, getStaticPaths에서 반환하는 객체의 paths 안에 있는 경로를 가지고 렌더링에 사용한다.
1. 개념
위에서 언급했듯이, 페이지가 동적 경로를 사용하여(Dynamic Routing) 정적으로 사전 렌더링(getStaticProps)을 하고 있다면 반드시 getStaticPaths를 사용해야 한다.
getStaticPaths는 getServerSideProps와 함께 사용할 수 없다.
getStaticPaths는 서버 측에서 빌드 시간에만 동작한다.
getStaticPaths는 페이지에서만 export 될 수 있다. 페이지가 아닌 파일(ex. 컴포넌트)에서는 export 할 수 없다.
개발 모드(next run dev)에서는 매 요청시마다 호출된다.
export async function getStaticPaths() {
return {
paths: [
{ params: { ... } } // See the "paths" section below
],
fallback: true, false, or 'blocking' // See the "fallback" section below
};
}
2. paths 키(필수)
paths 키는 사전 렌더링되는 경로들을 정의한 것이다.
만약, pages/posts/[id].js 라는 경로를 가진 페이지가 있다고 생각해본다.
이럴 경우, 해당 페이지에서 getStaticPaths를 export하고 paths를 아래와 같이 리턴한다.
return {
paths: [
{ params: { id: '1' } },
{ params: { id: '2' } }
],
fallback: ...
}
이 때, 각각의 값 'params'는 페이지 이름에 사용된 매개변수와 일치해야 한다.
- 만약 pages/posts/[postId]/[commentId] 가 페이지 이름이라면, params에 postId와 commentId가 꼭 포함되어야 한다.
- 페이지의 이름이 pages/[...slug]와 같이 포괄 경로를 포함한다면, params는 slug라는 배열을 포함해야 한다. (ex. ['foo', 'bar'] -> pages/foo/bar
- 만약 페이지가 선택적으로 포괄 경로를 포함한다면 null, [], undefined, false를 제공하여 루트 경로를 렌더링 한다. (ex. slug: false -> pages/)
3. fallback 키(필수)
getStaticPahts로부터 리턴되는 객체는 fallback 키에 대한 boolean 값을 반드시 포함해야 한다.
fallback: false
- getStaticPaths로부터 리턴받지 않은 경로는 404 페이지가 된다.
- 사전 렌더링을 하는 경로의 수가 적은 경우, 즉, 새로운 페이지가 자주 추가되지 않는 경우 유용하다.
- 만약, 새로운 데이터가 추가되고 새로운 페이지를 렌더링해야 된다면 다시 빌드를 해야 한다.
// pages/posts/[id].js
function Post({ post }) {
// Render post...
}
// This function gets called at build time
export async function getStaticPaths() {
// Call an external API endpoint to get posts
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: false } means other routes should 404.
return { paths, fallback: false }
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return { props: { post } }
}
export default Post
위의 코드는 공식문서 예제로, CMS로부터 블로그 포스트 리스트를 가져오고, getStaticPaths에 의해 리턴된다.
그러면 각 페이지마다 getStaticProps를 사용하여 CMS로부터 포스트 데이터를 가져온다.
fallback: true
- getStaticPaths에서 리턴된 경로는 getStaticProps에 의해 빌드 시간에 HTML로 렌더링된다.
- 빌드 시간에 생성되지 않은 경로는 404 페이지 대신, 해당 경로에 대한 첫 번째 요청에서 대체 버전(fallback page)을 보여준다.
- 백그라운드에서는 Next.js가 요청받은 경로에 대한 HTML과 JSON을 정적으로 생성한다.(getStaticProps 포함)
- 이 과정이 끝나면, 브라우저는 생성된 경로에 대해서 JSON을 제공받아 페이지를 자동으로 렌더링한다.(대체 페이지가 온전한 페이지로 전환)
- 사전 렌더링 페이지들의 목록에 이 경로가 추가되고, 동일 경로에 대한 후속 요청이 발생할 경우, 빌드 시간에 사전 렌더링된 것처럼 생성된 페이지를 제공한다.
// pages/posts/[id].js
import { useRouter } from 'next/router'
function Post({ post }) {
const router = useRouter()
// If the page is not yet generated, this will be displayed
// initially until getStaticProps() finishes running
if (router.isFallback) {
return <div>Loading...</div>
}
// Render post...
}
// This function gets called at build time
export async function getStaticPaths() {
return {
// Only `/posts/1` and `/posts/2` are generated at build time
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
// Enable statically generating additional pages
// For example: `/posts/3`
fallback: true,
}
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return {
props: { post },
// Re-generate the post at most once per second
// if a request comes in
revalidate: 1,
}
}
export default Post
어플리케이션이 데이터에 의존해 있는 매우 많은 정적인 페이지들이 있는 경우에 유용하다.(ex. 쇼핑몰)
쇼핑몰을 생각해보면, 모든 상품 페이지들이 사전렌더링되야 한다면 빌드 시간이 엄청나게 오래 걸릴 것이다.
따라서, fallback: true를 통해, 적은 부분의 페이지들을 정적으로 생성하고, 사용자가 아직 생성되지 않은 페이지를 요청하면, 그 때 해당 페이지를 로딩 표시와 함께 볼 수 있다.(위의 예제 코드 참고)
다만, fallback: true는 생성된 페이지를 업데이트하지 않는다.
업데이트하려면 ISR을 fallback: true와 같이 써야 한다.
fallback: blocking
getStaticPaths로부터 반환받지 못한 새로운 경로는 SSR(서버 사이드 렌더링)과 동일하게 HTML이 생성될 때까지 기다린다.
- 빌드 시간에 생성되지 않은 경로들은 404 페이지 대신, 서버 사이드 렌더링을 진행하고, 생성된 HTML을 반환한다.
- 사용자 입장에서는 페이지가 모두 로드된 것으로 보여, 로딩 시의 깜빡임이나 대체 화면 상태로 보이는 일은 없다.
- 마찬가지로, 이 경로를 사전 렌더링 페이지 목록에 추가되어, 동일 경로에 대한 후속 요청이 발생할 경우, 빌드 시간에 사전 렌더링된 것처럼 생성된 페이지를 제공한다.
4. fallback pages
페이지의 props가 비어있다.
router를 사용하여 fallback이 렌더링되는지를 감지할 수 있다. (router.isFallback은 true)
5. TypeScript 사용 시
import { GetStaticPaths } from 'next'
export const getStaticPaths: GetStaticPaths = async () => {
// ...
}
이상으로, Data Fetching에서 정적 생성, 그 중에서도 동적 경로가 함께할 경우에 대해 알아보았다.
다음 포스트에서는 SSR(서버 사이드 렌더링)인 getServerSideProps를 알아본다.