728x90
반응형

ISR(Incremental Static Regeneration)

ISR(Incremental Static Regeneration) 을 사용하면 트래픽이 들어올 때 백그라운드에서 다시 렌더링해서

기존 페이지를 업데이트 할 수 있게 해준다. 아래와같이 코드를 작성해보자.

 

about.js

function About({posts}) {
  console.log(posts)
    return (
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.name}</li>
        ))}
      </ul>
    )
  }
  
export default About

export async function getStaticProps(context) {
    const res = await fetch(`https://jsonplaceholder.typicode.com/users`)
    const posts = await res.json()

  return {
    props: {
      posts,
    },
    // Next.js will attempt to re-generate the page:
    // - When a request comes in
    // - At most once every second
    revalidate: 1, // In seconds
  }
}

 

실행해보자.

 

 

 

이제 블로그의 포스트리스트는 1초마다 재생성된다. 만약 새로운 블로그 포스트가 추가되면 앱을 재빌드하거나 재배포없이 거의 곧바로 활용가능해진다.

 

이것은 또한 fallback:true 옵션과 완벽히 동작한다.

 

Static content at scale

전통적인 SSR과는 다르게 ISR은 정적특징의 장점을 보장해준다.

  • 지연시간이 급증하지않고, 페이지는 지속적으로 빠르게 제공된다.
  • 페이지가 오프라인이 되지 않으며 만약 재생성이 실패해도 예전 페이지는 변경되지 않고 유지된다.
  • db나 백엔드에 로드 부하가 작다. 페이지들이 동시에 한번 재계산되기 때문이다.

 

파일 읽기 : process.cwd()

파일들을 getStaticProps 에서 직접 파일시스템을 통해 읽혀 질 수 있다. 그러기 위해선 반드시 파일의 full path를 가지고 있어야 한다.

 

Next.js는 코드를 몇개의 디렉토리 속으로 나누어 컴파일한다. 따라서 __dirname을 사용하면 실제 페이지 디렉토리 경로와 다른 경로를 리턴하기 떄문에 사용할 수 없다.

 

대신 우리는 process.cwd()를 사용할 수 있다. 이것은 Next.js 실행이 일어나는 디렉토리 정보를 준다.

 

about.js

import { promises as fs } from 'fs'
import path from 'path'

function About({posts}) {
  console.log(posts)
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <h3>{post.filename}</h3>
          <p>{post.content}</p>
        </li>
      ))}
    </ul>
  )
  }
  
export default About

export async function getStaticProps(context) {
  const postsDirectory = path.join(process.cwd(), 'posts')
  const filenames = await fs.readdir(postsDirectory)

  console.log(postsDirectory)
  console.log(filenames)

  const posts = filenames.map(async (filename) => {
    const filePath = path.join(postsDirectory, filename)
    const fileContents = await fs.readFile(filePath, 'utf8')

    // Generally you would parse/transform the contents
    // For example you can transform markdown to HTML here

    return {
      filename,
      content: fileContents,
    }
  })
  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts: await Promise.all(posts),
    },
  }
}

 

이렇게 만들고 posts폴더를 만든 후 abc.txt 파일을 생성해보자.

 

 

abc.txt

abc

 

실행 해보면 아래 화면과 로그를 확인할 수 있다.

 

 

posts 폴더 하위에 있는 파일들의 이름과 내용을 찾고 About 컴포넌트로 넘겨주었다.

 

 

Write server-side code directly

getStaticProps는 서버사이드에서만 동작한다. 클라이언트 사이드에서는 동작하지 않는다. 또한 브라우저에서 확인 가능한 js bundle에 조차 포함되지 않는다. 이것이 의미하는 것은 direct database queries를 직접 사용해서 정보를 가져올 수 있다는 것이다. getStaticProps로부터 API route를 fetch하면 안된다. 

 

getStaticProps(정적 생성)는 빌드 시 데이터를 가져오고 그곳에서만 처리됩니다. 즉, 프론트엔드와 백엔드가 혼합되지 않습니다. getStaticProps에 데이터가 필요한 경우 데이터를 가져오기 위해 /api에 대한 추가 네트워크 호출을 발생시키지 않아야 합니다. 대신 해당 논리와 함께 함수를 사용하거나 getStaticProps에서 직접 함수를 사용해야 합니다. 프론트엔드에 데이터가 필요한 경우 rest/graphql/whatever/api에서 데이터를 가져오거나 getServerSideProps를 통해 전달할 수 있습니다.

 

아래 예제를 통해서 js bundle에서 무엇이 제거되는지 확인할 수 있다( 브라우저에서 )

// This app shows what Next.js bundles for the client-side with its new SSG
// support. This editor supports TypeScript syntax.
import Cookies from 'cookies';
import Mysql from 'mysql';
import Link from 'next/link';
import SQL from 'sql-template-strings';
import Layout from '../components/Layout';

const pool = Mysql.createPool(process.env.DATABASE_URL);

export default function ({ projects }) {
  return (
    <Layout>
      <h1>Projects</h1>
      <ul>
        {projects.map((project) => (
          <li key={project.id}>
            <Link href="/projects/[id]" as={`/projects/${project.id}`}>
              <a>{project.name}</a>
            </Link>
          </li>
        ))}
      </ul>
    </Layout>
  );
}

export async function getServerSideProps({ req, res }) {
  const userId = new Cookies(req, res).get('user_id');
  const projects = await new Promise((resolve, reject) =>
    pool.query(
      SQL`SELECT id, name FROM projects WHERE user_id = ${userId};`,
      (err, results) => (err ? reject(err) : resolve(results))
    )
  );
  return { props: { projects } };
}
// This is the code that is bundled for the client-side:

import Link from 'next/link';
import Layout from '../components/Layout';
export var __N_SSP = true;
export default function ({ projects }) {
  return (
    <Layout>
      <h1>Projects</h1>
      <ul>
        {projects.map((project) => (
          <li key={project.id}>
            <Link href="/projects/[id]" as={`/projects/${project.id}`}>
              <a>{project.name}</a>
            </Link>
          </li>
        ))}
      </ul>
    </Layout>
  );
}

 

 

Statically Generates both HTML and JSON

getStaticProps를 가지고 있는 페이지가 빌드 시 pre-render 될 때, Next.js는 HTML파일 뿐 아니라 getStaticProps의 실행 결과인 JSON을 생성한다.

 

이것은 클라이언트에서 getStaticProps를 호출하는것이 아니라 단지 export된 JSON을 사용한다는 것을 의미한다.

 

 

Only allowed in a page

getStaticProps는 page에서만 exported 된다. 다른 곳에서는 사용할 수 없다. 이유는 리액트는 페이지가 render되기 전에 data 모두를 가지고 있어야 하기 때문이다. 

 

또한 반드시 export async function getStaticProps() {}로 동작해야한다.

TypeScript쓸 때는 export const getStaticProps: GetStaticProps = async (context) => {}

 

 

Run on every request in development

개발환경에서 getStaticProps는 매 요청시에 호출된다.

 

 

Preview Mode

요청시에 페이지가 render 되길 원하는 경우가 생긴다. 배포되기전에 프리뷰 초안을 보길 원할 수 있다.

자세한 내용은 Preview Mode 문서를 참조하자.

 

 

 

728x90
반응형

+ Recent posts