Next.js 시작해보자 3/5 - Pre-rendering and Data Fetching
external blog data를 어떻게 불러(fetch)오는지에 대해서 알아본다. 블로그 컨텐츠는 파일 시스템에 저장되지만 컨텐츠가 다른곳에 저장되는 경우 (e.g. databases or Headless CMS)
해당 포스트에서 다룰 내용들
- Next.js
pre-renderingfeature - Static Generation, Server-side Rendering
- Static Generation with data, and without data
getStaticProps와 어떻게 external blog data를 index page에 추가하는지에 대해서getStaticProps의 useful information
Pre-rendering
https://nextjs.org/learn/basics/data-fetching/pre-rendering
pre-rendering을 알기전에 data fetching에 대해서 알아야 한다. 기본값으로 Next.js에서는 모든 페이지에서 pre-renders를 한다. 이 의미는 Next.js는 client-side JavaScript로 모든 작업을 수행하는 대신에 미리 각 페이지에 대해서 HTML을 만들어 놓는다. pre rendering은 더 낲은 성능과 SEO의 결과를 얻을 수 있음.
각 generatged HTML은 minimal JavaScript code에 의해 생성된다. 브라우저에 의해서 페이지가 로드될때 페이지의 JavaScript 코드가 실행되고 페이지를 완전히 interative로 만든다. (이 과정을 hydration (수화?) 라고 말한다.)
Check That Pre-rendering Is Happening
pre-rendering이 발생하는지 보려면 브라우저에서 JavaScript를 disable 해보면 안다.
Two Forms of Pre-rendering
https://nextjs.org/learn/basics/data-fetching/two-forms
Next.js는 pre-rendering의 두가지 형태가 존재한다
-
Static Generation
- build time에 HTML을 생성
-
Server-side Rendering
- each request에서 HTML을 생성
두개의 차이는 언제 페이지의 HTML을 생성하느냐에있다.

