Next JS Page Static Generation

Min Su Kwon·2021년 7월 3일
0
post-thumbnail

Next JS에서는 각 페이지에 대해서 pre-rendering을 위해 SSR(서버 사이드 렌더링) 또는 SG(정적 생성) 옵션을 제공한다. 각각 페이지 컴포넌트에서 getServerSideProps, getStaticProps/getStaticPaths 함수 export를 통해서 옵션을 정할 수 있으며, 여기서 페이지 생성을 위한 데이터 페칭 등의 작업을 진행할 수 있다.

여기서 정적 페이지 생성 옵션을 선택할 경우에는 페이지가 "앱 빌드"시(next build 커맨드 실행시)에 생성되고, 서버 사이드 렌더링 옵션을 선택할 경우에는 각 "클라이언트 요청에 맞춰서" 페이지가 생성된다.

물론 페이지의 일부분에 대해서만 pre-rendering을 적용하고, 일부분은 클라이언트 사이드에서 데이터 페칭 후 렌더링하는 등 유연하게 페이지를 구성하는 것도 가능하다.

우선은 Static Generation 쪽을 살펴보자.

Static Generation

조금 풀어서 말해보면, 정적 페이지 생성 옵션의 경우에 앱 빌드시 한번만 페이지가 빌드되고, 여러 요청에 대해서 빌드시에 만들어 놓은 똑같은 페이지를 계속해서 반환한다.

getStaticProps

간단한 예시 코드를 만들어봤다. 아래 페이지 컴포넌트는 Next JS 파일 시스템 라우팅 규칙에 따라서, /users 로 들어온 GET 요청을 처리한다.

// pages/users/index.js
import axios from 'axios'
import _ from 'lodash'

export default function UsersPage({ users }) {
  const userList = _.map(users, (user) => {
    return <li key={user.id}>{user.id} / {user.name} / {user.email}</li>
  })

  return (
    <ul>
      {userList}
    </ul>
  )
}

export async function getStaticProps(context) {
  const { data: users } = await axios.get('https://jsonplaceholder.typicode.com/users');

  return {
    props: {
      users
    }
  }
}

이 페이지를 정적으로 생성하도록 하기 위해서, getStaticProps 함수를 export 해준다. 이렇게하면, Next JS 앱 빌드를 할 때 각각 정적 페이지에 대해서

  1. getStaticProps 함수가 실행되고
  2. 반환된 데이터가 페이지 컴포넌트에게 넘겨지고
  3. 컴포넌트 렌더링 결과를 바탕으로 HTML 페이지를 만든다

실제로 next build 커맨드를 실행해보면,

next build 커맨드 실행 결과

친절하게 결과를 알려주는데, 위의 코드에서 정의한 /users 페이지가 SSG를 통해서 생성되었음을 알 수 있다. 실제로 빌드된 파일을 살펴보면 .next/server/pages 디렉토리에 users.html이 있는 것을 확인할 수 있고, 이 파일이 클라이언트 요청시에 반환되는 것이다.

getStaticPaths

만약 생성하고자 하는 페이지가 라우트 파라미터에 의존하는 경우(users/1, users/2), 페이지 컴포넌트를 [id].js와 같은 이름으로 지정해줘야한다. 이런 경우에 어떤 파라미터들에 대해서 정적 페이지를 생성할 것인지 판단할 수 없기 때문에, getStaticPaths 함수를 export 해줘야한다.

이 함수를 통해서 어떤 라우트 파라미터들에 대해서 페이지를 빌드타임에 생성할지 지정해줄 수 있다. 단, getStaticPaths 함수만 export 해서는 안되고, 마찬가지로 getStaticProps 함수 역시 export 해줘야한다.

각 함수의 역할은 위에서 언급한 것 처럼

  • getStaticPaths : 어떤 path들의 정적 페이지를 빌드할건지 반환
  • getStaticProps : 각각 path에 대해서 정적 페이지 빌드에 필요한 데이터 반환

라고 정리할 수 있다.

아래 코드 예시는 users/:userId 로 들어온 GET 요청을 처리하는 페이지 컴포넌트다.

// pages/users/[id].js
import axios from 'axios'
import _ from 'lodash';

export default function UserDetailPage({ user }) {
  return (
    <div>
      {user.id} / {user.name} / {user.email}
    </div>
  )
}

export async function getStaticPaths() {
  const { data: users } = await axios.get('https://jsonplaceholder.typicode.com/users');
  const paths = _.map(users, (user) => {
    return { params: {id: _.toString(user.id)}};
  });

  return {
    paths,
    fallback: false
  }
}

export async function getStaticProps(context) {
  const { id } = context.params;

  const { data: user } = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);

  return {
    props: {
      user
    }
  }
}

위 코드는 마찬가지로 Next 앱 빌드시에

  1. getStaticPaths를 통해 정적으로 생성할 페이지들의 path 반환
  2. getStaticProps를 통해 생성할 각 페이지들에 대해서 필요한 데이터 페칭 & 반환
  3. 컴포넌트에 값을 넘겨주고, 이를 바탕으로 html 문서 생성

하는 과정을 거치게 된다.

next build 커맨드를 실행해보면,

getStaticPaths에서 반환한 path들(1~10 id를 가진 users)을 대상으로 정적 페이지가 생성되었음을 알 수 있다. 실제로 파일들도 정상적으로 생성된 것을 확인할 수 있다.

어떨 때 사용해야 할까?

  • 페이지 렌더링에 필요한 데이터가 사용자의 요청에 앞서서, 빌드 시에 가용할 때
  • 데이터가 퍼블릭하게 캐싱될 수 있을 때(유저 specific한 데이터가 아닌)
  • 페이지가 프리렌더링 되어야하고(SEO를 위해) 매우 빨라야할 때
    • getStaticProps는 각각 페이지에 대해 HTML과 JSON파일을 만드는데, 둘 모두 CDN에 의해 캐싱될 수 있다 → 성능에 이점.

추가적으로 할 수 있는 것들

  • getStaticProps

    • 빌드 이후에도 일정 시간이 지나면 페이지를 다시 생성하도록 할지 지정
      → 반환 객체의 revalidate 옵션
    • 특정 조건에서 페이지가 404 status / 페이지를 반환하도록 설정(ex: 데이터 페칭 실패시)
      → 반환 객체의 notFound 옵션
  • getStaticPaths

    • paths로 반환되지 않은(즉, 빌드시에 정적으로 생성되지 않은) path의 페이지들에 대해서 어떤 응답을 보낼지 지정

      → 반환 객체의 fallback 옵션

      • 404 페이지로 가도록 하거나 (false)
      • 클라이언트 요청에 맞춰서 요청된 path에 맞는 페이지 생성(getStaticProps 이용), 이후 오는 이 path에 대한 요청들에 대해서 이때 생성한 페이지 반환

Next Steps

  • Static Generation 관련 옵션들을 좀 더 상세하게 살펴보고 예시 코드 작성
  • Serever Sidre Rendering 옵션을 이용해보고 예시 코드 작성

Reference

profile
이제 막 커리어를 시작한 소프트웨어 엔지니어입니다. 배운 것을 정리하면서 조금 더 깊이 이해하려는 습관을 들이려고 합니다. 피드백은 언제나 환영입니다.

0개의 댓글