브라우저는 html의 정보를 읽어서 이를 내부적으로 렌더링하고 js + css 를 반영한 후 최종적으로 뷰를 뿌려준다.
리를 렌더링이라고 하며 렌더링에는 두가지 방식이 있다.
1. CSR(Client Side Rendering)
React.js만 사용하여 웹을 만들면 기본적으로 CSR, SPA(Single Page Application)방식으로 랜더링된다.
최초로 한번만 전체 페이지를 로딩하고 이후에는 브라우저 내부에서 데이터만 사용하는 방식이다.
따라서 이 방식은 때마다 렌더링을 할 경우에 성능에 부담을 줄 수 있기에 이를 고려한 환경에서 자주 이용된다.
이러한 SPA방식은 트래픽을 감소시키고 사용자에게 최적화 된 환경을 제공해 줄 수 있다.
CSR은 서버는 단순히 클라이언트에게 json파일만을 보내주고, 클라이언트 측에서는 초기에 전송받은 HTML , javscript를 통해 랜더링을 수행하는 과정을 말한다.
CSR의 경우 서버에서는 뷰를 렌더링하지 않고 HTML, javascript 등 각종 리소스를 서버에서 다운 받은 후 브라이저에서 랜더링을 수행하게 되는데 때문에 처음 뷰를 로딩할때의 속도가 느리다는 단점이 있다. 하지만 사용자의 액션에 따라 필요한 부분만 부분적으로 로딩하기 때문에 인터랙션이 빠르다.
또한 SPA의 치명적인 단점은 SEO에 적합하지 않다는 것이다.
SPA의 경우 완성된 페이지가 아닌 HTML, javascript 같은 리소스를 받아 브라우저에서 조합하기 때문에 어려움이 존재한다.
( 검색엔진이 public/index.html 을 읽었을 때 초기정보를 알 수 없기 때문...)
2. SSR(Server Side Rendering)
서버에게 웹 페이지를 요청할때 마다 새로고침이 일어나며, 서버에서 새로운 html을 받아와서 서버에서 랜더링 하는 것을 의미한다.
pre-rendering은 server단에서 dom요소들을 build하여 html 문서를 랜더링 하는것을 말한다.
Next.js에서는 클라이언트에게 웹페이지를 보내기 전, server side단에서 미리 웹페이지를 pre-rendering 한다.
프리랜더링하면 html document가 생성되고 이 파일을 클라이언트에게 전송한다.
next.js 사용 시에 html을 미리 렌더링하고 그 뒤 요청이 오면 chunk 단위로 자바스크립트를 보내주어 이벤트가 작동하게 되는 것이 Hydration이다.
Hydration
Hydration은 server side 단에서 렌더링 된 정적 페이지와 번들링된 Js 파일을 클라이언트에게 보낸 뒤 클라이언트 단에서 html 코드와 React.js 코드를 서로 매칭시키는 과정을 말한다.
🤷🏻♀️ 그러면 렌더링을 두번 하는 게 아닌가?
하지만 서버 단에서 빠르게 pre-rendering하고 유저에게 빠른 웹페이지로 응답할 수 있다는 것이 큰 이점이다.
pre-rendering한 Document는 모든 자바스크립트 요소들이 빠진 가벼운 상태이기 때문에 클라이언트에게 빠른 로딩이가능하다.
때문에 렌더링을 두 번 일어나지만 그 단점을 보완하게 되는 것.
더 나아가서 클라이언트 단에서 자바스크립트가 렌더링 될 때, 단지 각 Dom 요소에 자바스크립트 속성을 매칭 시키기 위한 목적이므로 실제 웹페이지를 다시 그리는 과정까지는 하지 않는다 (paint 함수 호출하지 않음)
ReactDOM.hydrate(element, container, [callback]);
ReactDOM.hydrate() 함수는 특정 컴포넌트를 두 번째 파라미터인 지정된 DOM 요소에 하위로 hydrate 처리만 한다.
이는 렌더링을 통해 새로운 웹 페이지를 구성할 DOM을 생성하는 것이 아니라, 기존 DOM Tree에서 해당되는 DOM요소를 찾아 정해진 자바스크립트 속성(이벤트 리스너 등)들만 부착시키겠다는 것이다.
SSG(Static-Site-Generation)
pre-rendering을 동적으로 해서 페이지를 생성하는가, 정적으로 해서 페이지를 생성하는가에 대한 차이가 SSR과 SSG의 차이이다.
SSG는 빌드를 진행할 때 pages 폴더에서 작성한 각 페이지들에 대해 각각의 문서를 생성해서 static한 파일로 생성한다.
만약 해당 페이지에 대한 요청이 발생하게 되면, 이 페이지들을 다시만드는 것이 아니라 이미 생성되어 있는 페이지를 반환하는 형태로 동작한다. 따라서 CSR보다 응답속도가 빠른 것.
언제 사용하는가?
-> 블로그 게시글, 제품 목록 등 정적 생성된 정보를 각 요청에 동일한 정보로 반환하는 경우에 이를 사용하면된다.
(1) getStaticProps
Next.js에서 SSG를 사용하기 위해서는 getStaticProps를 사용하면된다.
이는 서버측에서만 실행되는 함수이다.
빌드시 한 번만 호출되며, static file로 빌드 되는데, 이 함수는 API와 같은 외부데이터를 받아 Static Generation 하기 위한 용도이다.
page/index.js
import Head from "next/head";
import Layout, { siteTitle } from "../components/layout";
import utilStyles from "../styles/utils.module.css";
import { getSortedPostsData } from "../lib/post";
import Link from "next/link";
import Date from "../components/date";
export async function getStaticProps() {
const allPostsData = getSortedPostsData();
return {
props: {
allPostsData,
},
};
}
export default function Home({ allPostsData }) {
return (
<Layout home>
<Head>
<title>{siteTitle}</title>
</Head>
<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}>
<Link href={`/posts/${id}`}>{title}</Link>
<br />
<small className={utilStyles.lightText}>
<Date dateString={date} />
</small>
</li>
))}
</ul>
</section>
</Layout>
);
}
page가 호출되면 getStaticProps()가 먼저실행되게 된다. 위 코드에서는 getSortedPostData()함수를 호출하여 사용했는데, 보통은 fetch(혹은 axios)를 통해 게시물 리스트를 가져오고 그 이후에 props에 리턴값을 담에서 index.js에 전달한다.
이렇게 getStaticProps()를 사용해서 코드를 작성한 후 build를 하면, 사전 서버에서 api 호출을 해서 데이터를 담고, 그 데이터가 담긴 html을 생성하게된다.
(2) getStaticPaths
getStaticPaths는 동적 라우팅에 사용된다. 페이지를 동적으로 생성할때, 특히 특정 페이지는 정적으로 생성할때 사용한다.