[Next.js] Static Generation vs. Server-side Rendering

js43o·2023년 1월 6일
0
post-thumbnail

1) Next.js란?

Next.js는 빠른 웹 애플리케이션 제작을 위한 여러 유용한 기능들을 제공하는 리액트 프레임워크이다.
대표적인 기능으로는 프리 렌더링, 동적 라우팅, 자동 코드 스플리팅, 이미지 최적화, API 라우트 등이 있다.

이 중 프리 렌더링(Pre-rendering)은 단어 그대로 페이지를 미리 렌더링 해놓은 후 사용자에게 제공하는 것으로, Next.js에서는 Static GenerationServer-side Rendering(SSR) 두 가지 형태가 존재한다.

2) Pre-rendering vs. No Pre-rendering

프리 렌더링 방식과 비 프리 렌더링 방식을 비교해보자.

기존의 Client Side Rendering(CSR) 방식(= NO Pre-rendering)은 서버가 클라이언트에게 빈 HTML 파일과 커다란 하나의 자바스크립트 파일을 함께 보내면, 클라이언트(브라우저)가 직접 자바스크립트를 실행하여 페이지를 렌더링하도록 하는 방식이다.

CSR 방식은 서버에 부담을 주지 않고 페이지가 나타나는 즉시 사용자와 상호작용이 가능하지만, 초기 페이지 로드 속도가 느리고 검색엔진 최적화(SEO)가 어렵다는 단점이 있다.

브라우저가 처음 서버와 통신을 마쳤을 때엔 실질적인 내용 대신 비어있는 HTML 껍데기만 존재한다.
자바스크립트를 실행시켜야 그 안의 내용물을 직접 화면에 그려 확인할 수 있는 것이다.
검색엔진은 기본적으로는 자바스크립트를 실행시킬 수 없으므로 그 내용을 알지 못한다. 즉, SEO가 제대로 되지 않을 가능성이 크다.

반면, Pre-rendering 방식은 서버에서 미리 자바스크립트 일부를 사용하여 HTML 페이지를 미리 렌더링한 후 클라이언트에게 보낸다. 이후 클라이언트가 자바스크립트를 실행시켜 페이지의 남은 부분을 마저 그리는(hydration) 식이다.

SSR 방식은 페이지 렌더링 시 서버의 자원이 사용되고 사용자 상호작용이 가능한 시점까지 약간의 지연이 발생하지만, 페이지 로드 자체가 더 빠르고 효과적인 SEO가 가능하다는 장점이 있다.

3) Static Generation

위에서 Next.js는 Pre-rendering을 두 가지 형태로 제공한다고 했다. 하나는 Static Generation(정적 생성) 방식이다.
정적 생성 방식은 HTML을 빌드 시간에 생성하며, 생성된 HTML을 여러 요청에 대해 재사용하도록 한다.
Next.js 공식 문서에서는, 사용자 요청 이전에 페이지를 프리 렌더링 해도 괜찮을 경우 정적 생성 방식을 이용하는 것을 권장한다. 빌드 시간에 이미 생성된 HTML을 그대로 응답하면 되므로 성능이 매우 뛰어나기 때문이다.

getStaticProps()

우리가 만든 페이지에서 따로 Data Fetching이 필요하지 않다면 해당 페이지는 정적 생성 방식을 그대로 따르게 된다.
하지만 만약 페이지를 만들기 위해 외부 데이터가 필요하다면(API 요청, DB 쿼리 등) 특별한 함수를 하나 정의해야 하는데, 그것이 바로 getStaticProps()이다.

이 함수 안에는 우리가 원하는 Data Fetching 코드를 작성하고, { props: ... } 형태의 객체를 리턴하도록 한다. 그러면 해당 함수가 빌드 시간에 실행되고 리턴한 객체를 페이지 컴포넌트의 props로 전달해주게 된다.

export default function Home(props) { ... }

export async function getStaticProps() {
  // Get external data from the file system, API, DB, etc.
  const data = await fetch(...)

  // The value of the `props` key will be
  //  passed to the `Home` component
  return {
    props: ...
  }
}
  • getStaticProps()는 서버 사이드 영역에서 실행되므로, 클라이언트(브라우저)가 관여할 수 없다. 즉, 자유롭고 안전한 외부 데이터 Fetching이 가능하다.
  • 빌드 시간에 호출되므로 네트워크 요청에 관한 정보는 알 수 없음.
  • 페이지 컴포넌트(/page/...)에서만 사용(export)이 가능하다.
  • 개발 모드(development)에서는 배포 모드(production)와 달리 개발 편의를 위해 매 요청마다 호출된다.
  • 동적 라우팅 페이지에서 사용할 경우, 반드시 밑의 getStaticPaths()와 함께 쓰여야 함.

