• Next

[Next.js] Pre-rendering 개념 & Static Generation(getStaticProps)

man_on 2023. 1. 8. 16:29
반응형
 

 

 

 

 

 


 

 

1. Pre-rendering이 필요한 이유
2. Pre-rendering의 2가지 방식
3. Static Generation
4. Build
5. ISR 정적페이지 업데이트

 

 

 

 

Pre-rendering이 필요한 이유

 

 

 

SPA(Single Page Application) 인 React로 구현된 사이트는 하나의 파일로 전체 사이트(여러 페이지)를 구현한다.

즉, html 파일이 한개라는 뜻인데, 이 HTML을 JavaScript가 동적으로 만드는데 동적생성이 만들어지는 곳이

client라면 CSR(Client Side Rendering)이고 server 쪽이라면 SSR(Server Side Rendering) 이다.

 

React는 CRA(Create React App)으로 별도의 초기 설정 없이도 CRA를 통해 React 기반의 SPA를 쉽게 구현할 수 있게 되었는데,

이 CRA로 build한 프로젝트는 only *CSR로 실행되어서 빈 HTML파일에 모든 JS코드가 로드되어 실행된 후에 스크린에 내용이 표시된다. 단순한 페이지라면 문제가 없겠지만, 데이터를 서버로부터 로딩해야하는 페이지라면 시간이 소요될 수 있다.

이 때, 사용자는 페이지가 로딩되는 동안 빈 페이지만을 보면서 기다리게되며, SEO 측면에서도 좋지않은 문제점들이 발생했다.

 

Next는 Pre-rendering이라는 개념을 이용해서 이와 같은 문제를 해결한다.

 

 

 

로딩시간이 걸리는 페이지일 때, Next 사전에 준비된 필요한 데이터가 채워진 HTML 페이지를 pre-rendering한다.

사용자는 로딩시간 동안 빈 페이지가 아닌 Next가 pre-rendering한 페이지를 볼 수 있어서 사용자 경험을 좋게하며,

사전 렌더링된 페이지에는 주요 콘텐츠가 모두 포함되어 있는 상태이기 때문에,

검색 엔진 크롤러가 완성된 콘텐츠를 살펴보게되어서 SEO 측면에서도 좋다.

pre-rendering된 html파일이 보여지는 동안 Next.js는 JavaScript를 로드하여 완전한 interactive page가 되도록 만드는데

이를 Hydration이라 한다.

 

 

 

 

* Pre-rendering에는 2가지 방식이 있는데 Static GenerationSSR(Server-side Rendering)이 있다.

   하단에서 2가지를 상세히 설명한다.

 

* Pre-rendering은 오직 최초 로딩할 때만 영향을 미친다.

   페이지의 첫 번째 렌더링이 끝나고나면, 다시 표준 SPA로 돌아간다.

 

 

 

CSR (Client Side Rendering)

웹 페이지의 렌더링이 클라이언트(브라우저) 측에서 일어나는 것을 의미한다.
브라우저는 최초 요청에서 html - JavaScript - css 순서로 로드하는데,
이 때, 최초로 불러온 html파일은 빈 상태로 있게되고,  JS가 다운로드 완료되면 dom을 빈 html위에 그리기 시작한다.

장점 
웹서버 호출을 최소화 할 수 있다.
최초 호출 때만 html, css, js를 요청하고, 이후에는 화면에서 변화가 일어나는 만큼의 데이터만 요청한다. (AJAX/JSON)

 

 

 

 

 

 

 


 

 

 

 

Pre-rendering 2가지 방식

 

 

Next.js의 pre-rendering에는 두가지 종류가 있다. 이 둘의 차이는 언제 html을 만드는지에 있다.

 

 

 

  •   Server-side Rendering : html을 각 request가 일어날 때 생성한다.
  •   Static Generation : html을 build타임에 생성한다. 생성된 html은 각 request에 재사용된다.

 

가능한 static generation을 추천한다고 공식 next.js 홈페이지에 나와있다.

이유는, 페이지가 한번에 build될 수 있고, 각 요청에 따라 페이지를 렌더하는 속도가 빨라지기 때문이다.

  

유저의 요청보다 먼저 페이지가 렌더링되는 것이 좋지않은 경우 (자주 데이터를 업데이트, 매 요청마다 콘텐츠가 달라지는 경우)

이러한 경우에는 SSR을 사용하는 것이 좋다. 렌더시간은 좀 더 걸리겠지만, 페이지는 항상 업데이트 될 것이다.

 

 

 

 

 

 


 

 

 

 

 

Static Generation

 

 

 

Static Generation은 빌드하는 동안 페이지를 사전 생성한다.

사전 생성은 콘텐츠를 구성하는 모든 HTML 코드와 데이터를 사전에 준비시켜 놓는다는 의미이다.

빌드 전 페이지가 사전에 구축되었기 때문에, 페이지가 구축되고나면 CDN을 통해서 cache에 저장된다.

그 후에는 hydrate 작업을 거쳐 표준 react앱처럼 동작한다.

 

 

 

해당 페이지가 사전 생성되어야 하는 페이지임을 알리기 위해서는 아래의 코드를 pages폴더에서 원하는 component에 사용해야한다.

export async function getStaticProps() {
	return { props : ... }
}

 

