Next.js - Data Fetching

박춘화·2022년 1월 18일
0

NEXT.js

목록 보기
2/2
post-thumbnail

Next.js에서 데이터 요청은 애플리케이션 사용 방식에 따라 다른 방식으로 콘텐츠를 렌더링한다. 이는 사전 렌더링에 속하는 Static Generation 또는 Server-side Rendering 뿐만 아니라 런타임 시에 콘텐츠를 생성 및 변경하는 Incremental Static Regeneration을 포함한다.

getServerSideProps

페이지에서 getServerSideProps(Server-side Rendering)을 export하면 Next.js는 매 요청마다 페이지 사전 렌더링 시에 getServerSideProps가 반환하는 데이터를 활용한다.

getServerSideProps는 서버 사이드에서만 실행되며, 브라우저 상에서는 실행되지 않는다. 페이지에 getServerSideProps 함수가 정의되어 있다면

  • 페이지를 직접 요청할 때 getServerSideProps는 요청 시마다 실행되며, 페이지는 반환된 props를 활용하여 사전 렌더링된다.
  • 페이지를 next/link 또는 next/router를 통해 클라이언트 사이드에서 페이지 변경을 통해 요청할 때 Next.js는 getServerSideProps를 실행하는 API 요청을 서버에 보낸다.

getServerSideProps페이지에서만 export되며, 페이지 파일이 아닌 파일에서 export될 수 없다. 또한, getServerSideProps는 단일 함수로 export되야하며 페이지 컴포넌트의 프로퍼티로 getServerSideProps를 넘겨주면 작동하지 않는다.

자주 변경되는 데이터의 경우에는 클라이언트 사이드에서 데이터 요청을 보내는 것이 좋다. 이런 경우에 데이터는 유저 특정적이며 검색 엔진 최적화에 큰 영향을 주지 않는다.

getStaticPaths

getStaticProps를 사용하면서 동적 라우트를 사용하는 페이지가 있다면 생성될 경로의 리스트를 정의해줘야한다.

동적 라우트를 사용하는 페이지에서 getStaticPaths(Static Site Generation) 함수를 호출하면 Next.js는 경로 패턴에 일치하는 모든 경로에 대해 사전 렌더링한다.

getStaticPathsgetStaticProps와 함께 사용되어야하며, getServerSideProps와 함께 사용될 수 없다.

getStaticPaths를 사용하는 경우는 다음과 같다.

  • headless CMS로부터 불러온 데이터
  • 데이터베이스에서 불러온 데이터
  • 파일 시스템에서 불러온 데이터
  • 일반적으로 (유저 특정적이지 않은) 캐싱될 수 있는 데이터
  • 페이지가 사전 렌더링과 빠른 속도가 필요한 경우 - getStaticProps는 HTML와 JSON 파일을 생성하는데, 이 둘은 CDN에 캐싱되어 성능을 향상시킨다.

getStaticPaths는 빌드 시점에 서버 사이드에서 실행된다. 만약 Incremental Static Regeneration을 사용한다면 getStaticPaths는 백그라운드 요청 시에도 서버 사이드에서 실행된다.

getStaticPaths페이지에서만 export되며, 페이지 파일이 아닌 파일에서 export될 수 없다. 또한, getStaticPaths는 단일 함수로 export되야하며 페이지 컴포넌트의 프로퍼티로 getStaticPaths를 넘겨주면 작동하지 않는다.

getStaticProps

페이지에서 getServerSideProps(Server-side Rendering)을 export하면 Next.js는 매 요청마다 페이지 사전 렌더링 시에 getServerSideProps가 반환하는 데이터를 활용한다.

getStaticProps를 사용하는 경우는 다음과 같다.

  • 사용자의 요청 전에 빌드 시점에서 페이지에 렌더링 되어야할 데이터
  • headless CMS에서 불러온 데이터
  • 일반적으로 (유저 특정적이지 않은) 캐싱될 수 있는 데이터
  • 페이지가 사전 렌더링과 빠른 속도가 필요한 경우 - getStaticProps는 HTML와 JSON 파일을 생성하는데, 이 둘은 CDN에 캐싱되어 성능을 향상시킨다.

