Next.js에서는 HTML 파일을 서버에서 Pre-rendering 하여 정적인 HTML 파일의 경우 클라이언트에게 대기시간 없이 빠르게 화면을 표출하고 CSR(Cllient Side Rendering)의 단점인 SEO(Search Engine Optimization)부분에서도 이점을 가져올 수 있다.
Next.js 에서 지원하는 Pre-Rendering 방식은 페이지 유형에 따라 다르게 사용할 수 있는 getServerSideProps와 getStaticProps 두가지가 있다.
1. getStaticProps
정적 사이트 생성(Static-Site-Generation, SSG)으로 빌드 시점에 서버에서 데이터를 가져와서 페이지를 렌더링한 후 만들어진 정적 html 파일을 클라이언트에게 제공하는 방식이다. 클라이언트 요청에는 빌드시점에 미리 생성된 정적 html 파일을 제공하기 때문에 속도는 빠르지만 동적으로 데이터를 가져오지 않기 때문에 데이터가 자주 바뀌는 페이지에는 적합하지 않고 데이터가 자주 바뀌지 않는 E-Commerce 상품 페이지나 브랜드 소개 페이지, 블로그 포스트 페이지 등 정적인 화면에 적합한 방식이다.
import { getAllPostIds, getPostData } from '@/lib/posts';
import { GetStaticPaths, GetStaticProps } from 'next';
import postStyles from '../../styles/Post.module.css';
import Head from 'next/head';
import React from 'react';
function Post({
postData,
}: {
postData: {
title: string;
date: string;
contentHtml: string;
};
}) {
//props로 받은 postData는 getStaticProps에서 반환받은 postData
return (
<div className={postStyles.container}>
<Head>
<title>{postData.title}</title>
</Head>
<article>
<h1 className={postStyles.headingXl}>{postData.title}</h1>
<div className={postStyles.lightText}>{postData.date}</div>
<div dangerouslySetInnerHTML={{ __html: postData.contentHtml }}></div>
</article>
</div>
);
}
export default Post;
export const getStaticPaths: GetStaticPaths = async () => {
const paths = getAllPostIds();
// ex) [{params: {id:'1'}}, {params: {id: '2'}}]
// pre-rendering할 동적 라우팅 페이지 id(경로)를 params 객체에 담아서 배열로 반환
return {
paths,
fallback: false,// paths에 없는 경로는 404페이지 표출
revalidate: 60, // 60초마다 페이지 재생성
};
};
export const getStaticProps: GetStaticProps = async ({ params }) => {
// params는 getStaticPaths 에서 넘겨준 사전 렌더링할 페이지의 id
// pre-rendering할 페이지의 데이터를 getStaticProps에서 가져오기 위함
const postData = await getPostData(params?.id as string);
return {
props: {
postData,
},
};
};
정적인 블로그 post 게시글을 Pre-rendering 하기 위해 getStaticProps를 사용하였다. 블로그 post의 경로는 '/posts/detail/[id]'로 동적라우팅이 되어있다. 동적라우팅 page 를 Pre-rendering 하기 위해서는 getStaticPaths를 같이 사용해서 사전에 Pre-rendering할 동적 경로 'id' 를 paths에 담고 params라는 매개변수로 getStaticProps로 전달한다.
- getStaticPaths
- 정적 Pre-rendering 할 동적 경로를 paths에 담아서 getStaticProps에 params로 매개변수를 전달한다.
- paths: 동적 경로 목록을 지정하는 배열, '[ {params: {...}}]' 형식으로 지정, 경로가 'posts/detail/[id]' 와 같은 동적경로가 있다면 [ {params: {id : '1'}}, {params: {id:'2'}}, ..] 와 같이 지정한다.
- fallback: 사전 렌더링되지 않은 경로에 접근했을때 동작을 지정한다. false로 지정할 경우 사전 정의되지 않은 경로로 접근할 경우 404 페이지를 표출, true로 지정할 경우 사전정의되지 않은 경로로 접근할 경우 서버 사이드에서 동적으로 페이지를 생성한다.
- revalidate : 정적 페이지의 재생성 주기를 설정할 수 있다. 위 코드에서는 60초를 주기로 페이지를 재생성하여 최신 데이터로 갱신한다.
getStaticPaths 에서 넘겨준 Pre-Rendering 할 동적경로는 위와 같이 params 를 매개변수로 받아서 가져올 수 있고 API 나 Database에서 데이터를 가져온 뒤 props에 담아서 반환하면 Post 컴포넌트에서 props로 받아서 사용할 수 있다.
2. getServerSideProps
서버 사이드 렌더링(Server-Side-Rendering, SSR)으로 클라이언트의 요청마다 서버에서 동적으로 데이터를 가져와서 렌더링한 후 클라이언트에게 제공하는 방식이다. 클라이언트의 요청마다 서버에서 데이터를 가져오기 때문에 SSG(Static-Site-Generation)보다 속도가 느리지만 항상 최신상태의 데이터를 보여줄 수 있는 이점이 있다. 클라이언트별로 다른 데이터를 보여줘야 하거나 데이터가 자주 업데이트 되는 페이지에서 적합한 방식이다.
import { GetServerSideProps } from 'next';
import { getPost } from '@/pages/api/HandlePost';
interface post {
POST_ID: string;
POST_TITLE: string;
AMNT_DTTM: string;
}
const PostDetailPage = ({ post }: { post: post }) => {
//props로 받은 post는 getServerSideProps에서 반환받은 post
// props로 내려받은 post data 처리 로직
.
.
.
// post data 처리 결과 표출
return (
<div>
.
.
.
</div>
);
};
export const getServerSideProps: GetServerSideProps = async (context) => {
const params = {
postId: context.params.id, // 동적 라우팅 매개변수
};
const post = await getPost(params).then(res => res);
// 결과값은 props에 넣어서 반환
return { props: {post} };
};
export default PostDetailPage;
'/posts/detail/[id]' 와 같이 동적 라우팅의 경우 getServerSideProps에서 context.params 로 id 값을 가져와서 사용할 수 있다.
이후 API 나 Database에서 데이터를 가져와서 위와 같이 props에 결과 값을 담아서 반환하면 PostDetailPage 컴포넌트에서 props로 가져와서 사용할 수 있다.
페이지의 유형에 맞게 개발자가 적절한 Pre-rendering 방식을 채택하여 속도 개선 및 SEO에서 모두 이점을 챙길수 있도록 잘 활용해야 할 필요가 있을 것 같다.