
렌더링 방식 알아보기 (Next.JS 블로그 속도 개선기)
2024-07-20
대표적인 렌더링 방식 CSR, SSR, SSG를 알아보고, SSG로 블로그 속도를 개선한 과정을 소개합니다.
Contents
- 렌더링의 종류
- CSR (Client Side Rendering)
- SSR (Server Side Rendering)
- SSG (Static Site Generation)
- 이 글을 작성하게 된 이유
- 해결하려고 했던 시도들
- Gatsby.JS로 마이그레이션을 시도하면서
- Next.JS로 SSG 사용하기
- 이번에 얻은 교훈
렌더링의 종류
웹 사이트를 렌더링 하는 방법은 크게 CSR, SSR, SSG가 있습니다. 일반적으로 SPA (Single Page Application)의 경우는 CSR을 사용하고 MPA (Multiple Page Application)의 경우는 SSR을 사용합니다.
CSR (Client Side Rendering)
CSR은 앞서 말했듯이 SPA에서 사용하는 렌더링 방식입니다. SPA는 Single Page Aplication으로 한 개의 페이지로 이루어져 새로운 페이지를 다시 불러오지 않고 필요한 부분만 동적으로 다시 갱신합니다.
CSR은 Client에서 HTML, CSS, JavaScript를 모두 다운로드 한 뒤에 렌더링 하는 방식입니다.
CSR은 필요한 부분만 다시 로드되기 때문에 더 부드러운 사용자 경험을 제시할 수 있습니다. 또한 렌더링 속도에서도 이점을 갖습니다. 하지만 CSR은 첫 렌더링의 경우 속도가 느릴 수 있습니다. 그리고 SEO 부분에서 취약점을 갖습니다. 초기 HTML이 비어있어 크롤링에 불리하기 때문입니다.
SSR (Server Side Rendering)
SSR은 앞서 말했듯이 MPA에서 사용하는 렌더링 방식입니다. MPA는 Multiple Page Aplication으로 여러개의 페이지로 이루어져 페이지를 요청할 때 마다 새로운 페이지를 불러옵니다.
SSR은 페이지 요청이 오면 Server에서 페이지를 완성한 후에 Client에게 보내주는 렌더링 방식입니다.
SSR은 앞서 말한 CSR보다 SEO에 유리합니다. 크롤러가 인덱싱할 수 있는 정보가 더 많기 때문입니다. 그리고 CSR보다 초기 렌더링의 속도가 더 빠릅니다. 완성된 페이지를 Client가 받을 수 있기 때문입니다. 하지만 SSR은 페이지 전환 시 새로운 파일을 요청하여 받기 때문에 CSR보다 화면 전환에서 깜빡임과 같은 낮은 사용자 경험을 받을 수 있습니다. 또한 초기 이후 페이지 전환은 CSR보다 속도가 느립니다.
SSG (Static Site Generation)
SSG는 미리 정적 페이지를 만들어 놓고 Client가 페이지를 요청하면 Server에서 보내주는 방식입니다.
SSR과 비교해보면, SSR의 경우는 Client가 페이지를 요청을 하면 Server가 페이지를 만들어서 보내주지만, SSG는 Client가 페이지를 요청하면 Server가 이미 만들어 놓은 정적 페이지를 보내줍니다. 그렇기 때문에 내용이 업데이트가 되지 않는 정적 페이지라면 SSG가 SSR보다 유리합니다. 하지만 업데이트가 필요하다면 CSR이나 SSR을 사용해야 합니다.
이 글을 작성하게 된 이유
사실 웹 개발을 하는 사람들 중 위의 내용을 모르는 개발자들을 별로 없을 것입니다. 그럼에도 이 글을 작성하게 된 이유는 단순히 렌더링에 대해 다시 짚어보자는 부분도 있지만 이 블로그를 운영하면서 속도에 고민이 있었기 때문입니다. 블로그를 다시 만들면서 전의 블로그보다 렌더링 속도가 너무 느렸졌습니다.
이전 블로그 (v2)와 현재 블로그 (v3) 속도 비교
해결하려고 했던 시도들
Next.JS로 블로그를 처음 만들 때 Next.JS와 Gatsby.JS를 비교하거나 Next.JS에서 Gatsby.JS로 블로그를 마이그레이션하는 경우를 보았습니다. 속도를 고민하던 중 이 경우가 떠올랐고 Gatsby.JS를 알아보았습니다. Gatsby.JS는 정적 사이트 생성기였습니다. 포트폴리오나 블로그 같은 데이터가 변하지 않는 정적 페이지를 생성하기에 최적화 된 프레임워크였습니다. 그래서 우선 Gatsby.JS로 저가 작성해놓았던 MDX 파일들을 올려 배포해보았습니다. (Vercel, Cloudflare로 배포 테스트)
배포된 페이지들은 기존 블로그보다 훨씬 빨랐습니다. 그래서 저는 Gatsby.JS로 블로그를 마이그레이션 하기로 결정했습니다. 하지만 Next.JS에서도 SSG를 지원합니다. 그러면 저는 왜 Gatsby.JS로 마이그레이션 하려고 했덜걸까요? 사실 저는 Next.JS에서 SSG를 지원한다는 사실을 잊고 있었습니다. (Pages Router에서 SSG를 사용하는 공식문서를 본 적이 있으나 App Router에서는 관련 문서를 본 적이 없어 지원하지 않는다고 착각하고 있었습니다.) 심지어 Next.JS로 만들어진 전 블로그에도 글들이 SSG 방식으로 렌더링 되고 있어서 빨랐던 것이었습니다. 저는 이 부분을 놓치고 무작정 Gatsby.JS로 마이그레이션을 시작했습니다.
Gatsby.JS로 마이그레이션을 시도하면서
Gatsby.JS가 React 기반이라는 것은 이미 알고있었기 때문에 쉽게 개발할 수 있을 것이라고 생각했습니다. 하지만 처음보는 GraphQL과 빌드 때 나타나는 여러 에러들이 저를 힘들게 했습니다. GraphQL 구조 자체는 이해하는데 어렵지 않았습니다. 하지만 이를 이용해서 파일들을 가져오는 개념이 처음에는 조금 어려웠습니다. 그리고 gatsby.config.js 파일에 remark, rehype 관련 플러그인을 추가하면 import 에러가 발생했습니다. 이를 해결하기 위해 require, import, wrapESMPlugin을 사용해 보았지만 끝내 해결하지 못했습니다.
Next.JS로 SSG 사용하기
위의 문제로 난항을 겪던 중 우연히 Next.JS App Router에서 SSG를 사용하는 공식 문서를 발견했습니다. 이를 확인하고 혹시 전 블로그에 이와 관련된 코드가 있을까 확인해보았고 역시나 generateStaticParams()라는 함수를 발견했습니다.
export async function generateStaticParams() {
return techContents.map((post) => ({
slug: post.slug,
}));
}
export default function Post({ params }: { params: { slug: string } }) {
const props = getPost(params);
return <Article props={props} location="tech" />;
}
위와 같이 generateStaticParams() 함수를 추가하고 빌드를 해보았습니다. 결과는 역시 제가 원하는 속도를 낼 수 있었습니다.
SSG 적용 전과 적용 후
이번에 얻은 교훈
우선 제가 전에 개발했던 블로그와 현재 블로그의 속도를 개발하면서 둘의 코드를 자세히 비교하지 않았던 점에 대해서 반성했습니다. 만약 한 번이라도 차이점을 찾으려고 노력했다면 Gatsby.JS로 개발했던 2일 동안 그 수많은 에러를 겪진 않았을 것입니다.
하지만 이번 일로 많은 것을 얻기도 하였습니다. 우선 기존에 CSR과 SSR은 잘 이해하고 있었지만 SSR과 SSG의 차이점을 명확하게 알지 못하고 있었는데 이번 기회로 알게 되었습니다. 또한 웹 개발자로서 알아야 한다는 GraphQL에대해서 조금 이해할 수 있었습니다. 그리고 Gatsby.JS를 사용해보고 배포를 해보았다는 점이 매우 뜻깊었습니다.
사용자 경험 보다는 단순히 저의 속도 만족에서 시작했던 이 일로 이런 뜻 깊은 교훈과 경험들을 겪을 수 있는 계기가 되어 좋았습니다. 만약 이 글을 보신다면 렌더링과 사용자 경험에 대해 조금 더 생각해보시는 계기가 되면 좋겠습니다.
긴 글 읽어주셔서 감사합니다.