SSG 렌더링으로 Vercel 콜드스타트 문제 해결하기 Thumbnail

직접 해보면서 깨닫는 SSR과 SSG 차이

6 min read
NextJS렌더링

직접 해보면서 깨닫는 SSR과 SSG 차이

썸네일썸네일

문제 상황

현재 내 블로그는 NextJS로 구현되어있다. NextJS를 선택한 이유는 SEO를 최적화하기 간편하기 때문이었고, 실제로 SSR의 장점인 서버 컴포넌트를 사용해서 블로그 글을 렌더링하고 있었다.

최근 친구의 피드백으로 사이트가 느리다는 피드백을 받았다. 사이트가 성능적으로 문제될 것은 없었는데도 느리다는 것을 깨달았다.

알아본 결과 Vercel로 배포할 경우, 그리고 Hobby(무료) 요금제일 경우 웹 사이트가 사용되지 않으면 콜드 스타트 상태가 된다는 걸 알게되었다.

Cold Start

콜드 스타트는 직역하면 차가운 시작이라는 뜻이다. 실제로 기계&자동차 공학에서 유래된 말로 자동차가 예열되지 않은(차가운) 상태에서 시동을 켜려고 하면 더 많은 시간과 에너지를 필요로 하는 상황을 말한다.

컴퓨터 공학에서는 절전 모드에서 다시 켜는 것이라고 생각하면 될 것 같다. vercel은 그렇게 동작하기 때문에 자주 사이트에 접속하지 않으면 처음 서버에서 데이터를 가져오는 시간이 길어질 수 있다는 것을 알게 되었다.

콜드스타트 해결하기

콜드 스타트 문제를 해결하기 위한 방법도 여러가지가 있다.

무식한 방법으로는 콜드 스타트 상태로 들어가지 못하게 지속적으로 서버에 ping 메시지를 날리는 방법이 있었다.

하지만 이 방법은 뭔가 편법을 쓰는 느낌에 문제를 해결했다는 느낌이 안들어서 고려하지 않았다.

다음 방법으로는 SSG 방식을 사용하는 것이었다. 이전에는 SSR과 SSG의 차이 정도만 알고 있었는데 직접 사용해보는 것은 처음이었다.

SSG를 사용하면 더 빠른 속도로 로딩이 가능하고, 검색엔진 최적화에도 더 유리하다는 것을 알게 되어 이 방법으로 해결해보기로 결정했다.

SSG (Static Site Generation)

단어 그대로 정적으로 사이트를 생성하는 방식을 의미한다. 이 방법을 사용하면 빌드 시점에 posts url들이 정적으로 생성되어 배포된다.

이 방법을 사용하면 기존에 정적인 페이지들 ('/', '/portfolio') 처럼 동적인 페이지들을 렌더링하기 때문에 응답 속도가 매우 빨라진다는 특징이 있다. 그렇기 때문에 Vercel의 백엔드를 가동하는 시간을 필요로 하지 않다는 점이 특징이다.

그 외에도 콜드스타트로 인한 검색엔진 크롤러의 타임아웃 현상을 줄일 수 있다는 점도 장점이다.

SSR과의 차이점 SSR은 서버 사이드 렌더링으로, 서버에서 생성해서 유저에게 전달한다는 점이고, SSG는 빌드 시점에서 생성된 HTML을 정적으로 서빙한다는 점에서 약간의 차이점이 있다.

생성 시점으로 차이를 두면 될 것 같다.

  • SSG: 빌드 시 페이지 생성 (build time)
  • SSR: 요청 시 페이지 생성 (request time)

NextJS에서 사용하기

NextJS에서는 정말 쉽게 SSG를 사용할 수 있었다. App Router 기준 generateStaticParams() 를 사용해서 만들 수 있다.

기존 코드에 아래 코드만 추가하면 SSG를 적용할 수 있다. 빌드 시점에 생성하는 것이다보니 NextJS에게 "~~ 여기 경로를 SSG로 적용해주세요."라고 알리는 목적이다.

