Pre-rendering and Data Fetching
Pre-rendering
- Next.js는 브라우저가 로드 되면 모든 페이지를 pre-render한다.
- 생성된 HTML은 최소한의 필요한 자바스크립트 코드가 포함되어 있어 화면을 빠르게 띄운다.
- 그 후에 자바스크립트가 작동하면서 fully interactive하게 한다.
- 이를 hydration이라고 한다.
- Next를 사용하지 않은 React 앱이라면 pre-render를 지원하지 않음
- 각 페이지마다 Static Generation 또는 Server-side Rendering를 선택 가능
Static Generation
- build time에 HTML를 생성하고 각각의 request마다 재사용 되는 방식
- 미리 페이지를 생성해 두기 때문에 사용자마다 페이지를 generate할 필요가 없어진다.
- 상품 페이지, 블로그 게시물, 도움말 등의 정적인 페이지에 사용하면 좋다.
getStaticProps
- async 함수로 외부 파일 시스템에서 데이터를 가져옴
- pre-render시에 데이터도 함께 render하게 함
- 이는 build time시 가장 먼저 실행됨
- 작동 시기
- In development (npm run dev or yarn dev), getStaticProps runs on every request.
- In production, getStaticProps runs at build time.
- only be exported from a page. You can’t export it from non-page files.
gray-matter
- 파일의 메타데이터(ex. title, date 등)를 YAML Front Matter라고 부른다.
- 이를 parsing 하기 위한 라이브러리가 gray-matter
path
- nextjs-blog
- lib
- pages
- posts
- pre-rendering.md
- ssg-ssr.md
posts.js
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
★ process.cwd(): node명령을 호출한 작업디렉터리의 절대경로
★ 파라미터로 받은 경로들을 하나로 합쳐서 문자열 형태로 path를 리턴
const postsDirectory = path.join(process.cwd(), 'posts')
export function getSortedPostsData() {
★ /posts 폴더 아래의 파일 이름을 읽어들임
const fileNames = fs.readdirSync(postsDirectory)
★ 파일들의 데이터를 읽어들여 특정 데이터를 배열로 저장함(map 함수 참고)
const allPostsData = fileNames.map(fileName => {
★ 확장자 .md를 없앤 이름을 id로 함
const id = fileName.replace(/\.md$/, '')
★ 파일이름을 포함한 전체경로를 변수에 저장
const fullPath = path.join(postsDirectory, fileName)
★ 파일의 데이터를 읽어 들여 저장
const fileContents = fs.readFileSync(fullPath, 'utf8')
★ gray-matter를 사용해서 metadata section 부분을 파싱
const matterResult = matter(fileContents)
★ id 값과 gray-matter를 사용해 얻어낸 메타데이터를 합쳐서 반환
★ id, title, date
return {
id,
...matterResult.data
}
})
★ 위에서 얻은 데이터 배열을 최신 순으로 정렬하여 리턴
return allPostsData.sort((a, b) => {
if (a.date < b.date) {
return 1
} else {
return -1
}
})
}
index.js
import Head from 'next/head'
import Layout, { siteTitle } from '../components/layout'
import utilStyles from '../styles/utils.module.css'
★ posts.js로부터 getSortedPostsData() 함수를 불러들임
import { getSortedPostsData } from '../lib/posts'
export async function getStaticProps() {
★ getSortedPostsData() 함수를 통해 {id, title, date} 데이터를 리턴받음
const allPostsData = getSortedPostsData()
return {
props: {
allPostsData
}
}
}
export default function Home({ allPostsData }) {
return (
<Layout home>
<Head>
<title>{siteTitle}</title>
</Head>
<section className={utilStyles.headingMd}>
<p>[Your Self Introduction]</p>
<p>
(This is a sample website - you’ll be building a site like this on{' '}
<a href="https://nextjs.org/learn">our Next.js tutorial</a>.)
</p>
</section>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<h2 className={utilStyles.headingLg}>Blog</h2>
<ul className={utilStyles.list}>
★ allPostsData의 id, date, title 값을 뽑아 리스트 출력
★ utilStyles는 CSS
{allPostsData.map(({ id, date, title }) => (
<li className={utilStyles.listItem} key={id}>
{title}
<br />
{id}
<br />
{date}
</li>
))}
</ul>
</section>
</Layout>
)
}
Fetch External API or Query Database
- 외부 API나 DB를 통한 데이터 fetch 가능
- 이게 가능한 이유는 getStaticProps는 오직 server-side에서만 작동하기 때문
- broweser를 위한 JS bundle에도 들어가지 않는다.
- 이는 브라우저를 통할 것 없이 직접 데이터베이스에 쿼리를 작성해서 전달할 수 있다는 뜻
코드 설명
export async function getSortedPostsData() {
★ 파일 시스템 대신에 외부 API
★ 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() {
★ 파일 시스템 대신에 DB
// fetch post data from a database
return databaseClient.query('SELECT posts...')
}
Server-side Rendering
- 각각의 request마다 generate되는 방식
- getServerSideProps 사용( <-> getStaticProps)
getServerSideProps
- getServerSideProps는 request time마다 실행됨
- 파라미터 context는 request의 상세인자를 포함하고 있음
- request time에 데이터가 fetch되어야 하는 경우에만 사용해야 함
export async function getServerSideProps(context) {
return {
props: {
// props for your component
}
}
}
Client-side-Rendering
- 데이터를 pre-render할 필요가 없을 경우 사용
순서
- 외부 데이터를 필요로 하지 않는 페이지의 부분들을 먼저 정적으로 generate함
- 페이지가 로드되면 클라이언트가 자바스크립트를 사용해 외부데이터를 fetch, 데이터를 덧붙힘
사용처
- 유저페이지 같이 private한 정보를 담는 페이지에서 사용
- 빈번히 데이터가 업데이트 되어 request할 때마다 가장 최신 데이터를 필요로 할 경우 사용
SWR
- Client-side-Rendering 할 때 사용
- caching, revalidation, focus tracking, 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>
}