Next.js는 기본적으로 모든 페이지를 사전에 렌더링 진행 ( Pre-rendering )
페이지를 가져올 때 데이터 패칭이 필요한 경우(pre-rendering) getInitialProps
, getStaticProps
, getStaticPath
, getServerSideProps
등을 활용
_app.tsx
에서 데이터 패칭을 진행_app.tsx
에서 데이터 패칭 함수(getInitialProps
)가 존재한다면 실행 및 pageProps
를 받아서 처리getInitialProps
)가 존재한다면 실행 및 pageProps
를 받아서 처리pageProps
를 바탕으로 _app.tsx
/ Page Component
순으로 렌더링 진행_document.tsx
를 실행하여 최종 HTML 문서를 완성 및 반환Next.js는 2가지 방법을 제공합니다.
Static Generation: HTML 문서를 빌드타임에 생성한 후 요청시마다 재사용하는 방법 ( 첫번째 접속시 엄청 빠름 )
getStaticProps
/ getStaticPaths
활용Server-side Rendering: 요청시마다 HTML문서를 생성하는 방법
getServerSideProps
활용Next.js는 페이지 별로 어떤 Pre-rendering을 선택할지 개발자가 정의할 수 있고 상황에 맞게 적절하게 사용하는 것이 중요합니다.
pages/dynamic/[id].tsx
에서 getServerSideProps
함수를 정의합니다.getServerSideProps()
는 서버에서 동작하고 내부는 Node.js 환경입니다.getServerSideProps()
가 return 한 data를 props로 받을 수 있습니다.npm run build && npm run start
스크립트를 통해 Production 환경에서 테스트를 진행하면 이미 HTML문서에 API로 받아온 데이터를 채운 후 반환하는 모습을 볼 수 있습니다. import { GetServerSideProps } from "next";
import { useRouter } from "next/dist/client/router";
import axios from "axios";
// props 받기
const Dynamic = ({ item }) => {
// item 확인
console.log(item);
const router = useRouter();
const moveHome = () => {
router.push("/");
};
return (
<div>
<button onClick={moveHome}>홈으로</button>
<p>id: {item.id}</p>
<p>name: {item.name}</p>
</div>
);
};
export default Dynamic;
// getServerSideProps() 정의
export const getServerSideProps: GetServerSideProps = async (context) => {
// context 객체 내부에는 params, url, query 등 다양한 정보가 존재
const id = context.params.id;
const API_URL = `http://makeup-api.herokuapp.com/api/v1/products/${id}.json`;
const response = await axios.get(API_URL);
const data = response.data;
return {
props: {
item: data,
},
};
};
getStaticProps
/ getStaticPaths
를 활용합니다.getStaticProps
활용npm run build && npm run start
스크립트를 통해 Production 환경에서 테스트를 진행하면 이미 HTML문서에 API로 받아온 데이터를 채운 후 반환하는 모습을 볼 수 있습니다. import { GetStaticProps } from "next";
import axios from "axios";
import Head from "next/head";
import ItemList from "../component/itemList";
// props로 getStaticProps()가 return 한 data를 받을 수 있습니다.
const Index = ({ list }) => {
return (
<div>
<Head>
<title>Next</title>
</Head>
<p>Hello Next.js</p>
<ItemList data={list} />
</div>
);
};
export default Index;
// getStaticProps() 정의
export const getStaticProps: GetStaticProps = async () => {
const API_URL = `http://makeup-api.herokuapp.com/api/v1/products.json?brand=maybelline`;
const response = await axios.get(API_URL);
const data = response.data;
return {
props: {
list: data,
},
};
};
getStaticPaths
활용getStaticPaths
의 프로퍼티paths
: 배열에 등록된 Path는 static generation 적용 => 빌드 타임에 미리 정적 문서 생성fallback: boolean
: 위의 paths 배열에 선언한 path 이외에 다른 path가 들어올 경우 처리 방법false
: 404 Error 발생ture
: 첫 진입시 정적 문서를 만들고 위 paths에 등록 => 두번째 진입시 정적 문서 활용router.isFallback
을 활용하여 Loading을 구현할 수 있습니다.useRouter()
에서 제공합니다.true
: 아직 getStaticProps
를 통해 데이터를 받아오지 못한 경우false
: getStaticProps
를 통해 데이터를 받아온 경우import { GetStaticProps, GetStaticPaths } from "next";
import { useRouter } from "next/dist/client/router";
import axios from "axios";
const Dynamic = ({ item }) => {
const router = useRouter();
const moveHome = () => {
router.push("/");
};
// loading 구현 가능
const loading = router.isFallback;
return (
<div>
{item && (
<>
<button onClick={moveHome}>홈으로</button>
<p>id: {item.id}</p>
<p>name: {item.name}</p>
</>
)}
</div>
);
};
export default Dynamic;
// getStaticPaths() 생성
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [{ params: { id: "495" } }, { params: { id: "488" } }],
fallback: true,
};
};
// getServerSideProps() => getStaticProps()로 변경
export const getStaticProps: GetStaticProps = async (context) => {
const id = context.params.id;
const API_URL = `http://makeup-api.herokuapp.com/api/v1/products/${id}.json`;
const response = await axios.get(API_URL);
const data = response.data;
return {
props: {
item: data,
},
};
};
getStaticPaths
과 Next Link바로 위의 코드를 작성하고 테스트를 진행해보면 Path로 설정한 id: 495 / id: 488
이외에 모든 Item 들이 static generation으로 적용되어 정적 문서를 가지고 있습니다.
이유는 동적 라우팅을 활용하기 위해 사용한 Link
태그 때문입니다.
Link
태그는 기본적으로 Prefetch
옵션을 제공합니다.true
로 설정Prefetch
옵션: 첫 화면 / 스크롤 했을 때 뷰포트 내부에 있는 링크들은 모두 pre-renderring 및 static generation 진행component/itemList.tsx
import Link from "next/link";
export interface IItem {
id: number;
name: string;
}
export interface IProps {
data: IItem[];
}
const ItemList = ({ data }: IProps) => {
return (
<div>
{data.map((data) => (
// Link를 적용했기 때문에 모든 Item이 static generation 적용되어 모두 정적문서 생성
<Link href={`/dynamic/${data.id}`} key={data.id}>
<div key={data.id}>{data.name}</div>
</Link>
))}
</div>
);
};
export default ItemList;
getInitialProps
Next.js의 9.3v 이전에 자주 사용했던 데이터 패칭 방법입니다.
현재는 모든 페이지의 전역에 데이터 패칭이 필요한 경우 _app.tsx
에서 활용합니다.
_app.tsx
에서는 getStaticProps
/ getServerSideProps
를 지원하지 않습니다.getStaticProps
/ getServerSideProps
자체는 전역적인 데이터 패칭 기능을 지원하지 않고 페이지 단위로 지원합니다._app.tsx
에서 getInitialProps
를 사용한다면 Automatic Static Optimization이 비활성화되어 모든 페이지가 Pre-rendering 방식이 Server Side Rendering으로 동작하게 됩니다.
getInitialProps
만 실행됩니다._app.tsx
에 getInitialProps
가 선언되어 있고 다른 하위 페이지에 getInitialProps
가 선언되어 있을 경우 하위 페이지의 getInitialProps
은 실행되지 않습니다.Pages/_app.tsx
getInitialProps
의 context
ctx
: 다양한 정보를 담고 있는 객체pathname
: 현재 page의 pathquery
: 현재 page의 queryasPath
: pathname + queryreq
: request messageres
: request messageerr
: error messageComponent
: 해당 컴포넌트를 의미import type { AppProps } from "next/app";
import "../styles/globals.css";
import Header from "../component/header";
import Footer from "../component/footer";
// PageProps는 모든 Page에서 활용 가능
const App = ({ PageProps }: AppProps) => {
return (
<div>
<Header />
<Component {...pageProps} />
<Footer />
</div>
);
};
export default App;
App.getInitialProps = async (context) => {
const { ctx, Component } = context;
let pageProps = { props: "test" };
// return한 값은 해당 컴포넌트의 props로 들어가게 됩니다.
return { pageProps };
};
정리가 잘 되어 있네요! 도움 많이 됐습니다