[Next.js 마스터리] Next.js페이지 렌더링 -SSG

TK·2024년 1월 18일
0

[강의] Next.js

목록 보기
2/4
post-custom-banner

SSG

(Static Site Generation, 정적 사이트 생성)

서버측에서 페이지를 빌드 타임에 한번만 렌더링 하는 것

... 이후의 hydration과정은 SSR과 같음

정리

  • 빠른 페이지 응답을 보장할 수 있지만
  • 최신 데이터를 반영하기는 어렵기 때문에
  • 페이지 내부 데이터가 변경되지 않는 페이지에 적절한 렌더링 전략

실습

SSR에서 SSG로 바꾸기

pages/index.js

  • 이전에 사용했던 getServerSideProps에서 getStaticProps로만 바꾸면 된다.
import { fetchCountries } from "@/api";

export default function Home({ countries }) {
  return (
    <div>
      {countries.map((country) => {
        return <div key={country.code}>{country.commonName}</div>;
      })}
    </div>
  );
}

export const getStaticProps = async () => { //👈🏻 
  console.log("countires 데이터 불러옴"); // 👈🏻참고
  const countries = await fetchCountries();
  return {
    props: {
      countries,
    },
  };
};

참고: getStaticProps 내부에 있는 console.log 찍힘

  • 빌드 전 npm run dev에서 작동시킬때는 새로고침을 할때마다 서버측에 내용이 나타나지만,
  • 빌드 이후 npm run start로 작동시키면 서버에 나타나지 않는다.

pages/country/[code].js

  • 동적경로는 이 페이지는 위와 같이 단순하게 getStaticProps를 사용해서 만들 수 없다.
  • 존재하는 경로를 찾아내는 함수가 필요하다.

import { fetchCountry } from "@/api";
import SubLayout from "@/components/SubLayout";
import { useRouter } from "next/router";

export default function Country({ country }) {
  // 클라이언트에서 사용
  const router = useRouter();
  const { code } = router.query;

  if (!country) {
    return <div>존재하지 않는 국가입니다.</div>;
  }
  return (
    <div>
      {country.commonName}
      {country.officialName}
    </div>
  );
}

Country.Layout = SubLayout;

export const getStaticPaths = async () => {
  return {
    paths: [
      //어떤경로가 필요한지 명시
      { params: { code: "ABW" } },
      { params: { code: "KOR" } },
    ],
    fallback: true, // 👈🏻paths에 없는 경로 요청이 오는 경우 처리 옵션 (하단설명참고)
  };
};

export const getStaticProps = async (context) => {
  // 서버측에서 불러옴
  const { code } = context.params;
  let country = null;
  if (code) {
    country = await fetchCountry(code);
  }
  return {
    props: { country },
  };
};
  • 존재하지 않는 경로를 요청하는 경우

    1. fallback: false; : 404페이지


    2. fallback: "blocking"; : 빌드타임에 생성하지 않은 페이지도 실시간으로 생성해서 제공할 수 있다.

    • 존재하지 않는 경로를 요청하면 서버에서 해당 페이지를 생성해서 반환 및 저장해둔다.
    • 그 이후 같은 페이지를 요청하면 저장해 둔 페이지를 곧바로 제공한다.
      • 특징: (짧은) 로딩 시간이 생김

    1. fallback: true; : 사용자에게 데이터 없는 상태의 페이지를 먼저 보여준 후 데이터를 보냄
    • 지정값 외에 경로 요청시 빈 페이지 (존재하지 않는 국가입니다.)를 먼저 내보냈음을 확인할 수 있다.

    • 그 이후 데이터 값을 보내주었다.

      • 특징: blocking 방식에 비해 페이지 자체는 빠르게 렌더링되어 사용자 입장에서는 쾌적한 환경이라고 느낄 수 있다.

      참고: router객체를 이용해 현재 페이지가 fallback상태인지 구분 가능

      • 컴포넌트 내에 다음 코드 추가
      export default function Country({ country }) {
        const router = useRouter();
        const { code } = router.query;
        if (router.isFallback) {    // 👈🏻
          return <div>Laoding...</div>;
        }
        // ...후략...
      }

pages/search/index.js

  • pages/country/[code].js처럼 동적경로를 갖는다고 생각하고 getStaticPaths를 사용할 수 없음
    → 이유1. url parameter 활용하는 페이지에서만 getStaticPaths사용 가능
    → 이유2. getStaticProps함수 내부에서는 url parameter에 접근가능하지만, qeury string에는 접근 불가
    (query stirng 값은 너무 랜덤하다. url parameter는 경우의 수가 한정되어 있다.)

※ 클라이언트 사이드에서 직접 불러오기
fetchSearchResults의 값(검색결과)이 굳이 서버측에서 불러올 필요가 없다면 컴포넌트 내부에서 useRouter훅을 통해 query stirng만 가져오고, 컴포넌트가 페이지에 마운트 되었을 때 쿼리값을 가지고 컴포넌트 내부에서 api를 호출하여 state에 보관하는 방법이 있다.

import { fetchSearchResults } from "@/api";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";

export default function Search() {
  const router = useRouter();
  const { q } = router.query;
  const [countries, setCountries] = useState([]);

  const setData = async () => {
    const data = await fetchSearchResults(q); // 👈🏻검색결과 불러오기
    setCountries(data);
  };

  useEffect(() => {
    if (q) {
      setData();
    }
  }, [q]);

  return (
    <div>
      {countries.map((country) => {
        return <div key={country.code}>{country.commonName}</div>;
      })}
    </div>
  );
}
profile
쉬운게 좋은 FE개발자😺
post-custom-banner

0개의 댓글