CSR에서 SSG로 마이그레이션, Github Pages로 Next.js 배포하기

✨ 기존 블로그의 구현 방식

기존 블로그는 CSR(Client-Side Rendering)로 구현되어 있었습니다. 이유는 다음과 같습니다.

  1. SEO 고려사항: 구글 서치는 단순 정적 크롤링을 넘어서 JavaScript를 실행하며 SEO 데이터를 생성합니다. 이를 감안하여, 구글 Search Console을 잘 활용하면 CSR로도 충분히 검색 노출이 가능하다고 판단했습니다.
  2. 빠른 개발: 익숙한 CSR 방식을 활용하면 블로그를 빠르게 개발할 수 있었습니다.

그러나 현재 블로그 유입 경로를 분석한 결과, 구글 검색보다는 SNS 공유를 통해 들어오는 경우가 많았습니다. 이에 따라 기존 CSR 블로그를 SSG(Static Site Generation)로 변경하여 Open Graph 미리보기 기능을 활용하고, 링크의 신뢰도를 높이기로 결정했습니다.

🔍 CSR에서 SSG로의 전환 이유

1. 링크의 신뢰성 향상

SNS에 공유한 기존 블로그 링크는 다음과 같은 문제가 있었습니다.

  • 단순 URL로 인해 신뢰감이 떨어지고 클릭하고 싶은 욕구를 자극하지 못함.

이를 개선하기 위해 Open Graph 메타태그를 적용하여 클릭률(CTR)을 높이고, 더 신뢰감 있는 링크로 보이게 만들고자 했습니다. Open Graph를 사용하려면 서버에서 HTML을 생성해야 하기에, CSR 방식에서 벗어날 필요가 있었습니다.

2. 비용 절감과 유지 보수 간소화

기존 블로그는 Github Pages를 사용해 배포되고 있었습니다. SSR을 사용하여 추가 서버비용을 들여 EC2나 Vercel 같은 동적 호스팅 서비스를 사용할 순 있었지만, 블로그의 특성상 SSR의 필요성을 느끼지 못했으며, SSG를 활용하면 서버비용과 CDN 관리 비용을 모두 줄일 수 있었습니다.

🔄 SSG로의 마이그레이션 과정

1. 동적 라우트를 정적 HTML로 생성하기

Next.js의 getStaticPaths를 사용하여 동적 라우트를 정적으로 생성할 수 있습니다. 이를 통해 각 블로그 글에 대한 HTML 파일을 빌드 타임에 생성했습니다.

// pages/[slug].js // ... export async function getStaticPaths() { const paths = [{ params: { id: "1" } }, { params: { id: "2" } }]; return { paths, fallback: false }; }

링크 미리보기 이미지Data Fetching: getStaticPaths | Next.jsFetch data and generate static pages with `getStaticPaths`. Learn more about this API for data fetching in Next.js.

2. MDX 활용하기

next-mdx-remote를 사용하면 MDX 파일을 직접 컴파일 할 필요없이 쉽게 사용할 수 있습니다. getStaticProps를 통해 필요한 데이터를 로드하고, MDX 내용을 컴포넌트화하여 렌더링했습니다.

// ... export default function PostDetailPage({ mdxSource }: PostDetailPageProps) { return ( <div className={styles.mdx}> <MDXRemote {...mdxSource} components={{ a: FancyLink, code: FancyCode, img: FancyImage, }} /> </div> ); } export async function getStaticProps(context: Parameters<GetStaticProps>[0]) { // ... const filePath = path.join(process.cwd(), "src", "statics", postType.toLowerCase(), `${postFileName}.mdx`); const fileContents = fs.readFileSync(filePath, "utf-8"); const mdxSource = await serialize(fileContents, { mdxOptions: { remarkPlugins: [remarkGfm], }, }); return { props: { mdxSource }, }; }

링크 미리보기 이미지next-mdx-remoteutilities for loading mdx from any remote source as data, rather than as a local import. Latest version: 5.0.0, last published: a year ago. Start using next-mdx-remote in your project by running `npm i next-mdx-remote`. There are 123 other projects in the npm registry using next-mdx-remote.

3. ESM 모듈 지원 문제 해결하기

Next.js 프로젝트에서 ESM만 지원하는 라이브러리를 사용할 때는 transpilePackages 옵션으로 문제를 해결할 수 있습니다. 이번 프로젝트에서는 react-syntax-highlighter 라이브러리가 ESM만 지원했기에 아래와 같이 설정했습니다.

// next.config.ts const nextConfig: NextConfig = { transpilePackages: ["react-syntax-highlighter"], };

링크 미리보기 이미지next.config.js: transpilePackages | Next.jsAutomatically transpile and bundle dependencies from local packages (like monorepos) or from external dependencies (`node_modules`).

4. .nojekyll 파일 생성하기

Github Pages는 기본적으로 Jekyll을 기반으로 동작합니다. 그러나 Next.js의 _next 폴더는 Jekyll에서 특수 리소스로 간주되어 정상적으로 배포되지 않는 문제가 있었습니다. 이를 해결하기 위해 .nojekyll 파일을 추가했습니다.

#... - name: Add .nojekyll file run: echo > ./out/.nojekyll

5. Sitemap 생성하기

기존 Vite 기반의 CSR에서는 커스텀 플러그인을 통해 Sitemap을 생성했지만, Next.js에서는 next-sitemap 라이브러리를 사용하여 간편하게 Sitemap을 구현할 수 있었습니다.

//next-sitemap.config.js module.exports = { siteUrl: "https://example.com", generateRobotsTxt: true, };

링크 미리보기 이미지next-sitemapSitemap generator for next.js. Latest version: 4.2.3, last published: 2 years ago. Start using next-sitemap in your project by running `npm i next-sitemap`. There are 50 other projects in the npm registry using next-sitemap.

🎉 결과와 느낀 점

SSG 블로그를 완성하면서 SNS 공유 링크의 신뢰성과 클릭률이 크게 향상되었습니다. 또한, SSG 방식은 빌드 타임에 HTML 파일을 생성하므로 CSR 대비 FCP(First Contentful Paint)와 LCP(Largest Contentful Paint) 지표가 크게 개선되었습니다.

렌더링 방식은 사용자의 요구와 홈페이지 특성에 따라 달라져야 합니다. 이번 경험을 통해 적절한 렌더링 방식을 선택하는 것이 중요하다는 것을 깨달았습니다.

여러분들도 적절한 렌더링 방식을 선택하여 비용을 줄이고 사용성을 높힐 수 있는 효과적인 서비스를 만드셨으면 합니다!