Next.js는 개발자 도구에서 javascript 로드를 꺼도 페이지가 열린다. 프리 렌더링 작업을 통해서 페이지가 이미 있기 때문이다. css만 불러와지지 않고 페이지 내용은 그대로 나온다.
dev모드로 실행 중일때는 모든 페이지의 각 요청에서 HTML을 생성한다. (Static Generation을 사용하고 있어도 말이다.)
Static Generation을 추천한다. 매번 요청에 대해 서버가 HTML을 만드는 것보다 한번 만들고 CDN에 의해 제공하는 것이 훨씬 빠르기 때문이다.
외부 데이터를 가져오지 않는 페이지들은 production용으로 빌드될 때 자동으로 정적으로 생성된다.
하지만, DB같은 곳에서 데이터를 불러와서 HTML페이지를 만들어줘야 할 수도 있다. next.js의 static generation으로도 가능하다. 이럴 때 getStaticProps()
를 사용한다. 이 함수는 production 모드에서 빌드 때 실행된다. 함수 안에서 props로 받아온 데이터를 넘겨줄 수 있다.
export async function getStaticProps() {
const data = ...
return {
props: ...
}
}
getStaticProps를 사용하면 Next.js에게 렌더링하기 전에 데이터 종속성이 있다고 알려줄 수 있다. 개발모드에서는 각 요청에 대해 getStaticProps가 실행된다.
src/pages/posts/pre-rendering.md, ssg-ssr.md 파일을 만들고
src/lib/posts.js파일에서 해당 마크다운 파일들을 파싱해서 title과 date를 배열에 저장하는 작성한다.
src/lib/posts.js
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
const postsDirectory = path.join(process.cwd(), 'src/pages/posts');
export function getSortedPostsData() {
const fileNames = fs.readdirSync(postsDirectory);
const allPostsData = fileNames.map(fileName => {
const id = fileName.replace(/\.md$/, '');
const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, 'utf-8');
const matterResult = matter(fileContents);
return {
id,
...matterResult.data,
};
});
return allPostsData.sort(({ date: a }, { date: b }) => {
if (a < b) {
console.log(a, b);
return 1;
} else if (a > b) {
console.log(a, b);
return -1;
} else {
console.log(a, b);
return 0;
}
});
}
아래 형태로 getSortedPostsData()는 반환한다.
[
{ id: 'ssg-ssr',
title: 'When to Use Static Generation v.s. Server-side Rendering',
date: '2020-01-02'
},
{ id: 'pre-rendering',
title: 'Two Forms of Pre-rendering',
date: '2020-01-01'
}
]
다른 페이지에서 해당 데이터를 이용한다고 한다면 getStaticProps를 이용해서 아래와 같이 작성한다.
src/pages/index.js
import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout';
import utilStyles from '../styles/utils.module.css';
import { getSortedPostsData } from '../lib/posts';
export default function Home({ allPostsData }) { // props를 불러온다.
console.log(allPostsData);
return (
<Layout home>
<Head>
<title>{siteTitle}</title>
</Head>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<h2 className={utilStyles.headingLg}>Blog</h2>
<ul className={utilStyles.list}>
{allPostsData.map(({ id, date, title }) => (
<li className={utilStyles.listItem} key={id}>
{title}
<br />
{id}
<br />
{date}
</li>
))}
</ul>
</section>
</Layout>
);
}
export async function getStaticProps() {
const allPostsData = getSortedPostsData();
return {
props: {
allPostsData, // 👏 props는 객체형태로 받으며 내부에 전달할 props를 넣어준다.
},
};
}
api를 조회할 수도 있고 query를 사용할 수도 있다.
이런 것이 가능한 이유는 getStaticProps
가 오직 server-side에서 동작하기 때문이다. 절대 클라이언트 측에서 실행되지 않는다. 브라우저에 JS번들에서도 확인할 수 없다. 즉, 이런 쿼리문을 브라우저에 보내지 않고 조회할 수 있게 된다.
export async function getSortedPostsData() {
// Instead of the file system,
// fetch post data from an external API endpoint
const res = await fetch('..');
return res.json();
}
import someDatabaseSDK from 'someDatabaseSDK'
const databaseClient = someDatabaseSDK.createClient(...)
export async function getSortedPostsData() {
// Instead of the file system,
// fetch post data from a database
return databaseClient.query('SELECT posts...')
}
getStaticProps 더 알아보기
- 개발 모드에서는 getStaticProps가 매 요청마다 동작한다.
- 배포 모드에서는 getStaticProps가 빌드할 때 동작한다. getStaticPaths로부터 반환된 fallback key를 사용하면 동작을 향상시킬 수 있다.
- 빌드할 때 getStaticProps가 동작하기 때문에 쿼리 매개변수나 http헤더 같이 요청 시에 사용할 수 있는 데이터는 사용할 수 없다.
- page내에서만 export할 수 있다. 이유는 렌더링 되기 전 react요소가 있어야 되기 때문이다.
export async function getServerSideProps(context) {
return {
props: {
// props for your component
},
};
}
데이터를 미리 렌더링 할 필요가 없는 경우 ClientSide Rendering으로 JS를 이용해 데이터를 불러올 수 있다. 빌드되서 페이지가 보이고 JS를 이용해서 필요한 부분을 채운다. 예를 들어 사용자 대시보드 같이 미리 렌더링을 할 필요가 없고 매번 데이터가 업데이트되서 바뀌고 SEO도 필요없을 때 베스트다!
이럴 때 Next.js에서 만든 SWR을 이용할 수 있다. 캐싱, revalidation, refetching on interval 등에 도움을 준다. 아래는 간단한 사용 예시다.
import useSWR from 'swr';
function Profile() {
const { data, error } = useSWR('/api/user', fetch);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return <div>hello {data.name}!</div>;
}
getStaticProps() vs getServerSideProps()