콘텐츠로 이동
Study NoteStarlight

Cloudflare Pages로 배포하기

결론부터
  • 이 저장소는 Cloudflare Pages의 Git 연동으로 main 푸시 후 자동 배포하는 구성을 사용한다.
  • 빌드 명령은 npm run build, 출력 폴더는 dist다. Node 버전과 D2 준비는 저장소 파일에서 관리한다.
  • 배포 성공 뒤에는 대표 주소·본문·검색·수정일을 확인한다. 실제 운영 값은 배포 운영 문서가 정본이다.

세 호스팅 중 이 저장소가 실제로 운영해 본 유일한 경로다. 이 페이지는 대시보드에서 무엇을 넣고, 저장소가 이미 무엇을 대신 처리하는가에 답한다. 원본 사이트의 도메인·계정 값은 배포 운영 문서에 있고, 다른 저장소가 그 값을 복제하면 안 된다. 웹으로 공유하기에서 로컬 확인과 공개 배포의 차이를 먼저 볼 수 있다.

이 장에서 처음 나오는 말3개
Cloudflare Pages
Git 저장소를 연결하면 빌드해서 정적 파일을 제공하는 Cloudflare의 호스팅. 브랜치마다 미리보기 주소가 생긴다.
build cache
이전 빌드의 node_modules/.astro 등을 다음 빌드에 재사용하는 대시보드 기능. 성능 최적화이지 성공 조건이 아니다.
Workers Static Assets
Cloudflare Workers에 정적 파일을 얹어 제공하는 방식. Cloudflare가 새 프로젝트에 권하는 후속 경로다.

Pages의 Git 연동을 대시보드에서 연결하고 GitHub의 저장소 접근을 승인한다. 이미 연결했다면 빌드 설정부터 확인한다.

  1. Cloudflare 대시보드 → Workers & Pages → Create application → Pages → Connect to Git.

  2. GitHub 로그인 뒤 저장소를 고르고 Begin setup.

  3. 빌드 설정을 넣는다. 환경 변수는 필요 없다.

    항목값
    Framework presetAstro
    Build commandnpm run build
    Build output directorydist
    Production branchmain
  4. Save and Deploy. 끝나면 <프로젝트>.pages.dev 주소가 생기고, main 푸시는 프로덕션, 다른 브랜치는 Preview 대상 설정에 따라 미리보기로 배포된다.

Cloudflare 빌드 이미지 문서에 따르면 v3 이미지는 Node와 패키지 관리자 버전을 package.json의 engines에서 읽지 않는다. 대신 Node는 .nvmrc를 읽고, 저장소에 24가 들어 있다. npm은 그 Node에 딸려 오므로 따로 정할 것이 없다. package-lock.json은 npm 의존성 버전의 기준이며, 설치 결과는 첫 빌드 로그에서 확인한다.

Cloudflare Pages는 빌드 환경에 CF_PAGES=1을 넣는다. Git 이력 준비 스크립트는 이 값을 조건으로 동작하고, D2 준비는 모든 빌드 환경에 공통으로 적용된다.

스크립트언제하는 일
scripts/prepare-git-history.mjsnpm run build의 prebuildCF_PAGES=1이고 얕은 checkout이면 git fetch --unshallow로 전체 이력을 받는다. 랜딩의 “마지막 수정”이 맞으려면 필요하다
scripts/prepare-d2.mjsastro.config.mjs 로드 시고정 버전 D2 바이너리를 node_modules/.astro/d2/에 내려받고 검증한다. build cache가 켜져 있으면 다음 빌드에서 다운로드를 건너뛴다

그래서 대시보드에 D2 설치 명령이나 이력 관련 설정을 따로 넣지 않는다. 이력 fetch가 실패하면 잘못된 수정일을 내보내지 않도록 빌드도 실패한다. 다른 호스팅에서는 CF_PAGES가 없으므로 첫 스크립트가 아무 일도 하지 않는다. 그 차이는 GitHub Pages와 Vercel 페이지에서 각각 다뤘다.

Custom domains 탭에서 도메인을 붙이면 pages.dev 기본 주소와 둘 다 살아 있다. src/config/site.mjs의 site를 대표 주소로 두면 두 주소 모두 canonical이 그쪽을 가리켜 대표 URL을 알린다. canonical은 HTTP 리다이렉트나 검색엔진의 색인 결과를 보장하는 설정은 아니다.

터미널 창
curl -s 'https://YOUR-DOMAIN.example/' | grep -o '<link rel="canonical"[^>]*>'
curl -s 'https://YOUR-DOMAIN.example/sitemap-0.xml' | grep -o '<loc>[^<]*</loc>' | head -3

YOUR-DOMAIN.example은 실제 대표 도메인으로 바꾼다. 공개 주소에서 수정한 본문과 D2 이미지가 보이고, 전역 검색이 동작하며, 덱별 마지막 수정일이 Git 이력과 맞는지도 확인한다.

로컬 산출물을 직접 올릴 수 있다. wrangler 설치는 공식 문서를 따른다.

터미널 창
npm run build
npx wrangler pages deploy dist --project-name=YOUR-PROJECT

YOUR-PROJECT는 실제 Pages 프로젝트 이름이다. 로컬 checkout에 전체 Git 이력이 있어야 수정일도 정확하다. 이 명령 자체가 Git 푸시 자동 배포를 설정하지는 않는다.

Astro 공식 Cloudflare 안내는 Cloudflare가 새 프로젝트에 Pages 대신 Workers를 권한다고 적고 있다. 정적 사이트는 어댑터 없이 wrangler.jsonc에 산출물 위치만 알려 주면 된다.

wrangler.jsonc — 정적 사이트 최소 예
{
"name": "study-notes",
"compatibility_date": "2026-09-17",
"assets": { "directory": "./dist" }
}

이 저장소를 Workers로 옮길 때는 Git 이력 준비도 점검한다. 현재 스크립트는 CF_PAGES=1을 조건으로 삼으므로 Workers의 빌드 환경에서 그대로 실행된다고 가정하면 안 된다. 저장소의 운영 기록은 Pages를 기준으로 하며 Workers 이전은 검증하지 않았다.

  • 빌드 이미지의 기본 Node 버전은 바뀔 수 있다. 이 저장소는 .nvmrc로 24를 지정한다.
  • cold build는 GitHub release에서 D2를 내려받아야 하므로 외부 네트워크가 막히면 실패한다.
  • Git 연동에 쓰는 GitHub 앱 권한은 저장소 단위로 준다. 조직 저장소는 조직 관리자의 승인이 필요할 수 있다.

배포는 되는데 랜딩의 모든 덱이 같은 날짜로 보인다. 무엇을 의심할까? prebuild가 전체 이력을 못 받은 경우다. 빌드 로그에서 “얕은 Git 이력을 전체 이력으로 확장합니다” 줄이 있는지 보고, 없다면 이미 전체 이력이 있는지, CF_PAGES=1인지, Build command가 astro build로 바뀌어 prebuild를 건너뛰었는지 차례로 확인한다. 그 로그가 없다는 이유만으로 실패를 단정하지 않는다.