본문 바로가기

JavaScript/Next.js

Pages 번역

Next.js에서 페이지는 pages 디렉토리 안에 있는 .js, .jsx, .ts, or .tsx 파일에 있는 export된 React 컴포넌트이다. 각 페이지는 파일의 이름에 따른 route로 연결된다.

 

예시: 아래와 같은 React 컴포넌트를 export하는 pages/about.js를 생성하면 /about 에서 접근이 가능할 것이다.

function About() {
  return <div>About</div>
}

export default About

동적 경로

Next.js는 다이나믹(동적) 라우트를 지원한다. 예를 들어 pages/posts/[id].js란 파일을 생성하면 post/1, post/2 같은 경로로 접근이 가능하다.

 

사전 렌더링

기본적으로 Next.js는 모든 페이지를 먼저 렌더링한다. 먼저 렌더링 한다는 것은 Next.js가 클라이언트 사이드의 JavasScript에 의해 렌더링 되기 전에 각 페이지의 HTML을 먼저 생성한다는 의미이다. Pre-rendering(사전 렌더링)은 더 나은 성능과 SEO(Search Engine Optimization)를 가져올 수 있다.

생성된 각 HTML은 각 페이지에 필요한 최소한의 JS코드와 연결된다. 페이지가 브라우저에 의해서 로드되면 페이지의 스크립트 코드가 동작하면서 그 페이지가 인터랙티브 하게 만들어준다. (이 과정을 하이드레이션이라고 부름)

 

두가지 형태의 사전 렌더링

Next.js는 사전 렌더링의 두가지 형태가 있다: 정적 생성, 서버 사이드 렌더링. 차이점은 언제 페이지를 위한 HTML를 생성하는가에 있다.

  • 정적 생성(추천): 빌드 시간에 HTML이 생성되고 각 요청마다 재사용이 된다.
  • 서버 사이드 렌더링: 각 요청마다 HTML이 생성된다.

중요하게도 Next.js는 각 페이지마다 당신이 원하는 사전 렌더링 형태를 선택할 수 있게 되어 있다. 대부분의 페이지를 정적인 생성을 사용하고 나머지에 서버 사이드 렌더링을 사용하는 하이브리드 Next.js 앱을 생성할 수도 있다.

 

우리는 성능상의 이유로 서버 사이드 렌더링보다는 정적 생성을 사용하기를 추천한다. 정적으로 생성되는 페이지는 성능을 높이기 위해서 추가적인 설정 없이도 CDN에 의해서 캐싱된다. 하지만 어떤 경우에는 서버 사이드 렌더링을 사용할 수밖에 없을 수도 있다.

 

클라이언트 사이드 렌더링도 정적 생성이나 서버 사이드 렌더링과 함께 사용할 수 있다. 이 말의 의미는 페이지의 어떤 부분은 클라이언트 사이드 자바스크립트에 의해서만 렌더할 수도 있다.

 

정적 생성(추천)

페이지가 정적 생성을 사용한다면 해당 페이지의 HTML은 빌드 타임에 생성된다. 이 말의 의미는 운영 시에(?) 해당 페이지의 HTML는 next build를 동작시킬 때에 생성된다. 이 HTML은 매 요청마다 재 사용될 것이다. 이것은 CDN에 의해서 캐싱된다.

Next.js에서 개발자는 페이지를 데이터를 포함할 수도, 포함하지 않을 수도 있다. 각 케이스에 대해 살펴보자.

 

데이터 없이 정적 생성하기

기본적으로 Next.js는 데이터를 가지고 오지 않는 페이지를 정적 생성으로 사전 렌더링 한다.

function About() {
  return <div>About</div>
}

export default About

이 페이지는 어떠한 외부의 데이터로 가져올 필요가 없이 사전 렌더링이 되는 것을 유의하였을 것이다. 이러한 경우에는 Next.js는 빌드 시간에 페이지 당 한 개의 HTML 파일을 생성한다.

 

데이터 있는 정적 생성하기

일부 페이지는 사전 렌더링을 위해 외부 데이터를 가져와야 합니다. 두 가지 시나리오가 있으며 하나 또는 둘 다 적용될 수 있습니다. 각각의 경우에 Next.js가 제공하는 다음 기능을 사용할 수 있습니다.

 

1. 페이지 콘텐츠가 외부 데이터에 따라 다를때, getStaticProps를 사용하세요.

2. 페이지 경로가 외부 데이터에 따라 다를때,  getStaticPaths(일반적으로 getStaticProps와 함께)를 사용하십시오.

 

시나리오 1: 페이지 콘텐츠가 외부 데이터에 의존해있음

예시: 당신의 블로그 페이지가 CMS로부터 블로그 포스트의 리스트를 가져올 필요가 있는 경우.

// TODO: Need to fetch `posts` (by calling some API endpoint)
//       before this page can be pre-rendered.
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

export default Blog

사전 렌더 시에 이 데이터를 가져오기 위해 Next.js는 당신이 동일한 파일 내에 getStaticProps라는 async(비동기) 함수를 export하는 것을 허용한다. 이 함수는 빌드 시간에 호출되어 불러온 데이터를 사전 렌더시에 페이지의 props으로 전달한다.

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

getStaticProps가 어떻게 동작하는지 더 알아보기 위해선 Data Fetching 문서를 확인하라.

 

시나리오 2: 페이지 경로가 외부 데이터에 의존해있음