Per-page Basis
Next.js에서는 각 페이지에서 어떤 pre-redering을 사용할지 선택이 가능하다. Next.js App에서는 대부분의 페이지를 Static Generation로 사용하고 다른 것들은 Server-side Rendering을 하는 hybrid로 생성이 가능하다.
When to Use Static Generation V.S. Server-side Rendering
Next.js에서는 Static Generation (with and without data)를 추천한다. 그 이유는 구성한 페이지를 한번 빌드하고 CDN에서 제공할 수 있기 때문에 가능할 때마다 서버가 모든 요청에 대해 페이지를 렌더링하는 것보다 훨씬 빠르다.
Static Generation 은 많은 타입의 페이지에서 사용이 가능하다:
- Marketing Pages
- Blog posts
- E-Commerce product listings
- Help and Documentation
사용자의 요청에 앞서 페이지를 pre-render 할 수 있는지 자문하고, 대답이 예라면 Static Generation을 선택하자.
반면에 Static Generation은 사용자의 요청에 앞서 페이지를 미리 렌더링 할 수 없는 경우에는 좋은 생각이 아니다. 페이지에 자주 업데이트가 되는 데이터가 표시되고 모든 요청에 따라 페이지 콘텐츠가 변경될 수 있다. 이 경우에는 Server-side Rendering을 사용해야 한다. 더 느리지만 pre-rendering 페이지는 항상 최신 상태이다. 또는 pre-rendering을 스킵하고 client-side JavaScript를 사용하여 자주 업데이트되는 데이터를 채울 수 있다.
Static Generation with and without Data
https://nextjs.org/learn/basics/data-fetching/with-data
Static Generation은 데이터가 있을때 없을때 사용이 가능하다. 지금까지 우리가 만든 페이지는 external data를 가져올 필요가 없었다. 이러한 페이지들은 앱이 production으로 build 될때 자동으로 정적으로 생성이 된다.
그러나 몇명의 페이지들은 일부 외부 데이터를 먼저 가져오지 않으면 HTML을 렌더링하지 못할 수 있다. 파일시스템에 접근하거나 fetch external API, query database를 빌드시에 할 수 있다. Next.js는 Static Generation with data를 지원한다.
Static Generation with Data using getStatiscProps
Next.js는 page component를 export 할때 getStaticProps를 호출해 async 또한 export를 할 수 있다.
export default function Home(props) { ... }
export async function getStaticProps() {
// Get external data from the file system, API, DB, etc.
const data = ...
// The value of the `props` key will be
// passed to the `Home` component
return {
props: ...
}
}getStaticProps은 Next.js에 “야 이 페이지에 data dependencies가 있어 따라서 빌드시 이 페이지를 pre-render 할때 먼저 해결해야 해” 라고 얘기하는것과 같음
development mode에서는
getStaticProps는 각 요청에서 실행이 된다.
Blog Data
https://nextjs.org/learn/basics/data-fetching/blog-data
file system을 이용해서 blog data를 앱에 추가를 해보자. 각 포스트는 markdown file로 이루어져 있다.
pages/posts에 디렉토리를 생성poage/posts안에pre-rendering.md와ssg-ssr.md를 생성하자.
pre-rendering.md 파일
---
title: "Two Forms of Pre-rendering"
date: "2020-01-01"
---
Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. The difference is in **when** it generates the HTML for a page.
- **Static Generation** is the pre-rendering method that generates the HTML at **build time**. The pre-rendered HTML is then _reused_ on each request.
- **Server-side Rendering** is the pre-rendering method that generates the HTML on **each request**.
Importantly, Next.js lets you **choose** which pre-rendering form to use for each page. You can create a "hybrid" Next.js app by using Static Generation for most pages and using Server-side Rendering for others.ssg-ssr.md 파일
---
title: "When to Use Static Generation v.s. Server-side Rendering"
date: "2020-01-02"
---
We recommend using **Static Generation** (with and without data) whenever possible because your page can be built once and served by CDN, which makes it much faster than having a server render the page on every request.
You can use Static Generation for many types of pages, including:
- Marketing pages
- Blog posts
- E-commerce product listings
- Help and documentation
You should ask yourself: "Can I pre-render this page **ahead** of a user's request?" If the answer is yes, then you should choose Static Generation.
On the other hand, Static Generation is **not** a good idea if you cannot pre-render a page ahead of a user's request. Maybe your page shows frequently updated data, and the page content changes on every request.
In that case, you can use **Server-Side Rendering**. It will be slower, but the pre-rendered page will always be up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate data.포스트에 작성한 title/date는 YAML Front Matter라고 말하고, gray-matter 라이브러리를 이용하여 파싱이 가능하다.
Parsing the Blog Data on getStaticProps
이제 pages/index.js에서 위에서 생성한 md파일을 사용해보자.
- filename을 post URL의
id로 사용될 예정 - index page에 포스트의 리스트를 날짜로 정렬해서 나타낼 예정
pre-render를 하기 위해서는 getStaticProps을 구현해야 한다.
Implement getStaticProps
https://nextjs.org/learn/basics/data-fetching/implement-getstaticprops
markdown의 metadata를 파싱하기 위해서 gray-matter를 설치하자
npm install gray-matterfile system으로 부터 fetching data를 하는 simple library를 생성하자
lib을 생성하고posts.js의 코드 추가
import fs from "fs"
import path from "path"
import matter from "gray-matter"
const postsDirectory = path.join(process.cwd(), "posts")
export function getSortedPostsData() {
// Get file names under /posts
const fileNames = fs.readdirSync(postsDirectory)
const allPostsData = fileNames.map((fileName) => {
// Remove ".md" from file name to get id
const id = fileName.replace(/\.md$/, "")
// Read markdown file as string
const fullPath = path.join(postsDirectory, fileName)
const fileContents = fs.readFileSync(fullPath, "utf8")
// Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents)
// Combine the data with the id
return {
id,
...matterResult.data,
}
})
// Sort posts by date
return allPostsData.sort((a, b) => {
if (a.date < b.date) {
return 1
} else {
return -1
}
})
}이제 getStoredPostsData를 pages/index.js에 import
import { getSortedPostsData } from "../lib/posts"getSTaticProps를 pages/index.js에 추가하고 getSortedPostsData를 내부에 구현
export async function getStaticProps() {
const allPostsData = getSortedPostsData()
return {
props: {
allPostsData,
},
}
}allPstsData의 값이 props의 object에 반환이 되고 Home 컴포넌트에 blog posts가 전달이 된다. {allPostsData}를 사용하도록 component definition을 수정
export default function Home ({ allPostsData }) { ... }데이터를 화면에 출력하기 위해서 <section> tag를 컴포넌트 아래 추가한다.
export default function Home({ allPostsData }) {
return (
<Layout home>
<Head>…</Head>
<section className={utilStyles.headingMd}>…</section>
<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>
)
}getStaticProps Details
https://nextjs.org/learn/basics/data-fetching/getstaticprops-details
getStaticProps에 대해서 알기 위해서 필수적인 정보에 대해서 소개
Fetch External API or Query Database
lib/posts.js에서 file system의 data를 가져오기 위해서 getSortedPostsData를 구현을 했다. 그러나 다른 데이터 소스에서 데이터 fetch를 해야할때가 있다. 예를 들어서 API endpoint
export async function getSortedPostsData() {
// Instead of the file system,
// fetch post data from an external API endpoint
const res = await fetch("..")
return res.json()
}database에 직접적으로 쿼리가 가능하다.
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가 server-side에서만 실행되기 때문에 가능하다. 이 함수는 절대로 client-side에서 실행되지 않습니다. 브라우저 용 JS 번들에도 포함되지 않습니다. 즉, 직접 데이터베이스 쿼리와 같은 코드를 부라우저로 보내지 않고도 작성이 가능하다.
Development vs. Production
- In development(
npm run devoryarn dev),getStaticProps는 every request에서 실행된다. - In production,
getStaticProps은 build time에만 실행된다.
빌드시 실행되기 때문에 쿼리 매개변수 또는 HTTP header와 같이 요청시에만 사용할 수 있는 데이터를 사용할 수 없습니다.
Only Allowed in a Page
getStaticProps는 페이지에서만 export 할 수 있다. non-page-files에서는 내보낼 수 없다. 이 제한의 이유는 페이지가 렌더링되기 전에 React가 필요한 모든 데이터를 가져가야하기 때문이다.
What If I Need to Fetch Data at Request Time?
사용자의 요청에 앞서 페이지를 미리 렌더링 할 수 없는 경우 Static Generation은 좋은 생각이 아니다. 페이지에 자주 업데이트되는 데이터가 표시되고 모든 요청에 따라 페이지 컨텐츠가 변경될 수 있다. 이 경우에는 Server-side Rendering 또는 skip pre-rendering을 사용해야 한다.
Fetching Data at Request Time
https://nextjs.org/learn/basics/data-fetching/request-time
만약 build time 대신 request time에서 fetch data가 필요하다면 Server-side Rendering을 사용해야 한다.
Server-side Rendering을 사용하기 위해서는 getStaticProps대신에 getServerSideProps를 export 하면 된다.
Using getServerSideProps
getServerSideProps의 starter code
export async function getServerSideProps(context) {
return {
props: {
// props for your component
},
}
}request time에서 getServerSideProps가 호출되기 때문에 파라미터(context)는 request specific parameters를 포함하고 있다. 요청시 데이터를 가져와야하는 페이지를 pre-render 해야하는 경우에만 getServerSideProps을 사용해야 한다. Time to first byte(TTFB)는 서버가 모든 요청에 대해 결과를 계산해야하고 추가 구성 없이는 결과를 CDN에 의해 캐시할 수 없기 때문에 getStaticProps보다 느리다.
Client-side Rendering
데이터를 pre-render 할 필요가 없다면 Client-side Rendering을 사용하면 된다.
- 외부 데이터가 필요하지 않은 페이지 부분을 정적으로 생성(pre-render)를 한다.
- 페이지가 로딩되면 JavaScript를 사용하여 클라이언트에서 외부 데이터를 가져오고 나머지 부분을 채운다.
이 접근 방식은 사용자 대시보드 페이지에 적합하다. 대시보드는 비공개 사용자 별 페이지이기 때문에 SEO는 관련이 없으며 페이지를 미리 렌더링 할 필요가 없다. 데이터는 자주 업데이트되므로 요청시에 데이터를 가져와야 한다.
SWR
Next.js팀은 SWR이라는 데이터 가져오기를 위한 React Hook을 만들었다. client side에서 fetching data를 한다면 추천하는 hook. SWR은 caching, revalidation, focust tracking, refetching on interval, and more를 처리한다. 여기서는 간단한 사용방법에 대해서만 다룸
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>
}