// 정적 생성할 경로 지정
export async function generateStaticParams() {
  await dbConnect();
  const posts = await Post.find({}, 'slug').lean();

  return posts.map((post) => ({
    slug: post.slug,
  }));
}

빌드 로그 확인

빌드하게 되면 아래와 같은 빌드 로그를 얻을 수 있다. route 옆에 직관적인 기호로 어떤 방식이 적용되었는지 보여준다.

잘보면 /posts/[slug]에 색이 칠해져있는 것으로 SSG가 적용되었음을 알 수 있다!

├ ƒ /portfolio/[slug]                    11.9 kB         136 kB
├ ○ /posts                               7.96 kB         137 kB
├ ● /posts/[slug]                        390 kB          518 kB
├   ├ /posts/next-js로-나만의-블로그-만들기
├   ├ /posts/블로그-cls-성능-최적화하기
├   ├ /posts/내가-만드는-블로그로-알아보는-seo-최적화
├   └ [+13 more paths]
├ ○ /robots.txt                          0 B                0 B
├ ○ /rss                                 0 B                0 B
├ ○ /series                              2.36 kB         127 kB
└ ○ /sitemap.xml                         0 B                0 B
+ First Load JS shared by all            87.7 kB
  ├ chunks/7023-0674f41d3955597f.js      31.6 kB
  ├ chunks/fd9d1056-d060727c397bec1b.js  53.6 kB
  └ other shared chunks (total)          2.49 kB
○  (Static)   prerendered as static content
●  (SSG)      prerendered as static HTML (uses getStaticProps)
ƒ  (Dynamic)  server-rendered on demand

빌드 시점에 정적인 페이지로 바꿔주는 과정이 필요하기 때문에, 글이 많아지면 필연적으로 빌드 시간이 길어진다고 한다.

결과

얼마나 개선이 되었는지를 확인하기 위해 SSG 도입 전 빌드 파일과 도입 후 빌드 파일 간의 로딩 속도를 확인해보자.

개선 전

개선 전 데이터로는 1.75초가 걸린다. 로컬 환경에서 production 빌드를 실행시킨 것이기 때문에 성능상으로는 배포환경과 같지만, 다른 점이라면 vercel의 콜드 스타트로 인해 아래 1.75초에 오버헤드가 더 붙는다는 점이다.

개선 전개선 전

개선 후

개선 후 데이터를 보자. 122ms로 거의 15분의 1이 된 모습이다. 확실한 비교를 위해 캐시 없이 비교했다.

개선 후개선 후

이제 SSG로 서빙이 되기 때문에 이전의 SSR 방식보다 더 빠르게 페이지가 로딩이 되는 것을 확인할 수 있다. 백엔드를 거치지 않기 때문에 콜드 스타트 문제도 없다.


답은 SSG로 빌드된 사이트에 새 콘텐츠를 추가하면 그 콘텐츠는 다음 빌드 시점까지 사이트에 반영되지 않는다.

그렇지만 NextJS에서는 이런 문제를 해결하기 위해 ISR, On Demanding Revalidation 등의 기능을 지원한다.

이론적으로 SSG만 설정하고 별도의 ISR이나 On-demanding 설정을 하지 않는다면 새로운 컨텐츠에 대한 정적 페이지는 생성이 되지 않아야 한다.

그러나 새로운 글을 발행했을 때 SSG로 문서가 생성되는 것을 확인했다. 찾아보고 여러가지로 생각해본 결과 Vercel에서 자동으로 ISR을 지원해주는 것이라고 결론을 지었다.

왜냐하면 새로운 글에 대해서는 HTML이 생성되었지만(새로운 route가 생겼으므로), 이미 생성된 글에 대한 수정 사항이 반영이 되지 않는 것을 보면 명시적으로 ISR 설정을 안했기 때문에 반영이 안된다고 판단했다.

이걸 해결하기 위해 ISR을 도입하는 것을 고려해야겠다.

📌 Table of Contents

0
추천 글