Next.js는 당신이 페이지를 동적인 라우트로 생성할 수 있게 허용한다. 예를 들어 한 개의 블로그 포스트를 id기반으로 보여주기 위해서 pages/posts/[id].js라는 파일을 생성할 수 있다. 이렇게 하면 id가 1인 블로그 포스트를 posts/1로 접근하여 볼 수 있게 해준다.

 

그러나 어떤 id를 빌드 시간에 사전 렌더하고 싶은지는 외부 데이터에 달려있다.

 

예시: 당신이 id가 1인 블로그 포스트를 데이터 베이스에 추가했다. 이 경우에 당신은 빌드 시간에 posts/1만 사전 렌더 하고 싶을 것이다.

그 후에 id가 2인 두 번째 포스트를 추가했다. 그럼 posts/2도 사전 랜더하고 싶을 것이다.

그렇기 때문에 당신의 사전 렌더된 페이지 경로는 외부 데이터에 달려있다. 이것을 핸들링 하기 위해서 Next.js는 동적인 페이지를 위해서 getStaticPaths라는 비동기 함수를 동적인 페이지에서 export할 수 있게 한다. 이 함수는 빌드 시간에 호출되고 어떤 경로들을 사전 렌더하기 원하는지 특정 지을 수 있다.

// 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 }
}

또한 pages/posts/[id].js파일에서 포스트에 대한 데이터를 해당 id로 가져오고 사전 렌더에 사용하기 위해서는 getStaticProps 함수를 export해야한다.

*fallback: false라는 옵션은 허용된 경로 외에 다른 경로는 404를 던지는 것.

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

getStaticPaths가 어떻게 동작하는지 더 자세한 내용은 Data Fetching 문서 참조

 

언제 정적 생성을 사용해야 할까?

데이터를 포함하던 그렇지 않던 가능한한 정적 생성을 사용하기를 추천한다. 당신의 페이지들이 한번에 생성되고 CDN에 의해 제공되기 때문에 페이지들이 요청 때마다 서버에서 렌더하는 것보다 훨씬 더 빠르다.

 

"사용자가 요청하기 전에 이 페이지를 사전 렌더해도 되는가"하고 먼저 물어 볼 수 있다. 만약 그렇다면 정적인 생성을 선택하는 것이 좋다.

 

반면에 사용자의 요청 전에 페이지를 사전 렌더할 수 없다면 정적 생성은 좋은 아이디어가 아니다. 아마도 자주 업데이트되는 데이터를 보여주는 페이지 이거나 매 요청 마다 변경되는 경우라면...

 

이와 같은 경우 다음 중 하나를 수행할 수 있습니다.

  • 정적 생성을 클라이언트 사이드 렌더링과 함께 사용하기: 페이지의 어떤 부분을 사전 렌더링하는 과정을 생략하고 클라이언트 사이드 자바스크립트로 데이터를 추가할 수 있다. 이 방식에 대해서 더 알기 원한다면 Data Fetching 문서를 참고하라.
  • 서버 사이드 렌더링 사용하기: Next.js는 페이지 요청마다 사전 렌더한다. 이 방식은 CDN에 의해 캐싱이 되지 않기 때문에 더 느릴 수 있다. 그렇지만 사전 렌더된 페이지는 항상 최신의 상태일 것이다. 이 방식에 대해서는 아래에서 살펴볼 것이다.

서버 사이드 렌더링

페이지가 서버 사이드 렌더링을 사용한다면 HTML페이지는 요청때마다 생성될 것이다.

페이지에 서버 사이드 렌더링을 사용하기 위해서는 getServerSideProps라는 비동기 함수를 export해야한다. 이 함수는 서버에 의해서 매 요청 마다 호출될 것이다.

예를 들어 가령 당신의 페이지가 자주 업데이트 되는 데이터(외부 API에서 가져오는)에 의해 사전 렌더될 필요가 있다면 이 데이터들을 가져와서 전달하기 위해 getServerSideProps를 아래와 같이 사용할 수 있다.

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

코드에서 볼 수 있듯이 getServerSideProp는 getStaticProps와 비슷하지만 차이점이라면 getServerSideProps는 빌드 시간때만이 아니라 매 요청마다 호출된다는 것이다.

getServerSideProps이 어떻게 동작하는지는 Data Fetching 문서 참조

 

요약

  • 정적 생성(추천): HTML이 빌드 시간에 생성되고 매 요청마다 재사용된다. 페이지를 정적 생성을 사용하여 만드려면 페이지 컴포넌트를 export하거나 getStaticProps를 (그리고 필요하다면 getStaticPaths도) export한다. 사용자의 요청 전에 사전 렌더되어도 되는 페이지들에 좋다. 정적 생성을 추가적인 데이터를 가져오기 위해 클라이언트 사이트 렌더링과 같이 사용할 수도 있다.
  • 서버 사이드 렌더링: HTML이 매 요청마다 생성된다. 페이지에서 서버 사이드 렌더링을 하려면 getServerSideProps export한다. 서버 사이드 렌더링은 정적 생성보다 더 낮은 성능을 보이기 때문에 반드시 필요한 경우에만 사용하라.

'JavaScript > Next.js' 카테고리의 다른 글

next-redux-wrapper가 필요한 이유  (0) 2021.12.01
Data Fetching 번역  (0) 2021.11.19
내가 정리하는 Next.js  (0) 2021.11.06
Next.js Scripts  (0) 2021.11.03
Next.js 에서 https 사용법  (0) 2021.11.03