본문 바로가기
React/Next.js

[Next.js] Pre-Rendering

by 박헹구 2022. 1. 12.
반응형

 

💡Pre-Rendering이란?

Next.js에서는 기본적으로 모든 페이지를 미리 렌더링 한다.  

Pre-rendering이야 말로 Next.js에서 가장 중요한 개념이라고 할 수 있는데 Next.js는 클라이언트에서 JavaScript로 

모든 작업을 수행하는 대신 각 페이지에 대해 미리 HTML을 생성할 수 있다. 

 

두 가지 형태의 사전 렌더링

Next.js에서는 Static Generation(정적 생성)과  Sever-side Rendering(SSR)의 형태로 두 가지 방법의 사전 렌더링 방식이 존재한다.  

🎈 Static Generation(정적 생성)

Static Generation은 Next.js에서도 성능상의 이유로 SSR보다 권장하고 있는 방식이고, 또한 많은 사람들이 사용하는

방식이다. 정적으로 생성된 페이지는 성능 향상을 위한 추가 구성 없이 CDN에서 캐시가 가능하다. 

Static Generation을 사용하는 경우 빌드할 때 HTML 문서를 만들어 둔다.

사용자가 요청을 하기전에 페이지를 미리 렌더링이 가능 할 경우에 사용하는 것이 좋다.  즉 자주 업데이트 되는 데이터가 있는 경우에는 다른 경우의 수를 생각해야 한다.

npm run build

 

 

build한 HTML은 각 요청에서 재사용 할 수 있게 된다. \

 

■ 데이터를 사용하지 않는 Static Generation

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

export default About

■ 데이터를 사용한 Static Generation

// 페이지 콘텐츠가 외부 데이터에 의존할 경우.

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
    }

export async function getStaticProps() {
  const res = await fetch('API주소')
  const posts = await res.json()


  return {
    props: {
      posts,
    },
  }
}

export default Blog
//페이지의 경로에 동적경로 ex)[id].js 가 있는 페이지를 만들 경우 
function Post({ post }) {
  // Render post...
}
export async function getStaticPaths() {
 
  const res = await fetch('API주소')
  const posts = await res.json()


  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
  
  return { paths, fallback: false }
}

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, 동적렌더링)

사용자의 요청을 받았을 때마다 HTML 문서를 새로 생성해서 보내주는 방식이다.

페이지의 내용이 매번 바뀌거나 자주 업데이트가 되는 경우 사용하자.

물론 CSR을 이용하는 것도 방법이다.

nextjs.org/learn/basics/data-fetching/request-time

 

공식 매뉴얼에서는 두 가지를 선택하는 팁으로 다음을 생각해보라고 권한다.

"사용자의 요청이 오기 전에 페이지를 미리 렌더링 해도 괜찮은가? 아닌가?"

function HomePage(props) {
  const {products} = props;

  return (
    <ul>
      {products.map((product) => 
        <li key={product.id}>{product.title}</li>)}
    </ul>
  );
}

//클라이언트측에서 실행되지 않음.
export async function getStaticProps() {
  return {props:{
    products: [{id: 'p1', title: 'Product 1'}], //객체여야함.
    }};
}

export default HomePage;

import path from "path";
import fs from "fs/promises"; //클라이언트에서 사용하는게 아님


function HomePage(props) {
  const {products} = props;

  return (
    <ul>
      {products.map((product) =>
        <li key={product.id}>{product.title}</li>)}
    </ul>
  );
}

//클라이언트측에서 실행되지 않음.
//getStaticProps로 미리 사전 렌더링됨
export async function getStaticProps() {
  const filePath = path.join(process.cwd(), 'data', 'dummy-backend.json');
  //cwd 현재 작업 디렉토리
  const jsonData = await fs.readFile(filePath);
  const data = JSON.parse(jsonData);
  return {props:{
    products: data.products
    }};
}

export default HomePage;

 

 

 

Incremental Static Generation

 

페이지를 미리 생성해놓고 설정해놓은 시간마다 들어오는 모든 요청에 대해 마지막으로 다시 생성된 이후로 기존 페이지를 방문자에게 제공한다.

그리고 설정해놓은 시간이 지나면 새 페이지가 미리 생성되기 때문에 따라서 서버에서 지속적인 사전 렌더링을 수행할 수 있게 된다.

 

미리 렌더링 된 페이지가 배포 후에도 여전히 업데이트 됨.

 

pre generate page

Re generate it on every request, at most every X seconds

 

serve "old" page if re-generation is not needed yet

 

generate, store and serve "new" page otherwise

 

 

import path from "path";
import fs from "fs/promises"; //클라이언트에서 사용하는게 아님


function HomePage(props) {
  const {products} = props;

  return (
    <ul>
      {products.map((product) =>
        <li key={product.id}>{product.title}</li>)}
    </ul>
  );
}

//클라이언트측에서 실행되지 않음.
export async function getStaticProps() {
  console.log('(Re-)Generating...');
  const filePath = path.join(process.cwd(), 'data', 'dummy-backend.json');
  //cwd 현재 작업 디렉토리
  const jsonData = await fs.readFile(filePath);
  const data = JSON.parse(jsonData);
  return {props:{
    products: data.products
    },
    revalidate: 10 //재생성 시간 설정
    //동적인 페이지인 경우 시간설정을 짧게
    //덜 동적일 경우 시간설정을 길게
  };
}

export default HomePage;
import path from "path";
import fs from "fs/promises"; //클라이언트에서 사용하는게 아님


function HomePage(props) {
  const {products} = props;

  return (
    <ul>
      {products.map((product) =>
        <li key={product.id}>{product.title}</li>)}
    </ul>
  );
}

//클라이언트측에서 실행되지 않음.
export async function getStaticProps(context) {
  console.log('(Re-)Generating...');
  const filePath = path.join(process.cwd(), 'data', 'dummy-backend.json');
  //cwd 현재 작업 디렉토리
  const jsonData = await fs.readFile(filePath);
  const data = JSON.parse(jsonData);

  if (data.products.length === 0) {
    return{ notFound: true}
  }


  return {props:{
    products: data.products
    },
    revalidate: 10, //재생성 시간 설정
    //동적인 페이지인 경우 시간설정을 짧게
    //덜 동적일 경우 시간설정을 길게
    notFound: true,

  };
}

export default HomePage;
import path from "path";
import fs from "fs/promises"; //클라이언트에서 사용하는게 아님


function HomePage(props) {
  const {products} = props;

  return (
    <ul>
      {products.map((product) =>
        <li key={product.id}>{product.title}</li>)}
    </ul>
  );
}

//클라이언트측에서 실행되지 않음.
export async function getStaticProps(context) {
  console.log('(Re-)Generating...');
  const filePath = path.join(process.cwd(), 'data', 'dummy-backend.json');
  //cwd 현재 작업 디렉토리
  const jsonData = await fs.readFile(filePath);
  const data = JSON.parse(jsonData);

  // if (data.products.length === 0) {
  //   return{ notFound: true}
  // }
  if (!data) {
    return {
      redirect: {
        description: '/no-data'
      }
    }
  }


  return {props:{
    products: data.products
    },
    revalidate: 10, //재생성 시간 설정
    //동적인 페이지인 경우 시간설정을 짧게
    //덜 동적일 경우 시간설정을 길게
    notFound: true,

  };
}

export default HomePage;

 

[]동적 세그먼트가 있는경우는 미리 렌더링 하지 않는다. 

그런경우에 getStaticProps를 사용하면 오류가 나는데 

그런경우에는 getStaticPaths를 사용함

 

import path from "path";
import fs from "fs/promises"; //클라이언트에서 사용하는게 아님

function ProductDetailPage(props) {
  const {loadedProduct} = props;

  return(
    <>
      <h1>{loadedProduct.title}</h1>
      <p>{loadedProduct.description}</p>
    </>
  )
}

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

  const productId = params.pid;
  const filePath = path.join(process.cwd(), 'data', 'dummy-backend.json');
  //cwd 현재 작업 디렉토리
  const jsonData = await fs.readFile(filePath);
  const data = JSON.parse(jsonData);

  const product = data.products.find(product => product.id === productId);

  return{
    props: {
      loadedProduct: product
    }
  }
}

export async function getStaticPaths() {
  return {
    paths: [
      {params: {pid: 'p1'}},
      {params: {pid: 'p2'}},
      {params: {pid: 'p3'}},
    ],
    fallback: false,
  };
}

export default ProductDetailPage;

 fallback 미리 렌더링해야 하는 페이지가 많을 경우 사용하면 좋다. 

fallback을 true로 사용할 경우 자주쓰는 페이지만 미리 불러오고 

다른페이지는 부를때 요청한다. 

 

import path from "path";
import fs from "fs/promises"; //클라이언트에서 사용하는게 아님

function ProductDetailPage(props) {
  const {loadedProduct} = props;

  if(!loadedProduct){
    return <p>Loading...</p>
  }

  return(
    <>
      <h1>{loadedProduct.title}</h1>
      <p>{loadedProduct.description}</p>
    </>
  )
}

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

  const productId = params.pid;
  const filePath = path.join(process.cwd(), 'data', 'dummy-backend.json');
  //cwd 현재 작업 디렉토리
  const jsonData = await fs.readFile(filePath);
  const data = JSON.parse(jsonData);

  const product = data.products.find(product => product.id === productId);

  return{
    props: {
      loadedProduct: product
    }
  }
}

 or
 
 export async function getStaticPaths() {
  return {
    paths: [
      {params: {pid: 'p1'}},
    ],
    fallback: 'blocking',
  };
}

export async function getStaticPaths() {
  return {
    paths: [
      {params: {pid: 'p1'}},
    ],
    fallback: true,
  };
}

export default ProductDetailPage;

https://nextjs.org/docs/basic-features/data-fetching

 

Data Fetching: Data Fetching | Next.js

Data fetching in Next.js allows you to render your content in different ways, depending on your applications use case. These include pre-rendering with server-side rendering or static-site generation, and incremental static regeneration. Learn how to manag

nextjs.org

 

반응형

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

Next.js + TypeScript 사용하기 !  (0) 2022.02.10
[Next.js] Next.js에서 Head 설정하는 방법  (0) 2022.01.17
useSWR 사용하기.  (0) 2022.01.17
[Next.js] 파일 기반 라우팅  (0) 2022.01.07
[Next.js] Next.js 란?  (0) 2021.12.22

댓글