Next.js는 React 라이브러리의 프레임워크이다.
Next.js는 React 생태계의 다양한 문제점들을 해결하기 위해 탄생한 프레임워크로,
React 애플리케이션을 쉽고 효율적으로 개발할 수 있게 해준다.
그 중 하나가 pre-rendering 이다.
Next.JS는 React의 CSR 방식을 유지하되, pre-rendering을 통해 처음 렌더링 시 발생하는 CSR의 여러 문제점을 해결해준다.
기본적으로 React는 Client Side Rendering(CSR)을 한다.
CSR은 유저가 웹사이트를 요청했을 때, 서버로부터 빈 html을 가져와 script를 로딩 No Pre-rendering 하고, 브라우저가 빈 html과 JS Bundle을 가지고 렌더링 작업을 모두 진행한다.
따라서 첫 로딩 시간 FCP 가 오래걸리고 Search Engine Optimization(SEO)에 취약하다는 단점이 있다.

또한 React는 데이터를 받아올때 보통 컴포넌트가 마운트 될 때, useEffect를 통해 백엔드로 부터 데이터를 받아오고 받아오는 시간동안은 로딩처리를 한다.
하지만, 이런 방식은 사용자입장에서 느린 FCP와 더불어 컴포넌트가 마운트 된 뒤,
한번 더 API 데이터패칭 시간을 기달려야 하므로 큰 단점이라고 볼 수 있다.

Next.js는 서버에서 미리 렌더링된 페이지를 가져와 Pre-rendering 브라우저에게 넘겨준다.
따라서 첫 로딩 시간 FCP 가 빨라지고, 검색 엔진에 잘 노출 될 수 있도록 해주는 SEO에서도 장점을 얻을 수 있다.
이후 서버에서 JS Bundle을 보내주고, 브라우저에서는 수화 작업만 이루어지며 최종적으로 상호작용이 가능한 웹 페이지가 만들어진다.
결과적으로, 빠른 FCP 달성 + CSR의 빠른 페이지이동 장점 을 가능하게 해준다.

위에서 언급했던 React의 데이터 패칭의 문제점을 해결하기 위해 Next.js는 다양한 렌더링 방식을 제공한다.
- SSR
- SSG - Next.JS의 기본 렌더링 방식
- ISR
⭐ SSR 혹은 SSG와 같이 서버에서 렌더링이 이루어질 때 CSR을 사용하고 싶다면 useEffect를 사용하면 된다!!
Next.js는 서버사이드렌더링을 통해 초기에 이미 서버에서 데이터 패칭이 완료된 파일을
브라우저에게 넘겨주기 때문에 React와 달리 컴포넌트가 마운트된 뒤 데이터 패칭을 진행 할 필요가 없다.

해당 컴포넌트 상단에 getServerSideProps 함수를 선언하면 된다.
이후 받아온 데이터를 반드시 props안의 객체로 return하고 컴포넌트에 전달하면 Next.js가 인식할 수 있다.
import { InferGetServerSidePropsType } from "next";
// 컴포넌트보다 먼저 실행
export const getServerSideProps = () => {
const data = /*백엔드에서 받아올 데이터*/
return {
props: {
data,
},
};
};
export default function Home({data}: InferGetServerSidePropsType<typeof getServerSideProps>) {
useEffect(() => {
console.log(window);
}, []);
return (
<div>{data}</div>
);
}
Next.js는 SSR의 방식을 보완하기 위해 SSG방식도 제공한다.
앞서 SSR은 접속요청이 발생 시 서버에서 데이터 패칭이 이루어지는데, 이때 서버에 문제가 있거나, 데이터가 많을경우 렌더링 시간이 오래 걸린다는 단점이 있다.
SSG는 이를 해결하기 위해, 빌드 타임에 데이터 패칭을 모두 완료함으로써 접속요청 발생시 바로 렌더링을 할 수 있도록 해준다.
하지만 빌드 타임에 데이터패칭이 이루어지므로 실시간 데이터 반영은 어렵다라는 단점이 있다.

아무것도 작성하지 않는다면, Next.js는 기본적으로
SSG로 방식으로 설정된다.
해당 컴포넌트 상단에 getStaticProps 함수를 선언하면 된다.
이후 받아온 데이터를 반드시 props안의 객체로 return하고 컴포넌트에 전달하면 Next.js가 인식할 수 있다.
import { InferGetServerSidePropsType } from "next";
// 빌드타임에 실행
export const getStaticProps = () => {
const data = /*백엔드에서 받아올 데이터*/
return {
props: {
data,
},
};
};
export default function Home({data}: InferGetStaticPropsType<typeof getStaticProps>) {
useEffect(() => {
console.log(window);
}, []);
return (
<div>{data}</div>
);
}
동적 페이지, 예를 들면 특정 도서 조회페이지 같은 경우에는 사용자가 몇번 도서를 검색할지 모르므로, 빌드타임에 페이지를 렌더링 해 줄 수 없다.
이때 getStaticPaths을 사용한다.
getStaticPaths는 기본적으로 아래와 같은 형태를 가지며 paths에 선언한 값은 빌드타임에 만들어지고,
그 외의 페이지들은 fallback의 속성에 따라 다르게 동작한다.
- false : paths에 지정해 놓지 않은 값들은 모두 404 띄운다.
- blocking : paths에 지정해 놓지 않은 값들은
SSR방식으로 처리한다.- true: :
blocking방식의 보완 (SSR방식으로 데이터 불러올 때 까지로딩상태보여줌)
import { InferGetServerSidePropsType } from "next";
// 빌드타임에 실행
export const getStaticPaths = () => {
return {
paths: [
{ params: { id: "1" } },
{ params: { id: "2" } },
{ params: { id: "3" } },
],
fallback: true,
};
};
export default function Home({data}: InferGetStaticPropsType<typeof getStaticProps>) {
if(router.isFallback) return "로딩중..";
...
}
Next.js는 SSG의 방식을 보완하기 위해 ISR방식을 제공한다.
앞서 SSG은 실시간 데이터 반영은 어렵다라는 단점이 있다고 언급했다.
ISR는 이를 해결하기 위해, 특정시간을 주기로 정적 페이지를 다시 생성한다.
즉
SSG의 방식을 따르되, 특정시간을 기준으로SSR을 수행하는SSG + SSR방식이다.

SSG방식에서 특정 시간을 선언하는 revalidate를 선언해주면 된다.
import { InferGetServerSidePropsType } from "next";
// 빌드타임에 실행
export const getStaticProps = () => {
const data = /*백엔드에서 받아올 데이터*/
return {
props: {
data,
},
revalidate: 60, // 60초 주기로 페이지 재생성
};
};
export default function Home({data}: InferGetStaticPropsType<typeof getStaticProps>) {
useEffect(() => {
console.log(window);
}, []);
return (
<div>{data}</div>
);
}
특정시간을 기준으로 데이터가 업데이트가 되어야하는 페이지가 아닌, 사용자의 상호작용을 통해 데이터가 반영되어야하는 경우 (ex) 게시판 게시글 업데이트
특정시간을 기준으로
ISR을 실행하는 방식이 아닌,
특정 API를 호출할때 마다ISR을 실행하도록 만들어줄 수 있다.
page Router 기준으로, 프로젝트의 api폴더에 revalidate.ts파일을 생성 후 revalidate 하고자 하는 페이지의 경로를 아래와 같이 설정해 주면 된다.
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
await res.revalidate("/"); // index 페이지 revalidate
return res.json({ revalidate: true });
} catch (err) {
res.status(500).send("Revalidation Failed");
}
}