getStaticPaths()

이 함수는 동적 라우팅 페이지 컴포넌트를 정의할 때, 미리 생성해놓고자 하는 특정 페이지 경로를 지정하기 위해 사용된다.

동적 라우팅 : 미리 지정한 경로가 아닌, 사용자의 요청에 의한 동적인 경로에 페이지를 라우팅하는 것.
ex) /pages/[id].js라는 파일을 통해 사용자가 /pages/1, /pages/me 등의 경로로 접근했을 때에도 대응할 수 있음.

{ params: { key: value } } 형태의 객체를 배열로 갖는 path와, boolean 변수인 fallback을 갖는 하나의 객체를 리턴하도록 한다.

  • 이때 value는 동적 라우팅 페이지의 경로 값, key는 동적 경로의 키 값.
    • ex) /page/[id].js에서 key == id
    • value는 직접 상수값을 지정해 줄 수도 있고, Data Fetching을 통해 가져온 임의의 값도 가능하다.
  • fallbackpath에 명시하지 않은 라우팅 경로로 접근했을 때의 동작 방식을 결정한다.
    • true: 지정되지 않은 경로에 대해 처음엔 'fallback page'를 보여줬다가, 이후 동적으로 새로운 페이지를 생성해서 응답하도록 함.
    • false: 지정되지 않은 경로에 대해 '404 page'를 응답함.
// pages/posts/[id].js

export async function getStaticPaths() {
  return {
    paths: [{ params: { id: 'notice' } }, { params: { id: 'help' } }],
    fallback: false, // can also be true or 'blocking'
  }
}
// `/posts/notice` 및 `/posts/help` 페이지가 빌드 시 미리 생성되도록 함
// `posts/1`, `posts/another` 등의 경로는 여전히 동적으로 생성됨
  • getStaticProps()와 반드시 함께 쓰여야 한다.
  • 역시 페이지 컴포넌트에서만 사용(export)이 가능하다.

4) Server-side Rendering

두 번째 Pre-rendering 형태는 서버 사이드 렌더링(SSR) 방식이다. 정적 생성 방식과 달리 HTML을 빌드 시간이 아닌 매 요청마다 생성하여 응답하는 것이 특징이다.

만약 페이지가 사용자 요청 이전에 미리 렌더링될 수 없다면, 또는 페이지가 요청에 의해 자주 변한다면 정적 생성 대신 SSR을 이용할 것을 권장하고 있다. 매 요청마다 페이지가 생성되므로 정적 생성 방식보다는 느리지만 항상 최신 상태를 유지할 수 있다.

getServerSideProps()

SSR로 페이지를 렌더링하기 전 데이터 Fetching이 필요한 경우 해당 함수를 이용한다. 정적 생성 방식의 getStaticProps()와 거의 비슷한 역할을 하지만, 빌드 시간이 아닌 매 요청마다 호출되므로 페이지의 데이터를 동적으로 관리할 수 있다.

// This gets called on every request
export async function getServerSideProps(context) {
  // 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 } }
}
  • 네트워크 요청에 관한 정보를 context 값을 통해 알 수 있다. (= {req, res})

5) Static Generation vs. Server-side Rendering

그래서, 정확히 어떤 곳에 정적 생성 방식을 쓰고 어떤 곳에 SSR을 적용해야 하는 것일까?

두 Pre-rendering 형태의 가장 큰 차이는 페이지가 렌더링되는 시점이다.

  • Static Generation: 프로젝트가 처음 빌드될 때 페이지를 렌더링하며, 이후 사용자가 요청할 때마다 기존에 만들어진 페이지를 응답함.
  • Server-side Rendering: 사용자가 요청을 보낼 때마다 매번 페이지를 새로 렌더링하여 응답함.

따라서, 이렇게 생각하면 될 것 같다.

  • 외부 데이터를 불러올 필요가 없거나, 필요하더라도 해당 데이터가 변하지 않는 형태임.
    즉, 최초 한 번만 불러오면 이후에 계속 업데이트 해줄 필요가 없음.
    ex) 마케팅 페이지, 정적 블로그 포스트, 도움말 및 문서(Docs) 등
    => Static Generation
  • 외부 데이터를 계속해서 사용하며 해당 데이터가 사용자 요청에 따라 변동될 가능성이 존재함.
    즉, 속도를 조금 타협하더라도 페이지가 항상 최신 상태의 데이터를 사용하도록 해야 함.
    ex) 공용 게시판 등
    => Server-side Rendering

만약 최신 상태를 유지해야 하면서도 SEO가 크게 중요하지 않은 페이지라면 (사용자 대시보드 등) 기존의 리액트에서와 같이 CSR 방식을 적용해도 된다.

Next.js 공식 페이지

profile
공부용 블로그

0개의 댓글