ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

Issue

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์„ ์ƒ์„ฑํ•ด๋ฒ„๋ฆฌ๋ฉด ์œ ๋™์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ด€๋ จ ๋ฉ”์„œ๋“œ๋“ค์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ์—๋Š” ์‹ ์ค‘ํžˆ ์‚ฌ์šฉํ•ด์•ผ ํ•จ์„ ์žฌํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

 

 

๋Œ“๊ธ€
๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
TAG
more
ยซ   2025/01   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
๊ธ€ ๋ณด๊ด€ํ•จ