특징

  1.  정확히 getStaticProps라고 지정해줘야 Next.js가 해당 함수를 찾을 수 있다.
    → getStaticProps 함수가 있으면 해당 함수 실행 후 component 함수가 실행된다.
  2.  항상 props를 포함한 객체를 반환한다.
  3.  promise를 반환하는 비동기 함수로 그 안에 await 키워드를 쓸 수 있다.
  4.  client-side에 제공되는 코드가 아니므로, 보통 server-side에서만 실행되는 코드들을 getStaticProps에서 실행할 수 있다.
    → 해당 함수 내에 포함하는 코드는 client에서는 볼 수 없으므로, 사용자가 볼 수 없는 credential을 쓸 수 있다.
  5. 정적인 페이지를 구축할 때 적절하다. (데이터가 자주 변경되지 않는 페이지)

 

 

 

 

 

 


 

 

[예제]

 

getStaticProps는 컴포넌트에 대한 props객체를 반환해주는 역할을 한다.

파일에 getStaticProps 함수가 있으면 Next.js에서 먼저 이 함수를 실행하고, 두 번째로 컴포넌트 함수를 실행한다.

 

 

 

우선 하드코딩으로 props객체를 getStaticProps함수에 넣고, Page 컴포넌트에서 props를 받아 반환해본다.

import React from 'react'

function Page(props) {
  const {product} = props
  return (
    <div>
      {product.map((product) => (
        <li key={product.id}>{product.title}</li>
      ))}
    </div>
  )
}

export default Page

export async function getStaticProps() {
  return {
    props: {
      product: [{id: 'p1', title: 'product1'}],
    },
  }
}

 

 

 

 

위의 코드로 실행이 되었다면, 하드코딩 된 데이터를 json파일로 data폴더에 저장해서 불러와본다.

import React from "react";
// node.js로부터 파일 시스템 모듈을 import한다. js는 파일 시스템에 접근할 수 없기 때문에
// client side에서는 fs 모듈 작업이 안된다.
// Next가 자동으로 getStaticProps 함수에서만 쓰이는 import를 확인하고,
// client side 코드 번들에서는(=react가 준비될때) fs import를 제거한다.
import fs from "fs";
//경로를 구축하는데 유용한 기능이 있는 모듈
import path from "path";

function Page(props) {
  const { product } = props;
  return (
    <div>
      {product.map((product) => (
        <li key={product.id}>{product.title}</li>
      ))}
    </div>
  );
}

export default Page;

export async function getStaticProps() {
  // Node.js에서 전역적으로 사용할 수 있는 process 객체
  // cwd 현재 작업 디렉토리를 의미 > 루트폴터를 의미 > 다음 data파일(무한으로 경로추가가능)
  const filePath = path.join(process.cwd(), "data", "dummy-backend");
  // readFileSync는 파일을 동기적으로 읽고 완료될 때까지 실행을 차단한다.
  // readFile은 계속하려면 callback해야하고, 읽어오려고 하는 파일의 경로만 있으면된다.
  // 비동기함수이므로 await를 붙여준다.
  const jsonData = await fs.readFile(filePath);
  // JSON.parse - json data를 파싱해서 js객체로 반환
  const data = JSON.parse(jsonData);
  return {
    props: {
      product: data.product,
    },
  };
}

 

 

 

해당 코드로 실행을 했을 때, 페이지 소스를 확인하면 처음부터 모든 데이터가 들어가 있는 것을 확인할 수 있다.

 

 

 

 


 

 

 

 

 

 

Build

 

 

Static Generation은 build 타임에 html을 생성하여 request가 발생할 때 준비된 html을 보여준다고 하였는데,

어떻게 동작하는지 확인해본다.

 

 

 

package.json을 확인해보면 scripts에서 build을 확인할 수 있다.

dev  개발 모드에서 Next.js를 시작
build  production mode를 위한 application의 build
start  production server를 시작

 

 

 

build를 실행하여 log를 확인해보면 여러가지 정보를 알 수 있다.

 

npm run build

정적페이지가 6개 생성

 

 

범례를 보면  ●은 getStaticProps를 사용하여 build시 사전 생성된 페이지를 나타내고

○은 데이터가 필요하지 않아서 getStaticProps을 사용하지 않고 사전 생성된 페이지를 나타낸다.

 

 

 

.next 폴더 - server - pages에서 사전 생성된 HTML파일을 확인할 수 있다.

이 HTML파일들이 검색 엔진 크롤러나 페이지의 방문자들에게 초기 요청 시 보여지게될 사전 생성된 페이지 이다.

 

 

 

 

 

 

 


 

 

 

 

 

 

ISR 정적 페이지 업데이트

 

Incremental Static Re-Generation

getStaticProps를 이용하여 pre-rendering을 하였는데,  자주 바뀌는 데이터라면 ISR이라는 Next.js의 내장기능을 활용한다.

ISR은 페이지를 빌드할 때 정적으로 한 번만 생성하는 것이 아니라, 배포 후에도 재배포 없이 계속 업데이트를 하게 해준다.

 

 페이지를 사전 생성을 하긴 하지만 최대 X초마다 들어오는 모든 요청에 대해 주어진 페이지를 Next.js가 재생성 하도록 할 수 있다.

아래의 코드처럼 return에 revalidate를 추가하면 페이지로 들어오는 모든 요청에 대해 마지막으로 재생성된지 10초 후 다시 재생성을 한다.

 

export async function getStaticProps() {
  console.log(Re-generating...)
  return {
    props: {data},
    // 10초마다 재생성
    revalidate : 10
  }
}

 

revalidate를 설정하고 다시 build를 해보면 build log에서 ISR 10초가 추가로 설정되있는 것을 확인할 수 있고,

 

npm start를 하여 페이지를 10초 후 새로고침해보면, getStaticProps가 재실행되면서 콘솔이 찍히는 것을 볼 수 있다.

 

 

 

 

 

 

 

 

 

반응형