Next.js에선 기본적으로 모든 페이지를 PreRender한다. 즉 HTML이 클라이언트 사이드에서 자바스크립트를 통해 생성이 되는 게 아니라 Next.js가 사전에 각 페이지를 만들어 놓는다. 이때 각 HTML은 해당 페이지에 최소한으로 필요한 자바스크립트 코드와 결합되어있다.
페이지가 브라우저에 의해 로드될 때는 hydration이라는 프로세스를 거치게 된다. hydration은 자바스크립트 코드가 실행되어 해당 페이지를 완전히 인터렉티브하게 만드는 것을 의미한다.
프리 렌더링을 할 경우, 프리 렌더링을 하지 않을 때보다 더 나은 성능과 SEO를 갖출 수 있다.
Next.js는 프리 렌더링 및 서버사이드 렌더링을 포함한 페이지 렌더링을 위한 런타임으로 Node.js를 사용한다.
Next.js의 pre-rendering 형태는 Static Generation과 Server Side Rendering으로 나뉘며 각 페이지별로 다르게 적용할 수 있다. 이 pre-rendering 방식의 차이는 HTML을 생성할 때에 있다.
서버에서 요청할 때 즉시만드느냐 혹은 미리 만들어 놓느냐에 따라 차이가 있다.
SSR은 요청시 서버에서 즉시 HTML을 만들어서 응답하기 때문에 데이터가 달라져서 미리 만들어 두기 어려운 페이지에 적합하다.
반면 SSG는 미리 다 만들어두고 요청시에 해당 페이지를 응답하기 때문에 바뀔일이 거의없어서 캐싱해두면 좋은 페이지에 사용된다.
기존 pages 라우터 방식
// pages/index.js
export default function Page() {
return <h1>Hello, Next.js!</h1>;
}
새로운 앱 라우터 방식
// app/layout.js
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
// app/page.js
export default function Page() {
return <h1>Hello, Next.js!</h1>;
}
경로의 각 폴더는 경로 세그먼트를 나타낸다. 각 경로 세그먼트는 URL 경로의 해당 세그먼트에 매핑된다.
경로 세그먼트의 특수 파일에 정의된 React 구성 요소는 특정 계층 구조로 렌더링된다.

export default function Page({ params }: { params: { slug: string } }) {
return <div>My Post: {params.slug}</div>
}app/blog/[slug]/page.js => /blog/a => { slug: 'a' }병렬 라우팅을 사용하면 동일한 레이아웃에서 하나 이상의 페이지를 동시에 또는 조건부로 렌더링할 수 있다.

React 및 Next.js에서 스트리밍이 작동하는 방식을 배우려면 SSR(서버 측 렌더링)과 그 제한 사항을 이해하는 것이 좋다.
이 단계는 순차적이고 차단적이다. 즉, 모든 데이터를 가져온 후에만 서버가 페이지에 대한 HTML을 렌더링할 수 있다. 그리고 클라이언트에서 React는 코드가 로드된 후에만 UI를 수화할 수 있다.
스트리밍을 사용하면 페이지의 HTML을 더 작은 청크로 나누고 점진적으로 해당 청크를 서버에서 클라이언트로 보낼 수 있다.
이를 통해 UI가 렌더링되기 전에 모든 데이터가 로드될 때까지 기다리지 않고 페이지의 일부를 더 빨리 표시할 수 있습니다. 우선순위가 더 높거나 데이터에 의존하지 않는 구성 요소가 먼저 전송될 수 있으며 React는 더 일찍 수화를 시작할 수 있다.
또한, loading.js 파일을 만들어서 트리밍 형태의 로딩 UI를 생성할 수 있음. loading.js 파일을 생성하면 React Suspense를 자동으로 래핑하여 로딩 화면을 보여주고, 라우트 세그먼트의 내용을 로드하는 동안 서버에서 즉시 로딩 상태를 표시하고, 렌더링이 완료되면 자동으로 새로운 콘텐츠로 교체됨
// This request should be cached until manually invalidated.
// Similar to `getStaticProps`.
// `force-cache` is the default and can be omitted.
fetch(URL, { cache: 'force-cache' });
// This request should be refetched on every request.
// Similar to `getServerSideProps`.
fetch(URL, { cache: 'no-store' });
// This request should be cached with a lifetime of 10 seconds.
// Similar to `getStaticProps` with the `revalidate` option.
fetch(URL, { next: { revalidate: 10 } });