본문 바로가기

Study

[Next.js] Data Fetching APIs 비교 분석 (getStaticProps, getStaticPaths, getServersideProps, getInitialProps)

개요

생각해보면 현재 재직중인 회사에서 관리자 페이지를 제외한 프론트 서비스들은 모두 Next.js 를 사용하고 있는데

Next.js 프레임워크를 시간들여 공부해본 적이 따로 없는 것 같다. 🥲

핑계를 대보자면 React.js 만 사용할 줄 알아도 Next.js 으로도 그냥저냥 개발이 가능하기 때문이었다.

게을렀던 본인을 속죄하는 마음으로 Next.js 를 사용하며 의문이 들었던 부분들 위주로 하나씩 공부해나가보고자 한다.

data fetching 메서드들을 정리하는 것으로 첫 글을 시작해본다.

Pre-Rendering

Next.js 는 페이지 요청시 자체적으로 pre-render 한 html 파일을 응답하고, 브라우저에서 hydration 위해 필요한 코드를 재호출하는 방식을 사용하고 있다. 이로써 빠른 초기 로드 속도와 SEO 를 제공한다.

이때 Next.js 에서는 pre-render를 위한 방법으로,

  • Static Generation
  • Server Side Rendering

두가지 방식을 제공한다.

공식 문서에서는 static generation 을 권장한다

두 방식은 한 프로젝트 내에서 혼용되어 사용될 수 있다.

때문에 페이지를 새로 추가하게 될때, 페이지 특성에 따라 어떤 방식으로 pre-render를 해야할지 선택할 수 있어야 한다.

Static Generation

SG 방식은 프로젝트 build 시에 data fetching 메서드를 호출하는 방식이다. 따라서 해당 메서드로 pre-render된 페이지는 정적이며, cdn 에 캐시될 수 있기에 더 빠른 퍼포먼스를 제공한다. 더 나은 퍼포먼스를 제공하기에 권장되는 방법이다. 

이때, 생성되는 정적 페이지에 외부 데이터들도 포함시킬 수 있는데 이는 client-side data fetching 통해 외부 데이터도 포함시킬 수 있다는 뜻이다.

  • 빌드시에 페이지를 생성. 미리 생성해둔 페이지를 호출하기에 더 빠른 퍼포먼스 제공
  • client-side data fetching 을 통해 외부 데이터들도 포함 가능

getStaticProps

getStaticProps는 pre-render될 컴포넌트에 props로 데이터를 전달하는 방식이다.

하지만 위 함수는 빌드 시점을 기준으로 호출된 함수이기 때문에 빌드 이후에도 빈번히 응답값이 바뀌는 api를 pre-fetch하기에는 부적합하다.

function Blog({ posts }) {
  // Render posts...
}

// This function gets called at build time
export async function getStaticProps() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts,
    },
  }
}

export default Blog

getStaticPaths

Next.js는 변수가 포함된 경로를 생성할 수 있다.

/posts/[id].js

getStaticPaths는 이러한 동적인 url에 포함되는 데이터를 결정해준다.

// This function gets called at build time
export async function getStaticPaths() {
  // Call an external API endpoint to get posts
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // Get the paths we want to pre-render based on posts
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // We'll pre-render only these paths at build time.
  // { fallback: false } means other routes should 404.
  return { paths, fallback: false }
}

getStaticPaths는 getStaticProps와 함께 쓰일 수 있으며 다음과 같이  getStaticPaths 에서 결정된 path를 getStaticProps에서 사용할 수 있다.

function Post({ post }) {
  // Render post...
}

export async function getStaticPaths() {
  // ...
}

// This also gets called at build time
export async function getStaticProps({ params }) {
  // params contains the post `id`.
  // If the route is like /posts/1, then params.id is 1
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  // Pass post data to the page via props
  return { props: { post } }
}

export default Post

Server-side Rendering

SSR 을 사용하는 페이지는 매 요청시마다 page HTML 이 생성된다. 그러기에 SSR 에서 data fetch의 결과가 빈번히 바뀌는 경우에 적합하다.

getServersideProps

사용 및 동작은 getStaticProps와 매우 유사하다. 다만 호출될 때가 빌드 시점인지, 페이지 요청 시점인지가 다를 뿐이다.

function Page({ data }) {
  // Render data...
}

// This gets called on every request
export async function getServerSideProps() {
  // Fetch data from external API
  const res = await fetch(`https://.../data`)
  const data = await res.json()

  // Pass data to the page via props
  return { props: { data } }
}

export default Page

getInitialProps (주의)

공식문서에서 더이상 권장하지 않는 방법이다. 다만 왜 권장하지 않고 어떤 차이가 있는지 알아둘 필요는 있다.

getServersideProps와 마찬가지로 페이지 요청때마다 함수가 호출된다.

function Page({ stars }) {
  return <div>Next stars: {stars}</div>
}

Page.getInitialProps = async (ctx) => {
  const res = await fetch('https://api.github.com/repos/vercel/next.js')
  const json = await res.json()
  return { stars: json.stargazers_count }
}

export default Page

다만 위 함수가 어떻게 호출되는지 살펴보자.

Page.getInitialProps = async (ctx) => { ... }

export 되는 getServersideProps 와 다르게 페이지 컴포넌트 내의 멤버 함수를 호출하는 형식이다.

Next.js는 pre-render 가 된 페이지를 먼저 브라우저에 응답하여 빠른 초기 로드를 제공하는데 이때 멤버 함수인 getInitialProps 코드도 함께 브라우저에 내려가게 된다. 단순히 코드가 함께 내려가는게 무슨 문제가 될 수 있느냐 할 수 있지만, getInitialProps에서 외부 모듈을  사용하고 있다면 외부 모듈도 함께 브라우저에 응답되기 때문에 응답 시간이 길어져 문제가 될 수 있는 것이다. 이러한 side-effect 때문에 Next.js 에서는 더이상 getIntialProps를 권장하지 않는다.

 

다만 getIntialProps의 내용이 브라우저로 함께 응답되기 때문에 이로 인한 이점도 있다. Next.js 에서  next/link, next/router 따위로 client-side navigation을 구현할 수 있다. 이때 getInitialProps는 이미 브라우저에 있는 코드이기에 별도로 server로 요청하지 않고 브라우저에서 data fetch를 할 수 있기에 서버 부담을 줄일 수 있다.

이 내용과 관련된 github discussion 이 있다. https://github.com/vercel/next.js/discussions/11211

정리

Pre-render on Next.js

  • 목적
    • 빠른 초기 로드 속도
    • SEO
  • 방식
    • sever-side data fetching
    • 브라우저에게 html pre-render request
    • 브라우저에서 hydration을 위한 코드 재요청

API

  • Static Generation
    • Data Fetch 시점: 빌드시
    • Method: getStaticProps, getStaticPaths
  • Sever-side Rendering
    • Data Fetch 시점: 페이지 요청시
    • Method: getServersideProps, getInitialProps

 

참고

https://nextjs.org/docs/basic-features/pages

https://nextjs.org/docs/api-reference/data-fetching/get-initial-props

'Study' 카테고리의 다른 글

OpenSource Contribute 후기  (2) 2023.11.20
웹 요청 중단 시키기 (ft. AbortController)  (0) 2022.08.15