Next.js 블로그 만들기 — GitHub Pages에서 Cloudflare Pages로 이전하기
GitHub Pages, 뭐가 부족했나
GitHub Pages로 블로그를 운영하면서 몇 가지 불편한 점이 있었다.
| 항목 | GitHub Pages | 제한 |
|---|---|---|
| 트래픽 | 월 100GB 대역폭 | 초과 시 사이트 다운 |
| 빌드 | GitHub Actions 의존 | 분당 빌드 횟수 제한 |
| basePath | 필수 (/repo-name) | URL이 길어짐 |
| 커스텀 도메인 | 별도 DNS 설정 필요 | Freenom 등 외부 서비스 의존 |
| CDN | 없음 | 글로벌 성능 불리 |
특히 basePath 문제가 가장 귀찮았다. terajh.github.io/tera-log처럼 레포 이름이 경로에 붙기 때문에, 모든 정적 파일 경로에 /tera-log를 prefix로 달아야 했다. 이미지, favicon, JSON 파일 전부.
const nextConfig = {
output: 'export',
basePath: '/tera-log', // 이게 모든 경로에 붙는다
images: { unoptimized: true },
}커스텀 도메인을 연결하면 basePath를 제거할 수 있지만, 무료 도메인 서비스(Freenom)는 2023년부터 신규 등록이 막혔고, 유료 도메인은 연간 비용이 든다.
Cloudflare Pages라는 대안
Cloudflare Pages는 정적 사이트 호스팅 서비스다. GitHub Pages와 같은 역할이지만, 제한이 훨씬 적다.
| 항목 | GitHub Pages | Cloudflare Pages |
|---|---|---|
| 대역폭 | 월 100GB | 무제한 |
| 빌드 | 월 2,000분 (Actions) | 월 500회 |
| 도메인 | username.github.io/repo | project.pages.dev |
| basePath | 필수 (Organization Pages 제외) | 불필요 |
| CDN | X | 글로벌 CDN 기본 제공 |
| DDoS 보호 | X | 기본 제공 |
| HTTPS | Let's Encrypt | 자동 |
| 빌드 환경 | GitHub Actions 직접 구성 | 자동 감지 |
| 비용 | 무료 | 무료 |
가장 큰 장점은 basePath가 필요 없다는 것이다. tera-log.pages.dev처럼 프로젝트 이름이 서브도메인으로 들어가기 때문에, 사이트가 루트(/)에서 바로 서빙된다.
이전 과정
1. Cloudflare Pages 프로젝트 생성
- Cloudflare Dashboard 접속
- Workers & Pages > Pages > Create a project
- Connect to Git > GitHub 로그인 > 레포 선택
2. 빌드 설정
| 설정 | 값 |
|---|---|
| Framework preset | None |
| Build command | pnpm run build |
| Build output directory | out |
Next.js의 output: 'export'가 out/ 디렉토리에 정적 파일을 생성하기 때문에, 이걸 그대로 배포 경로로 지정하면 된다.
Save and Deploy를 누르면 첫 빌드가 시작된다.
3. basePath 제거
Cloudflare Pages는 루트에서 서빙되므로 basePath가 필요 없다. 제거한다.
const nextConfig = {
output: 'export',
// basePath: '/tera-log', — 삭제
images: { unoptimized: true },
}프로젝트 전체에서 basePath를 참조하던 부분도 정리해야 한다.
export const BASE_PATH = '' // '/tera-log' → 빈 문자열로
export const SITE_CONFIG = {
// ...
url: 'https://tera-log.pages.dev', // GitHub Pages URL → Cloudflare URL
} as constBASE_PATH를 빈 문자열로 바꾸면 기존에 ${BASE_PATH}/images/...처럼 사용하던 경로들이 자연스럽게 /images/...로 바뀐다. 참조하는 곳을 하나하나 고칠 필요가 없다.
4. 배포 확인
GitHub에 푸시하면 Cloudflare가 자동으로 빌드하고 배포한다. 빌드 로그에서 다음을 확인한다:
✓ Compiled successfully
✓ Generating static pages (12/12)
✓ Exporting (2/2)
배포가 완료되면 https://tera-log.pages.dev에서 블로그를 확인할 수 있다.
SEO 설정
배포만 하면 구글에 자동으로 노출되지 않는다. 검색엔진이 사이트를 크롤링할 수 있도록 몇 가지 설정이 필요하다.
robots.txt
검색엔진 크롤러에게 "이 사이트를 크롤링해도 된다"고 알려주는 파일이다.
User-agent: *
Allow: /
Sitemap: https://tera-log.pages.dev/sitemap.xmlpublic/ 디렉토리에 넣으면 빌드 시 out/ 디렉토리에 자동으로 복사된다.
sitemap.xml 자동 생성
sitemap은 사이트의 모든 페이지 목록을 검색엔진에 알려주는 파일이다. 글을 추가할 때마다 수동으로 수정하면 번거로우니, 빌드 시 자동 생성되도록 스크립트를 만들었다.
import fs from 'node:fs'
import path from 'node:path'
import matter from 'gray-matter'
const SITE_URL = 'https://tera-log.pages.dev'
const CONTENT_DIR = path.join(process.cwd(), 'content')
const OUTPUT_PATH = path.join(process.cwd(), 'public', 'sitemap.xml')
function getPostEntries() {
return fs
.readdirSync(CONTENT_DIR)
.filter((file) => file.endsWith('.mdx'))
.map((file) => {
const content = fs.readFileSync(
path.join(CONTENT_DIR, file), 'utf-8'
)
const { data } = matter(content)
const slug = file.replace('.mdx', '')
return {
url: `${SITE_URL}/posts/${slug}`,
lastmod: new Date(data.date).toISOString().split('T')[0],
}
})
}
function buildSitemap(entries) {
const today = new Date().toISOString().split('T')[0]
const urls = [
` <url>
<loc>${SITE_URL}</loc>
<lastmod>${today}</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>`,
...entries.map(
(entry) => ` <url>
<loc>${entry.url}</loc>
<lastmod>${entry.lastmod}</lastmod>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>`
),
]
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls.join('\n')}
</urlset>`
}
const entries = getPostEntries()
const sitemap = buildSitemap(entries)
fs.writeFileSync(OUTPUT_PATH, sitemap, 'utf-8')package.json에 prebuild 스크립트를 추가하면, pnpm build 실행 시 sitemap이 먼저 생성된다.
{
"scripts": {
"prebuild": "node scripts/generate-sitemap.mjs",
"build": "next build"
}
}Google Search Console 등록
- Google Search Console에서 URL 접두어 방식으로
https://tera-log.pages.dev등록 - 인증 방식은 HTML 파일 업로드 선택 — 다운로드한 파일을
public/에 넣으면 된다 - 인증 완료 후 Sitemaps 메뉴에서
sitemap.xml제출
*.pages.dev도메인은 DNS 레코드를 수정할 수 없기 때문에, DNS 인증 방식은 사용할 수 없다. HTML 파일 또는 메타 태그 방식을 사용해야 한다.
등록 후 구글이 크롤링하고 인덱싱하는 데 며칠에서 몇 주 걸릴 수 있다.
GitHub Actions 정리
Cloudflare Pages로 완전히 이전했다면, 기존 GitHub Pages 배포 워크플로우는 더 이상 필요 없다.
# 사용하지 않는 워크플로우 삭제
rm .github/workflows/deploy.ymlGitHub 레포 > Settings > Pages에서도 GitHub Pages를 비활성화해두면 깔끔하다.
트러블슈팅
CSS가 깨진다
basePath를 제거하지 않으면 발생한다. Cloudflare Pages는 루트(/)에서 서빙하는데, CSS/JS 파일을 /tera-log/_next/...에서 찾으려고 하기 때문이다. next.config.mjs에서 basePath를 제거하고, BASE_PATH를 빈 문자열로 바꿔야 한다.
빌드는 성공하는데 배포가 실패한다
Build output directory가 out으로 설정되어 있는지 확인한다. Next.js의 output: 'export'는 기본적으로 out/ 디렉토리에 빌드 결과물을 생성한다.
페이지 새로고침 시 404
Next.js 정적 빌드는 각 경로마다 HTML 파일을 생성한다. /posts/my-post라면 out/posts/my-post.html이 존재해야 한다. generateStaticParams에서 모든 경로를 반환하고 있는지 확인한다.
최종 비교
| GitHub Pages | Cloudflare Pages | |
|---|---|---|
| URL | terajh.github.io/tera-log | tera-log.pages.dev |
| basePath 필요 | O | X |
| CDN | X | 글로벌 |
| 대역폭 | 100GB/월 | 무제한 |
| 설정 난이도 | Actions 직접 작성 | Git 연동만 |
| 비용 | 무료 | 무료 |
같은 무료인데 제한이 적고, 설정도 간단하고, CDN까지 붙는다. 정적 블로그라면 Cloudflare Pages가 더 나은 선택이다.
관심 있을 만한 포스트
Next.js 블로그 만들기 — 검색엔진 등록 4종 완전 정복
Google Search Console, Bing Webmaster Tools, 네이버 서치어드바이저, 다음 검색등록까지. Next.js 블로그를 검색엔진에 노출시키는 전체 과정을 정리했다.
Next.js 블로그 만들기 — 정적 블로그에 맞춤 추천 포스트 기능 추가
localStorage에 조회 이력을 저장하고, 태그 가중치 스코어링으로 정적 블로그에서도 개인화 추천을 구현하는 방법.
Bun vs Node.js vs Deno — 뭐가 다른지, 그래서 뭘 쓰면 좋은지 (2026 기준)
런타임 3대장 비교: 호환성(Node), 속도/번들(Bun), 올인원/보안(Deno). 팀/프로덕트 상황별 선택 기준과 체크리스트까지 정리.
번들러(Bundle)란 뭐고, 왜 필요할까? — 요즘 번들러/빌드 툴 비교 가이드
번들러의 역할(모듈/의존성/트랜스파일/최적화)을 쉽게 설명하고, Vite·Rollup·esbuild·Webpack·Rspack·Turbopack 같은 도구를 상황별로 비교합니다.
Claude Code 에이전트 팀 — 여러 AI가 협업하는 새로운 방식
Claude Code의 실험적 기능인 에이전트 팀을 정리했다. 여러 Claude 인스턴스가 리더-팀원 구조로 작업을 분담하고, 서로 메시지를 주고받으며 병렬로 협업하는 구조다.
Next.js 블로그 만들기 — 정적 블로그에 검색 기능 추가
빌드 타임 검색 인덱스 생성과 클라이언트 사이드 필터링으로 정적 블로그에 검색 기능을 구현하기. Cmd+K 단축키, 오버레이 UI까지.
Next.js 블로그 만들기 — 스크롤 프로그레스 바와 Canvas 렌더링 이슈 해결
스크롤 진행률 프로그레스 바 구현과 Canvas 커서 효과가 GNB backdrop-blur와 충돌하며 발생한 깜빡임 이슈 해결기.
Next.js 블로그 만들기 — TOC와 커서 효과로 디테일 살리기
IntersectionObserver 기반 TOC(Table of Contents)와 Canvas 커서 트레일 효과 구현기. 스크롤 하이라이팅, fixed 레이아웃 처리까지.