프리렌더링 : 웹 브라우저가 페이지를 로딩하기 이전에 렌더링하는 것
- 정적 생성(Static Generation) 과
- 서버사이드 렌더링(Server-side Rendering)으로 나뉜다.
Next.js에서는 기본적으로 모든 페이지를 정적 생성한다.
① 초기 로딩이 빨라진다
HTML이 렌더링 된 상태로 제공되므로 자바스크립트를 로딩해서 리액트가 완전히 실행될 때까지 기다리지 않아도 화면을 볼 수 있다.
페이지의 html 코드를 볼 수 있는 단축키
- 맥 : option + command + U
- 윈도우 : Ctrl + U
자바스크립트 사용중지 설정
- F12 → Settings → Preference → Debugger
자바스크립트 사용 중지 후 화면에 접속했을 때- CSR 페이지 : 텅 빈 흰 화면
- 프리렌더링 된 페이지 : 뼈대가 되는 HTML이 그려진 화면(동적 작용 CSS 등은 X) 이 나타난다
CSR 페이지 : 텅 빈 HTML 파일 불러옴 → 자바스크립트를 불러와서 렌더링하며 화면을 그려줌
▲ 리액트로 만든 CSR 페이지의 html 파일 <body>
에 <div id="root">
태그만 있다
- 자바스크립트를 불러오기 전까지 빈 흰색 화면만 보인다.
프리렌더링 된 페이지 : 렌더링 된 HTML 파일을 불러 옴 → 자바스크립트 불러와서 연결 시킴 (Hydration)
Hydration: 렌더링 된 HTML 과 리액트의 데이터를 연결하는 것
▲ Next.js로 만든 프리렌더링 된 페이지의 html 파일 <body>
에 모든 내용이 들어있다
② 검색 엔진 최적화가 된다
: 프로젝트를 빌드
하는 시점에 미리 HTML을 렌더링하는 것
기본적으로 Next.js에서는 모든 페이지를 정적 생성한다.
// /pages/setting.js
import { useTheme } from '@/lib/ThemeContext'
import Dropdown from '@/components/Dropdown'
import styles from '@/styles/Setting.module.css'
export default function Setting() {
const { theme, setTheme } = useTheme()
return (
<div>
<h1 className={styles.title}>설정</h1>
<section className={styles.section}>
<h2 className={styles.sectionTitle}>테마 설정</h2>
<Dropdown
className={styles.input}
name='theme'
value={theme}
onChange={(name, value) => setTheme(value)}
options={[
{ label: '라이트', value: 'light' },
{ label: '다크', value: 'dark' },
]}
/>
</section>
</div>
)
}
따로 받아오는 데이터가 없는 setting 페이지는 정적 생성된 html 파일이 넘어오게 된다.
정적 생성할 때 필요한 데이터를 api로 받아와야 하는 경우 getStaticProps()
함수 안에서 요청하고 데이터를 받아온다. 받은 데이터를 객체의 props 프로퍼티로 리턴하면, 컴포넌트의 props 로 받아서 사용할 수 있다.
import ProductList from '@/components/ProductList'
import SearchForm from '@/components/SearchForm'
import { getDatas } from '@/lib/apis'
import styles from '@/styles/Home.module.css'
// getStaticProps() 안에서 axios 요청
export const getStaticProps = async () => {
const res = await axios.get('/products')
const products = res.data.results ?? []
return { props: { products } } // props 객체로 products 를 전달
}
export default function Home({ products }) {
return (
<>
<SearchForm />
<ProductList className={styles.products} products={products} />
</>
)
}
다이나믹 라우팅 (ex: /pages/products/:id
)을 하는 페이지를 정적 생성을 할 때에는 어떤 페이지를 정적 생성할지 지정해줘야 한다. getStaticPaths()
함수를 구현하고 export해서 지정한다.
getStaticPaths() 함수에서는 리턴 값으로 객체를 리턴하는데, paths
라는 배열에서 각 페이지에 해당하는 정보를 넘겨줄 수 있다.
예를 들어서 id 값이 '1'인 페이지를 정적 생성하려면 { params: { id: '1' } }과 같이 쓸 수 있다.
이 때 숫자1
가 아닌 문자열 '1'
임을 주의해야 한다
그리고 fallback
이라는 속성을 사용해서 정적 생성되지 않은 페이지를 처리해 줄 것인지 지정할 수 있다.
fallback: true
이면 생성되지 않은 페이지로 접속했을 때 getStaticProps()
함수를 실행해 페이지를 만들어서 보여준다.export const getStaticPaths = async () => {
return {
paths: [ // 1 페이지와 2 페이지를 정적 생성한다
{ params: { id: '1' }},
{ params: { id: '2' }},
],
fallback: true, // 생성되지 않은 페이지로 접속하면 getStaticProps()를 실행해서 페이지를 만든다
};
}
getStaticProps() 함수
의 context 파라미터
를 사용해 필요한 params (context.params) 값
이나 쿼리스트링(context.query) 값
을 참조할 수 있다.
export const getStaticProps = async (context) => {
const { id } = context.params; // useRouter() 대신 사용
// 쿼리 스트링(/search?q=맨투맨)인 경우
// const { q } = context.query; // q는 맨투맨을 참조한다
let product;
try {
const res = await axios(`/products/${id}`);
product = res.data;
} catch {
// 데이터가 존재하지 않을 때의 예외 처리 → `/pages/404.js` 페이지로 이동 한다
return { notFound: true };
}
return { props: { product }}; // product 데이터를 컴포넌트의 props로 내려준다.
}
getStaticPaths()
의 리턴 값 중 fallback: true
라고 지정했을 때,
{ notFound: true }
를 리턴하면 데이터를 찾을 수 없을 때 404 페이지
로 이동시킬 수 있다.getStaticProps()
를 실행하는 동안 보여 줄 로딩 페이지는 data를 props를 받는 페이지 컴포넌트
에서 필요한 데이터가 존재하지 않을 때 (ex: if(!props)
) 를 처리해 주면 된다.export default function Product({ product }) {
if (!product) { // getStaticProps() 를 실행하는 동안 product 데이터가 아직 오지 않았을 때
return <>로딩 중 ...</>
}
return <>상품 이름: {product.name}</>;
}
: Next.js 서버에 리퀘스트가 도착할 때마다 페이지를 렌더링해서 보내주는 방식
getStaticProps() 대신 getServerSideProps()
함수를 구현하고, export
한다.
getStaticPaths()
와 함께 사용할 수 없다export const getServerSideProps = async () => {
const res = await axios('/products/');
const products = res.data;
return { props: { products }};
}
export default function Home({ products }) {
return (
<ProductList products={products} />
);
}