본문 바로가기
Web/React

[FE][Next JS] 웹 렌더링의 과거와 현재, 그리고 미래 ⏱ / 4️⃣ - Static Site Generation과 ISR (Incremental Static Regeneration)

by 서상혁 2023. 3. 19.

이전 포스팅들을 읽고 오시면 더 좋습니다 😄

 

- [FE] 웹 렌더링의 과거와 현재, 그리고 미래 ⏱  / 1️⃣ - 과거 <정적웹 에서 동적 웹으로>

- [FE] 웹 렌더링의 과거와 현재, 그리고 미래 ⏱ / 2️⃣ - 현재 < SPA와 CSR, SSR에 대해>

- [FE] 웹 렌더링의 과거와 현재, 그리고 미래 ⏱ / 3️⃣ - 미래 <React의  Concurrent Rendering>

 

 


 

렌더링의 시점과 횟수?

 

지난 포스팅에서 말했듯, 현대 렌더링 방식은 주로 '렌더링의 주체' (렌더링이 어디서 진행되느냐) 관점으로 크게 SSR 과 CSR로 나뉩니다. 렌더링을 담당하는 것이 서버냐, 클라이언트냐 의 차이죠.

오늘은 '렌더링의 주체' 관점으로가 아닌 '렌더링의 시점과 횟수(효율성)' 에 대한 얘기를 해보려고 합니다!

 

캐싱(caching) 에 대한 이야기를 하면 더 받아들이기가 편할 것 같아요. 우리는 레디스 등의 메모리 캐시로 API 결과값을 캐싱해서 사용하곤 합니다 . 불필요한 계산을 반복하지 않기 위해서이죠. 굳이 같은 output을 받기 위해 계산이라는 작업을 할 필요가 없으니까요. (input이 같으면 같은 output 이 나오는 케이스들에 대해). 그러면 렌더링이 대해서도 비슷한 관점에서 생각해봅시다.

 

렌더링은 과연 매 요청마다 필수일까요?

 

(여기서 말하는 렌더링의 의미는, 웹 서버가 다른 데이터소스로부터 데이터를 받아와서, HTML로 그리기 위해 데이터 파싱을 마치고 내려주는 Next JS 의 렌더링을 의미합니다.)

 

 

 

서버에서 렌더링을 담당하는(SSR) 경우를 가정해봅시다. 현대 서버가 클라이언트에게 리소스(JS, CSS, HTML 등등)을 제공하는 방식은 결코 단순하지 않습니다. 서버의 리소스를 잡아먹는 일이죠. 근데 만약 이 복잡한 과정을 매 요청바다 반복하는 것이 아닌, API 결과 값을 캐싱하듯이 캐싱해서 재사용할 수 있다면? 더할나위없이 좋겠죠? 

 

 


 

근데 그냥 이렇게 말로만 들으면 감이 잘 잡히지 않는게 당연합니다. 😅 예를 들어보죠, 클라이언트에서 제 블로그 게시글 '[FE] 웹 렌더링의 과거와 현재, 그리고 미래'  를 요청한다고 해봅시다. 제 블로그 게시글 내용은 유저가 클릭할 때마다 바뀔까요?? 결코 그렇지 않죠. 제가 수정을 하는 일이 없다면, 유저가 100번 열람을 해도 게시글 내용은 그대로일 것입니다. 그렇다는 말은, 제 블로그 게시글을 렌더링 하는 과정이 서버단에서 실행된다고 가정했을 때, 서버는 유저의 매 요청마다 똑같은 렌더링 과정을 반복하고 있고, 이는 서버자원 낭비로 직결된다는 것입니다. (이는 클라이언트 단에서 렌더링이 이루어져도 마찬가지이죠.)

 

그렇다면 이러한 서버자원낭비를 어떻게 해결할 수 있을까요? 방법은 간단합니다. 유저의 요청과 상관없이 렌더링을 미리 해두고, 그 렌더링 결과값을 계속해서 전달만 해주면 되는 것이죠. 일종의 캐싱작업 처럼요. 다시 말하자면 렌더링의 시점'서버가 클라이언트 에게 요청을 받았을 때' -> '빌드를 할 때' 로 바꾼 것이죠! 이를 빌드타이밍에 정적페이지를 미리 만들어둔다고 해서, 정적페이지 제공 방식 (Static Generation) 이라고 표현하구요, 이 방식을 이용하는 대표적인 프레임워크로는 Gatsby JS 가 있습니다. 블로그 페이지를 만들 때 많이 쓰곤 하죠. 

 

 https://www.gatsbyjs.org/ 

 

The Fastest Frontend for the Headless Web | Gatsby

Gatsby is a React-based open source framework with performance, scalability and security built-in. Collaborate, build and deploy 1000x faster with Gatsby Cloud.

www.gatsbyjs.com

 

 

 


 

Next JS 의 Static Generation

 

 Next JS에서도 이러한 과정을 손쉽게 사용할 수 있도록 함수 형태로 제공합니다. 바로 getStaticProps 를 이용해서인데요, 소스 빌드 타임에 리소스를 미리 렌더링 해두고, 이후 요청부터는 캐싱된 렌더링 결과들이 제공됩니다! 제가 앞서 예시로 든 블로그 페이지라던가, 도움말 페이지, 마케팅용 페이지 등등에서 주로 이러한 방식이 이용됩니다.

 

