티스토리 뷰
[Next.js] SSG 선택 기준과 SSG를 하는 두 가지 방법(with data, without data)
doeunnkimm 2023. 6. 23. 21:40
Next.js를 활용하면 페이지별로 Pre-rendering 방식을 선택할 수 있다
특정 페이지 별로 이곳은 SSG, 저기는 SSR 이런 식으로 가변적으로 사용할 수 있습니다. 또 아예 Pre-render를 안 하고 싶다고 하고 CSR에서 하도록도 선택할 수 있습니다.
- getStaticProps를 사용하면 SSG
- getServerSideProps를 사용하면 SSR
SSG(Static Site Generation)를 사용하면 좋은 페이지
- Marketing pages
- Blog posts
- E-commerce product listings
- Help and documentation
위 네 가지 모두 전반적으로 정적일 수 있고 자주 바뀌지 않는 데이터들일 것입니다. 정적으로 만든다면 새로 배포하기 전까지 새로 바뀔 일이 없다면 SSG를 선택하기 좋은 페이지가 됩니다.
적용 여부 선택 기준
사용자가 페이지를 요청하기 전에 pre-render 할 수 있는가?
Yes → SSG
No → SSR 혹은 ISR 혹은 CSR
반대로, SSR은 요청할 때 pre-render를 합니다. 사용자에게 커스터마이징된 페이지를 보여줘야 한다면 로그인이 되어 있어야 하고, 이 로그인으로 사람을 구분한 후에 pre-render를 선택해야 할 수 있습니다. 그럴 때에는 SSG를 선택할 수 없습니다.
반면에, 어느 사람이든 볼 컨텐츠를 미리 정할 수 있다면 SSG가 서버의 리소르를 사용하지 않으면서 서비스를 Static하게 제공할 수 있는 솔루션으로 볼 수 있습니다.
SSG의 2가지 케이스
- 외부 데이터 없이 pre-rendering
- 외부 데이터를 가져와서 pre-rendering
여기서 말하는 외부 데이터는 다른 파일이나, API, DB 등을 말합니다. SSG의 Static Generation은 결국 서버에서 동작합니다.
다른 파일을 읽어와보자
md 파일
md 파일에는 metadata를 포함할 수 있는데요!
📜 posts/pre-render.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.
---
으로 감싸진 metadata를 읽어보려고 합니다 (YAML Front Matter)
--
어떻게 보면 format이고, 저번 글에서 Head 컴포넌트에 meta 정보들을 담았었죠 ? 마찬가지로, 해당 블로그 글에 대한 meta 정보들을 담고 있고 위와 같은 방식으로 표현했다고 보면 됩니다.
파일을 읽어올 때는 추가로 install이 필요합니다.
$ yarn add gray-matter
YAML Front Matter를 해석해줄 라이브러리를 설치해야 합니다.
📜 lib/posts.js
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
// 해당 프로젝트의 root 경로 + '/posts' <- md 파일을 저장해둔 경로
const postsDirectory = path.join(process.cwd(), 'posts')
export function getSortedPostsData() {
// 파일 이름을 읽어온다
const fileNames = fs.readdirSync(postsDirectory)
// 파일 이름에서 '.md'라는 텍스트를 지운다
// ssg-ssr.md -> ssg-ssr : 이를 id로 가져가고 있다
const allPostsData = fileNames.map((fileName) => {
// Remove ".md" from file name to get id
const id = fileName.replace(/\.md$/, '')
// postsDirectory + '/pre-rendering.md'
const fullPath = path.join(postsDirectory, fileName)
// 파일을 직접 읽기
const fileContents = fs.readFileSync(fullPath, 'utf8')
// meta 데이터 읽기
const matterResult = matter(fileContents)
// Combine the data with the id
return {
id,
...matterResult.data,
}
})
// date를 기준으로 sort
return allPostsData.sort((a, b) => {
if (a.date < b.date) {
return 1
} else {
return -1
}
})
}
위 코드는 공식 문서에 포함되어 있는데요. gray-matter를 활용해서 특정 폴더를 긁어와서 그 데이터를 가지고 title을 sorting합니다.
이제 이를 SSG, SSR, CSR로 구현해보도록 합시다!
1) SSG로 구현 : `getStaticProps` 활용
export async function getStaticProps() {
const allPostsData = getSortedPostsData()
return {
props: {
allPostsData,
},
}
}
좀 전에 선언해 두었던 getSortedPostsData 함수를 이용해서 `allPostsData`를 만들고 이를 props로 return 합니다.
export default function Home({ allPostsData }) {
return (
<Layout home>
{/* Keep the existing code here */}
{/* Add this <section> tag below the existing <section> tag */}
<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>
)
}
이를 바로 props로 받아 화면에 렌더링 하게 되면 다음과 같이 화면이 구성되게 됩니다.
2) SSR로 구현 : `getServerSideProps` 활용
export async function getServerSideProps() {
const allPostsData = getSortedPostsData()
return {
props: {
allPostsData,
},
}
}
이렇게 되면 요청을 할 때마다 렌더링이 될 것입니다. dev 환경에서는 SSG와 SSR이 동일하게 동작하지만 build 환경에서는 다르게 동작합니다.
3) CSR로 구현 :API Routes 활용
CSR로 구현하기 위해서는 API Routes를 활용해야 합니다. 즉, 데이터를 fetch 해오고, 이를 state로 저장하여 화면에 렌더링합니다.
const [allPostData, setAllPostData] = useState([])
useEffect(() => {
fetch('/api/posts')
.then((res) => res.json())
.then((data) => setAllPostData(data.allPostsData))
}, [])
우선 위와 같이 state 하나를 선언해주고, useEffect로 데이터를 fetch해와서 state를 업데이트하는 로직을 작성해봅시다.
아직 /api/posts라는 api가 없기 때문에 Next.js의 API Routes를 활용해줍니다.
📜 pages/api/posts.js
import { getSortedPostsData } from '../../lib/posts'
export default function handler(req, res) {
const allPostsData = getSortedPostsData()
res.status(200).json({ allPostsData })
}
번외) SSG를 사용하는데, 직접 fetch를 해서 return을 한다면 (without data)
export async function getStaticProps() {
const response = await fetch('/api/posts')
const json = await response.json()
return {
props: {
allPostsData: json.allPostsData,
},
}
}
위 처럼 코드를 작성하고 실행했더니 다음과 같은 에러가 발생했습니다.
fetch할 api 주소를 상대경로로 작성했기 때문인데요. 절대 경로로 수정해 봅시다.
export async function getStaticProps() {
const response = await fetch('http://localhost:3000/api/posts')
const json = await response.json()
return {
props: {
allPostsData: json.allPostsData,
},
}
}
절대 경로로 api 주소를 변경해주었더니 잘 실행되는 것을 확인할 수 있었습니다.
⭐ SSG에서 직접 fetch를 하고 싶다면 절대 경로를 사용해야 한다. ⭐
하지만 Server Side에서는 API Routes를 사용하지 않아야 합니다. API Routes를 Client Side에서 Server Side로 요청할 때 사용하는 것이기 때문인데요. getStaticProps / getStaticPaths 등은 Client Side 코드에 포함되지 않습니다.
그렇기에 서버 사이드에서는 DB에 직접 접근하는 등 훨씬 자유도 높은 작업을 할 수 있습니다.
Data를 가져오는 함수 getSortedPostsData의 확장
우리가 앞서 작성했었던 getSortedPostsData 함수를 확장하면 다음과 같이도 사용될 수 있습니다.
- 다른 File 조회
- 외부 api 요청
- DB 조회
이번 글에서는 Pre-rendering과 Data Fetching 연습을 해보았습니다.
1. Pre-rendering
SSG 선택 기준
2. SSG 2가지 케이스
without data / with data
3. YAML Front Matter
Matadata 표기 방식 / gray-matter로 파싱
4. API Routes
fs는 server side에서만 가능
'프론트엔드 > Next.js' 카테고리의 다른 글
[Next.js] /post/write 페이지에서 새로운 글을 쓸 수 있도록 해보세요 (0) | 2023.06.26 |
---|---|
[Next.js] SSG를 활용해서 다이나믹한 page 생성 with getStaticPaths, fallback 에러 해결 (0) | 2023.06.25 |
[Next.js] Layout과 Styling - Image 컴포넌트, Head 컴포넌트, Global CSS (0) | 2023.06.23 |
[Next.js] Next.js가 제공하는 여러 기능들 - Client-side Navigate 부터 Prefetching까지 (0) | 2023.05.29 |
[Next.js] Next.js의 API Routes (0) | 2023.05.28 |
- Total
- Today
- Yesterday
- next.js
- styled-components
- 프로젝트 회고
- 데이터분석
- 디프만
- react-query
- 스타일 컴포넌트 styled-components
- 머신러닝
- react
- 리액트
- JSP
- 자바
- 프론트엔드
- 파이썬
- rtl
- testing
- 자바스크립트 기초
- 자바스크립트
- TypeScript
- CSS
- 타입스크립트
- Python
- 리액트 훅
- HTML
- 프론트엔드 기초
- jest
- frontend
- 딥러닝
- 인프런
- 프론트엔드 공부
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |