Next JS에서 파라미터가 동적으로 바뀌는 페이지(ex - users/:userId
)를 빌드타임에 생성하려면, 해당 페이지 컴포넌트에서 getStaticPaths
+ getStaticProps
함수를 export 해줘야한다.
getStaticPaths
반환 객체이 때, 어떤 path들에 대해서 정적 페이지 생성을 할지 정하는 getStaticPaths
함수는 반환 객체로 paths
키와 fallback
키를 반드시 포함시켜야한다.
pages/users/[id].js
에서 getStaticPaths
함수가 아래와 같은 객체를 반환하면,
// getStaticPaths
return {
paths: [
{ params: { id: '1' } },
{ params: { id: '2' } }
],
fallback: ...
}
Next JS는 users/1
, users/2
페이지를 빌드타임에 생성하게 된다.
paths
어떤 path의 페이지들을 빌드 타임에 생성할지 정하는 배열이다.
paths
배열에서 각각의 params
값은 페이지 이름에 있는 파라미터와 일치해야한다.
여기서 주의할 점들은 다음과 같다.
pages/users/[userId].js
였다면, params
객체도 userId
키값을 가지고 있어야한다.pages/[...slug]
와 같이 catch-all 라우트를 이용중인 경우, params
객체는 slug
배열을 가지고 있어야한다.pages/[[..slug]]
와 같이 optional catch-all 라우트를 이용중인 경우, null
/ []
/ undefined
/ false
값을 넣어주면 루트 라우트를 렌더링하게 된다.fallback
빌드 타임에 생성해놓지 않은 path로 요청이 들어온 경우 어떻게 할지 정하는 boolean
또는 'blocking'
값이다.
false
인 경우
getStaticPaths
가 반환하지 않은 모든 path에 대해서 404 페이지를 반환한다.
아래와 같은 경우에 사용할 수 있다
true
인 경우
getStaticProps
의 동작이 바뀌게 된다.
getStaticPaths
가 반환한 path들은 빌드 타임에 HTML로 렌더링된다
이외의 path들에 대한 요청이 들어온 경우, 404 페이지를 반환하지 않고, 페이지의 "fallback" 버전을 먼저 보여준다
백그라운드에서 Next js가 요청된 path에 대해서 getStaticProps
함수를 이용하여 HTML 파일과 JSON 파일을 만들어낸다
백그라운드 작업이 끝나면, 요청된 path에 해당하는 JSON 파일을 받아서 새롭게 페이지를 렌더링한다. 사용자 입장에서는 [ fallback → 풀 페이지 ]와 같은 순서로 화면이 변하게된다.
새롭게 생성된 페이지를 기존의 빌드시 프리렌더링 된 페이지 리스트에 추가한다. 같은 path로 온 이후 요청들에 대해서는 이때 생성한 페이지를 반환하게된다.
"fallback" 상태일 때 보여줄 화면은 next/router
의 router.isFallback
값 체크를 통해서 조건 분기하면 된다. 이때 페이지 컴포넌트는 props
로 빈값을 받게된다.
// 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...
}
아래와 같은 경우에 사용할 수 있다
true
로 설정해주면 이후 요청이 오는 것에 따라서 정적 페이지들을 추가하게 된다'blocking'
일 경우
true
일 경우와 비슷하게 동작하지만, 최초 만들어놓지않은 path에 대한 요청이 들어온 경우 fallback 상태를 보여주지 않고 SSR처럼 동작한다. 이후 true
옵션과 같이 기존의 정적 페이지 리스트에 새로 생성한 페이지를 추가한다.
fallback: false
import axios from 'axios'
import _ from 'lodash';
export default function UserDetailPage({ user }) {
return (
<div>
{user.id} / {user.name} / {user.email}
</div>
)
}
export async function getStaticPaths() {
const { data: users } = await axios.get('https://jsonplaceholder.typicode.com/users');
const paths = _.map(_.slice(users, 0, 5), (user) => {
return { params: {id: _.toString(user.id)}};
});
return {
paths,
fallback: false
}
}
export async function getStaticProps(context) {
const { id } = context.params;
const { data: user } = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
return {
props: {
user
}
}
}
fallback
인 false
인 경우에는, 사전에 빌드하지 않은 path에 대해서 404 응답을 반환한다.
위 예시에서는 1~5번 까지의 id
를 가지는 path들에 대해서만 앱 빌드시 페이지 생성을 하고 있다. 따라서, 다른 id의 요청이 들어올 경우, 404 응답을 반환한다.
fallback: true
import axios from 'axios'
import { useRouter } from 'next/router';
import _ from 'lodash';
export default function UserDetailPage({ user }) {
const router = useRouter();
// fallback version
if (router.isFallback) {
return (
<div>
Loading...
</div>
)
}
return (
<div>
{user.id} / {user.name} / {user.email}
</div>
)
}
export async function getStaticPaths() {
const { data: users } = await axios.get('https://jsonplaceholder.typicode.com/users');
const paths = _.map(_.slice(users, 0, 5), (user) => {
return { params: {id: _.toString(user.id)}};
});
return {
paths,
fallback: true
}
}
export async function getStaticProps(context) {
const { id } = context.params;
const [{ data: user }] = await Promise.all([
axios.get(`https://jsonplaceholder.typicode.com/users/${id}`),
timeout(5000)
]);
return {
props: {
user
}
}
}
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
fallback
이 true
인 경우에는 사전에 빌드하지 않은 path에 대해서 요청이 들어올시 첫 요청 시기에 getStaticProps
호출을 통해 페이지를 만들고, 빌드된 path 리스트에 추가한다. 페이지가 아직 만들어지지 않았을 때, 페이지의 fallback 버전을 렌더링한다.
위 코드 예시에서는 의도적으로 getStaticProps
의 실행을 늦추기 위해 timeout
함수를 선언해서 사용했다. getStaticProps
의 실행에는 최소 5초가 걸리게된다.
즉, 새로운 path로의 첫 요청을 하는 사용자는 최소 5초동안 fallback 페이지를 보게된다.
빌드 후에 파일을 확인해보면, [id].html
이 추가로 생성되는데, 이 파일이 /users/[id]
라우트로의 fallback 버전이 된다. 실제로 파일을 열어보면 아래와 같이 페이지 컴포넌트에서 정의한 fallback 버전을 볼 수 있다. 참고로, fallback 버전이 지정되지 않으면 빌드과정에서 오류가 발생한다.
그리고 사전에 빌드되지 않은 path에 대한 요청이 들어가게 되면,
위와 같이 페이지 생성이 완료되기 전까지 fallback 버전을 보여주다가, 생성이 완료되면 생성된 페이지를 저장하고, 보여주게 된다.
이렇게 새로운 path로의 첫 요청은 오래 걸리지만, 이때 페이지를 생성하고 저장해놓기 때문에, 이후 요청들에 대해서는 다른 빌드시 생성된 페이지들과 마찬가지로 사용자가 매우 빠르게 페이지를 볼 수 있게 된다.
fallback: 'blocking'
import axios from 'axios'
import _ from 'lodash';
export default function UserDetailPage({ user }) {
return (
<div>
{user.id} / {user.name} / {user.email}
</div>
)
}
export async function getStaticPaths() {
const { data: users } = await axios.get('https://jsonplaceholder.typicode.com/users');
const paths = _.map(_.slice(users, 0, 5), (user) => {
return { params: {id: _.toString(user.id)}};
});
return {
paths,
fallback: 'blocking'
}
}
export async function getStaticProps(context) {
const { id } = context.params;
const [{ data: user }] = await Promise.all([
axios.get(`https://jsonplaceholder.typicode.com/users/${id}`),
timeout(5000)
]);
return {
props: {
user
}
}
}
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
fallback
이 'blocking'
인 경우 true
일때와 비슷하지만, 페이지 생성중에 사용자에게 fallback 버전의 페이지를 보여주지 않고, ssr처럼 동작해서 아무것도 미리 보여주지 않게된다.
위 코드 예시에서 볼 수 있듯이 fallback 상태에 따라서 뭘 보여줄지 정의하지 않아도 되고, 빌드된 파일을 살펴보면 fallback: true
처럼 [id].html
을 만들어놓지도 않는다.
마찬가지로 getStaticProps
에 5초의 타임아웃을 걸어놓았기 때문에, 사용자는 빌드되지 않은 path로의 요청을 하는 경우 해당 시간동안 브라우저의 로딩 상태를 보게된다.
마찬가지로, 이후 같은 path로의 요청에는 빠른 응답을 할 수 있게 된다.
와.......진짜 도움 많이 됐습니다
진심으로 감사합니다