jeongkyu.dev

Astro 와 Cloudflare Pages 로 개인 사이트 만들기

이 사이트를 만들면서 내린 선택들을 정리해 둡니다. 나중에 구조를 바꿀 때 “왜 이렇게 했더라”를 다시 추적하지 않으려는 목적이 큽니다.

왜 Astro 인가

블로그는 대부분 읽기 전용입니다. 서버에서 매 요청마다 HTML 을 만들 이유가 없고, 클라이언트에 프레임워크 런타임을 내려보낼 이유는 더 없습니다. Astro 는 기본이 정적 HTML 이고, 인터랙션이 필요한 컴포넌트만 선택적으로 하이드레이션합니다.

콘텐츠 컬렉션도 결정적이었습니다. 프론트매터에 스키마를 붙일 수 있어서, 오타나 필드 누락이 런타임이 아니라 빌드 타임에 잡힙니다.

const blog = defineCollection({
  loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
  }),
});

pubDate 를 빼먹으면 배포가 아니라 빌드가 실패합니다. 이게 훨씬 낫습니다.

왜 Workers 가 아니라 Pages 인가

Cloudflare 는 신규 프로젝트에 Workers 를 권장하지만, 이 사이트에는 Pages 를 골랐습니다.

Pages Workers
기본 URL <프로젝트>.pages.dev <이름>.<계정>.workers.dev
404 처리 404.html 자동 설정 필요
Cron·Durable Objects 없음 있음
신규 기능 투자 중단 진행 중

Workers 전용 기능을 하나도 쓰지 않는 100% 정적 사이트라서 실질적인 손해가 없었고, URL 이 한 단계 짧다는 점이 남았습니다. 나중에 서버 로직이 필요해지면 wrangler.jsonc 를 추가해 Workers 로 옮기면 됩니다. 빌드 산출물(dist/)은 양쪽이 동일합니다.

한국어 타이포그래피

한 가지는 꼭 짚고 가야 합니다. 기본 설정으로 두면 한국어 문장이 어절 중간에서 끊깁니다.

body {
  word-break: keep-all;
  overflow-wrap: break-word;
}

keep-all 이 어절 단위로 줄을 바꾸고, overflow-wrap 이 긴 URL 처럼 한 덩어리가 화면을 넘칠 때만 예외적으로 끊어줍니다. 두 줄이지만 읽는 느낌이 확 달라집니다.

폰트는 Pretendard 를 동적 서브셋으로 self-host 했습니다. 92개 파일로 쪼개져 있고 각각 unicode-range 가 붙어 있어서, 브라우저가 실제로 쓰는 글자 범위만 내려받습니다.

배포

GitHub 저장소를 Pages 프로젝트에 연결해두면 main 에 푸시할 때마다 자동으로 빌드·배포되고, 다른 브랜치에는 프리뷰 URL 이 생깁니다. 빌드 설정은 두 줄이면 끝입니다.

Build command: npm run build
Build output directory: dist

로컬에서 배포 결과를 미리 확인하려면 npm run preview 대신 아래를 씁니다. 404 처리처럼 Pages 런타임에서만 재현되는 동작이 있기 때문입니다.

npm run build
npx wrangler pages dev ./dist