ํฐ์คํ ๋ฆฌ ๋ทฐ
Next.js๋ก ์ ์ ์์ฑ(Static Generation) ๊ตฌํ_getStaticProps์ getStaticPaths ์ฌ์ฉ๊ธฐ
choi95 2022. 3. 31. 13:06Issue
Next.js ํ๋ก์ ํธ ๋์ค ์ํ ์์ธ ํ์ด์ง๋ฅผ ์ ์ ์์ฑ(Static Generation)์ ํ๊ธฐ ์ํด์ getStaticProps์ getStaticPaths๋ฅผ ์ฌ์ฉํด ๋ณด๊ธฐ๋ก ํ์๋ค.ํด๋น ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ ์ด์ ์ ์ ์ ์์ฑ, getStaticProps, getStaticPaths๊ฐ ์ด๋ค ์๋ฏธ๋ฅผ ์ง๋๊ณ ์๋์ง ๊ฐ๋ตํ๊ฒ ๋ฉ๋ชจํด ๋๊ณ ์ ํ๋ค.
์ ์ ์์ฑ(Static Generation)์ ๋น๋ ์์ ์ ์จ์ ํ ํ์ด์ง์ HTML์ด ์์ฑ๋์ด ์๋ฒ์์ ๋ฌผ๋ฆฌ์ ์ผ๋ก HTML ํ์ผ์ ๋ชจ๋ ๊ฐ๊ณ ์๋ ์ํ์ด๋ค. ํ ๋ฒ ์๋ตํ ํ์๋ CDN(content delivery network)์ด ํ์ผ์ ๊ธฐ์ต(์บ์ฌ, cache)ํ์ฌ ์๋ตํ๊ธฐ ๋๋ฌธ์ ๋๋๋ง ์๋๊ฐ ๋น ๋ฅด๊ณ ๋ถํ์ํ ์๋ฒ ์์ฒญ์ ์ค์ผ ์ ์๋ค.
getStaticProps๋ ๋น๋ ์์ ์ api๋ฅผ ํธ์ถํ๊ณ ๋ฐ์ดํฐ๋ฅผ ์๋ต ๋ฐ์์ HTML์ ์์ฑํ๋ ์ ์ ์์ฑ์ ์ํ ๋ฉ์๋์ด๋ค.
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
getStaticPaths๋ ๋์ ๋ผ์ฐํธ์ธ ํ์ด์ง์ด๋ฉด์ ์ ์ ์ผ๋ก ํ์ด์ง๋ฅผ ์์ฑํ๊ณ ์ถ์ ๋ ์ฌ์ฉ๋๋ ๋ฉ์๋์ด๋ค.
export async function getStaticPaths() {
return {
paths: [
{ params: { ... } }
],
fallback: true // false or 'blocking'
};
}
Code
1) api ๋น๋๊ธฐ ํจ์ ๋ถ๋ฆฝ
api fetch๋ ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋๋ง๋ค ๋งค๋ฒ ๋ก์ง์ ์ง์ค์ผ ํ๊ธฐ ๋๋ฌธ์ ๊ฐ๊ฐ์ ์ปดํฌ๋ํธ์ ๊ด๋ จ ๋ก์ง์ ์ ์ํด์ค ๋์ ์ ๋ณ๋๋ก ๋ถ๋ฆฝํ์๋ค.
๐ฆ api
โฃ ๐ contacts.api.ts
โ ๐ items.api.ts // ํด๋น ์ปดํฌ๋ํธ ๋ด์์ API ํต์ ์ ์ํ ๋น๋๊ธฐ ํจ์ ์ ์
ํด๋น ์ปดํฌ๋ํธ ๋ด์์ async/await ํจ์๋ฅผ ํตํด ๋น๋๊ธฐ ๋ก์ง์ ์ฒ๋ฆฌํด์ฃผ๊ณ ์๋ฒ์์ ๋ฐ์ ์จ ๋ฐ์ดํฐ์ ํ์ ์ ์ธํฐํ์ด์ค์ ํตํด ์ ์ํด์ฃผ์๋ค.
import axios from 'axios';
export interface ItemInfoType {
id: number;
name: string;
originalPrice: number;
minSellingPrice: number;
ncSellingPrice: number;
warning: string;
discountRate: number;
imageUrl: string;
options: Option[];
brand: string;
}
export interface Option {
expireAt: Date;
count: number;
sellingPrice: number;
}
export async function getItemInfo(paramsId: string | string[] | undefined) {
const request = await axios.get(`https://(..URL)/${paramsId}`);
const { data: { conItem } } = request;
// ์ํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฉํํ๊ธฐ ์ํด ์๋ฒ์์ ๋ฐ์ ์จ ๋ฐ์ดํฐ๋ฅผ ๋์คํธ๋ญ์ณ๋ง ํ ๋น
const { id, name, originalPrice, minSellingPrice, ncSellingPrice, warning, discountRate, imageUrl, options, } = conItem;
const itemInfo = { id, name, originalPrice, minSellingPrice, ncSellingPrice, warning, discountRate, imageUrl, options, brand: conItem["conCategory2"].name };
return {
itemInfo
}
}
2) pages ํด๋ ๋ด์์ getStaticProps ์ฌ์ฉ
pages ํด๋์์ Next.js์ ํ์ผ ๊ธฐ๋ฐ ๋ผ์ฐํ ๊ธฐ๋ฅ์ ๋ด๋นํ๋ ๋๋ ํ ๋ฆฌ์ด๋ค. ํด๋ ํน์ ํ์ผ ์ด๋ฆ์ ์ง์ ํ์ฌ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค๋ฉด ์ด์ ํด๋นํ๋ router๊ฐ ์๋์ผ๋ก ์ง์ ๋๋ค.
๐ฆitems
โ ๐[id] // query parameter
โ โ ๐index.tsx
ํด๋น ์ปดํฌ๋ํธ ๋ด์์ api ํจ์๋ฅผ ํธ์ถํ์ฌ ๋ฏธ๋ฆฌ ๊ฐ ์ํ๋ค์ ๋ํ ์์ธ ์ ๋ณด ๋ฆฌ์คํธ๋ฅผ ์๋ฒ์์ ๋ถ๋ฌ์ HTML์ ๊ตฌ์ฑํจ์ผ๋ก์จ, ์ ์ ์์ฑ์ด ๊ตฌํ๋๋๋ก ํ์๋ค.
export const getStaticProps: GetStaticProps = async ({ params }) => {
const id = params && params.id;
const { itemInfo } = await getItemInfo(id);
if (!itemInfo) {
return {
notFound: true,
};
}
return {
props: itemInfo,
};
};
๋ํ ์๋ฒ์์ ๊ฐ์ ธ ์ฌ ์ํ ๋ฐ์ดํฐ๋ค์ query parameter id๊ฐ์ผ๋ก ๋์ ๋ผ์ฐํ ์ด ๋๊ธฐ ๋๋ฌธ์ ์ํ ๋ฆฌ์คํธ์ ํด๋นํ๋ ๋ชจ๋ ์ํ๋ค์ ์์ธ ํ์ด์ง๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด์ getStaticPaths๋ ๊ฐ์ด ์ฌ์ฉํด์ผ ํ๋ค.
(getStaticProps์ ์ธ์๋ก ๋ฐ๋ params๊ฐ query parameter id๊ฐ์ ํด๋นํ๋ค)
์ด๋ฅผ ์ํด ๋ค์๊ณผ ๊ฐ์ด ์ํ ์นดํ ๊ณ ๋ฆฌ์ ๋ํ ์ํ ๋ฆฌ์คํธ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ์์ ๋ฐ์์ค๊ณ , ํด๋น ์ํ ๋ฆฌ์คํธ ๋ฐ์ดํฐ์์ ๊ฐ ์ํ์ ์์ธ ํ์ด์ง์ ํด๋นํ๋ ๊ณ ์ id๊ฐ์ ์ถ์ถํ์ฌ paths์ ์ ์ฌํ์ฌ ๋ฐํํ์๋ค.
export const getStaticPaths: GetStaticPaths = async () => {
const {
data: { conCategory1s },
} = await axios.get('https://(...URL)');
const categoriesID = conCategory1s.map(({ id }: any) => id);
let paths: any = [];
for (const id of categoriesID) {
const { data } = await axios.get(`https://(...URL)`);
const {
conCategory1: { conCategory2s },
} = data;
let conItems = [];
for (let i = 0; i < conCategory2s.length; i++) {
conItems = conCategory2s[i].conItems;
const conItemsID = conItems.map(({ id }: any) => ({
params: { id: id.toString() },
}));
paths = paths.concat(conItemsID);
}
}
return { paths, fallback: false };
};
๋ง์ง๋ง์ผ๋ก getStaticProps์์ ๋ฐํํ ๊ฐ์ ๋๋๋งํ๊ณ ์ ํ๋ ์ปดํฌ๋ํธ ๋ด์์ ๋ค์๊ณผ ๊ฐ์ด props๋ก ์ ๋ฌ ๋ฐ์ ์ ์ ์์ฑ ๊ธฐ๋ฅ์ ๋ง๋ฌด๋ฆฌํ์๋ค.
const Items: React.FC<ItemInfoType> = (itemInfo: ItemInfoType) => {
return (
<ItemsTemplate>
<ItemsForm itemInfo={itemInfo} />
</ItemsTemplate>
);
};
ํ๊ณ
๊ฒฐ๊ณผ์ ์ผ๋ก ๊ฐ ์ํ๋ค์ ๋ํ ์์ธ ํ์ด์ง๋ฅผ ์ ์ ์์ฑ ๋ฐฉ์์ ํตํด ํด๋ผ์ด์ธํธ ์ธก์์ ๋๋๋งํ๋ ๋ฐ์ ๊ธฐ๋ฅ ์์ผ๋ก ์ฑ๊ณตํ์๋ค.ํ์ง๋ง CSR ๋ฐฉ์์ผ๋ก ๋๋๋งํ์์ ๋๋ณด๋ค ๋๋๋ง ์๋๊ฐ ํ์ ํ ๋๋ฆฐ ์ฑ๋ฅ ์์ ๋ฌธ์ ๊ฐ ์์๋ค.
๋๋๋ง ์ฑ๋ฅ์ ๋์ด๊ธฐ ์ํด์ ๋จ์ํ ์๋ฒ ์ธก์์ ํ์ด์ง๋ฅผ ๊ตฌ์ฑํ๋ SSR ๋ฐฉ์์ด, ์ ๋ต๋ง์ ์๋๋ผ๋ ๊ฒ์ ํด๋น ํ๋ก์ ํธ์์ ํ์ธํด ๋ณผ ์ ์์๋ค.
๋ํ ์ผํ๋ฌผ๊ณผ ๊ฐ์ด ์ปค๋จธ์ค ๊ธฐ๋ฐ์ ์๋น์ค ๋ด์์๋ ๋งค์ผ ์๋ก์ด ์ํ์ ๋ณด์ฌ์ฃผ๊ธฐ ์ํ์ฌ ์ํ ๋ชฉ๋ก api๋ฅผ ํธ์ถํด์ ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ผ ํ๋๋ฐ, ๋ง์ฝ ๋น๋ ์์ ์ ์ ์ ์ผ๋ก HTML์ ์์ฑํด๋ฒ๋ฆฌ๋ฉด ์ ๋์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค์ง ๋ชปํ๋ ๋ฌธ์ ๊ฐ ์๊ธฐ ๋๋ฌธ์, ๊ด๋ จ ๋ฉ์๋๋ค์ ์ฌ์ฉํ ๊ฒฝ์ฐ์๋ ์ ์คํ ์ฌ์ฉํด์ผ ํจ์ ์ฌํ์ธํ ์ ์์๋ค.