출처 :&nbsp;https://blog.logrocket.com/getinitialprops-vs-getserversideprops-nextjs/

 

조금 더 쉽게 설명하자면, 매 요청마다 특정 API 를 호출해서 데이터를 가져오는 방식이 아니라, 빌드시에 딱 1번만 API 호출이 이루어진다는 뜻입니다. 다시말해, 실시간으로 데이터를 불러와서 웹 페이지를 변경시켜줘야하는 사이트(SNS라던가, 커뮤니티 사이트라던가 등등) 들에는 적합하지 않겠죠?

 

아래 글들을 읽어보시면 더 이해하시기 편하실 겁니다.

 

https://programming119.tistory.com/234

 

[React] Next JS Pre-rendering / Static Generation(getStaticProps ) / SSR(getServerSideProps)

목차 기본 개념 Static Generation Static Generation 은 언제 사용될까? 예시 External Data 가 없는 경우 External Data 가 존재하는 경우 페이지가 External Data에 종속되는 경우 Fallback 기타 Server-side Rendering 예시

programming119.tistory.com

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

 

Data Fetching: getStaticProps | Next.js

Fetch data and generate static pages with `getStaticProps`. Learn more about this API for data fetching in Next.js.

nextjs.org

 

 


 

ISR (Incremental Static Regeneration)

 

 

Static Generation에서 한 단계 발전한 케이스로 ISR (Incremental Static Regenration) 방식이 있습니다. ISR 방식은  next js 12 버전부터 stable화된 따끈따끈한 방식입니다. 이는 특별히 새로운 방식이 아니구요, getStaticProps의 단점을 보완한 방식입니다. 

 

getStaticProps 는 빌드 시에만 렌더링할 데이터 변화를 줄 수 있죠.  이는 많은 웹 페이지에서 단점으로 작용합니다. 데이터 변화가 필요한 시점인데도 old한 데이터를 담고 있는 페이지를 노출한다던가 너무 잦은 빌드를 일으킬 수 있기 때문이죠. ISR 방식을 이용하면 이러한 단점들을 보완할 수 있습니다.

 

// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  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 10 seconds
    revalidate: 10, // In seconds
  }
}

 

Next JS에서 제공하는 예시인데요, getStaticProps를 사용함과 동시에 return 객체에 revalidate 옵션을 주고 있습니다. (이 예시에선 10초로 주었네요)

 

getStaticProps 방식 답게 빌드 타임에 렌더링을 미리 실행을 하구요, 이후 요청이 들어온다면 적어도 10초 마다는 렌더링을 재실행 한다는 뜻입니다. 빌드 타임 렌더링 방식의 장점을 가져감과 동시에 주기적으로 페이지의 변화를 가져갈 수 있죠.

 

매 요청마다 렌더링을 할 필요는 없지만, 주기적으로 정보를 갱신해줘야하는 무거운 페이지들에 적합합니다. 예시로 들어보자면, 전시회 홍보 웹 페이지 등등 정보제공 페이지에 적합하겠네요 👍

 

 


 

On-demand Revalidation

 

 

Next JS 12.2.0 부터는 on-demand revalidation 또한 지원합니다. 말 그대로 revalidate 과정을 필요에 따라 진행하는 방식입니다.

 

ISR 방식에서는 지정한 시간이 지날 때 까지는, 항상 같은 화면이 노출되게 됩니다. 미리 렌더링 캐싱된 HTML만이 내려가게 될 테니까요. 헌데 on-demand revalidation 방식을 이용하면, 지정된 인터벌 타임 외에도 revalidate이 가능하게 됩니다. 즉, 내가 지정한 특정 상황에 렌더링을 재실행 하는것이죠! 

 

임의의 사용자가 revalidate를 강제하지 못하게 토큰을 생성해주고요, 아래와 같은 next js API 라우터를 열어둡니다.

 

export default async function handler(req, res) {
  // Check for secret to confirm this is a valid request
  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  try {
    // this should be the actual path not a rewritten path
    // e.g. for "/blog/[slug]" this should be "/blog/post-1"
    await res.revalidate('/path-to-revalidate')
    return res.json({ revalidated: true })
  } catch (err) {
    // If there was an error, Next.js will continue
    // to show the last successfully generated page
    return res.status(500).send('Error revalidating')
  }
}

결국 API 핸들러 내부에 res.revalidate() 코드로 revalidate를 진행할 수 있게 되는 것입니다! (내가 원하는 상황 원하는 로직에 이 함수를 실행시켜주면 되겠죠?)

 

위 예시처럼 api 라우터를 열어두면 아래와 같은 api 요청으로 원격 revalidate 또한 가능해집니다.

https://<your-site.com>/api/revalidate?secret=<token>

 

특정 조건이 trigger 됐을 때 해당 api 를 호출해서 페이지를 revalidate 시킬 수 있겠네요. 예시로 들어보자면, 데이터 배치작업이 끝난 후 배치작업이 정상적으로 마무리됐다면 위 api 호출하는 과정을 포함시켜 데이터에 dependant한 페이지를 새로 revalidate 해주는 방식 등이 있겠네요.

 

 

 


출처

 

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

 

Data Fetching: Incremental Static Regeneration | Next.js

Learn how to create or update static pages at runtime with Incremental Static Regeneration.

nextjs.org

 

 

728x90

댓글