Incremental Static Regeneration와 함께 사용한다면 이전 페이지가 재평가되는 동안 getStaticProps는 백그라운드에서 실행되고 새로운 페이지가 브라우저에 보여진다. 이는 getStaticProps가 빌드 시점에서 실행되어 정적 HTML 파일을 생성하기 때문에 들어온 요청에 대한 접근을 제한한다. 만약 페이지의 요청에 접근해야한다면 getStaticProps에서 사용되는 미들웨어 사용을 고려해야한다.

getStaticProps를 사용하여 빌드 시점에서 사전 렌더링이 이루어질 때 HTML 파일 뿐만 아니라 getStaticProps의 반환값을 저장하기 위한 JSON 파일도 생성된다. 이 JSON 파일은 클라이언트 사이드에서 next/link 또는 next/router에 의한 경로 변경이 일어날 때 페이지 컴포넌트에 props를 넘겨준다.

getStaticProps페이지에서만 export되며, 페이지 파일이 아닌 파일에서 export될 수 없다. 이러한 제약의 이유는 리액트로 페이지를 렌더링할 때 필요한 모든 데이터가 제공되어야하기 때문이다. 또한, getStaticProps는 단일 함수로 export되야하며 페이지 컴포넌트의 프로퍼티로 getStaticProps를 넘겨주면 작동하지 않는다.

Incremental Static Regeneration

Next.js는 빌드 후에도 정적 페이지의 생성 및 변경을 허용한다. Icremental Static Regeneration(ISR)은 전체 사이트에 대한 재빌드 과정이 없어도 매 페이지 기반으로 Static Generation을 사용할 수 있게 해준다. ISR을 사용하기 위해서는 getStaticProps에 props로 revalidate를 넘겨주면 된다.

export async function getStaticProps() {
  const res = await fetch(URL);
  const posts = await res.json();

  return {
    props: {
      posts,
    },
    // Next.js는 요청이 들어올 때 10초 간격으로 페이지를 재생성한다.
    revalidate: 10,
  };
}

요청이 들어오면 초기에는 빌드 시점에 생성된 캐싱된 페이지를 보여준다.

  • 초기 요청이 들어온 시점부터 10초 이내의 어떤 요청이든 캐싱된 페이지를 보여준다.
  • 10초가 되었을 때 들어온 요청에 대해서도 캐싱된 페이지를 보여준다.
  • 이 시점에서 Next.js는 백그라운드로 페이지를 재생성한다.
  • 페이지 재생성에 성공했으면 Next.js는 캐싱된 데이터를 무효화하고 재생성된 새 페이지를 보여준다. 만약 백그라운드에서 페이지 재생성에 실패했다면 페이지는 교체되지 않는다.

생성하지 않았던 경로에 대한 페이지에 대한 요청이 들어오면 Next.js는 첫 요청에 대한 응답으로 서버 사이드에서 렌더링한 페이지를 응답하고, 이후의 요청에 대해서는 캐싱된 페이지로 응답한다.

클라이언트 사이드에서 데이터 요청

클라이언트 사이드에서 데이터를 요청할 때 유용한 경우는 SEO 인덱싱을 요구하지 않는 페이지, 데이터를 사전 렌더링할 필요가 없는 페이지, 콘텐츠가 자주 변경되는 페이지 등이 있다. 클라이언트 사이드에서는 컴포넌트 수준에서 데이터 요청이 가능하다.

클라이언트 사이드에서 데이터 요청을 할 때는 애플리케이션의 성능과 페이지 로딩 속도에 영향을 준다는 점을 명심해야한다. 이는 데이터 요청이 컴포넌트 및 페이지가 mount될 때 이루어지며, 데이터가 캐싱되지 않기 때문이다.

리액트에서 일반적으로 데이터 요청에 대한 코드를 useEffect Hook 내에서 처리한다. 이외에 Next.js에서 제공하는 리액트 Hook 라이브러리인 SWR을 사용할 수도 있다. 이는 매우 권장되는 방법으로 클라이언트 사이드에서 데이터를 요청할 때 캐싱, 재평가, 포커스 트랙킹, 주기적인 재요청 등의 기능들을 사용할 수 있다.

import useSWR from "swr";

const fetcher = (...args) => fetch(...args).then((res) => res.json());

function Profile() {
  const { data, error } = useSWR("/api/profile-data", fetcher);

  if (error) return <div>Failed to load</div>;
  if (!data) return <div>Loading...</div>;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.bio}</p>
    </div>
  );
}

출처

profile
함께 성장하는 개발자

0개의 댓글