GitHub Actions + Cloudflare Pages 자동 배포 — Astro 블로그 CI/CD

Astro 블로그를 GitHub Actions와 Cloudflare Pages로 자동 배포하면서 만난 삽질들. Wrangler v3 설정부터 pnpm 캐시, Secrets 관리, 배포 실패 디버깅까지 실전 트러블슈팅.


💡 Tip. 바쁜 현대인들을 위한 본문 요약

  • GitHub Actions + Cloudflare Pages 조합으로 git push만 하면 빌드→배포가 자동으로 돌아간다
  • Cloudflare Pages의 자체 빌드보다 GitHub Actions에서 빌드 후 Wrangler로 배포하는 게 빠르고 제어가 쉽다
  • pnpm/action-setupactions/setup-nodecache 옵션을 세팅해야 빌드 시간이 반으로 줄어든다
  • Secrets(CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID)는 Repository Secrets에 넣고 workflow에서 참조한다
  • 빌드 환경변수(PUBLIC_GA_ID 등)도 Secrets로 관리하면 로컬 .env와 CI 환경이 분리된다

블로그를 WordPress에서 Astro로 옮겼다. 로컬에서 pnpm build하고 수동으로 올리면 되긴 한다.

근데 그걸 매번 하고 싶진 않다.

🔧 환경


📌 증상: 수동 배포의 고통

분명 잘 되던 건데, 📌 증상: 수동 배포의 고통에서 갑자기 안 될 때
분명 잘 되던 건데, 📌 증상: 수동 배포의 고통에서 갑자기 안 될 때

블로그에 글 하나 올리려면:

pnpm build          # 빌드
# dist/ 폴더를 어딘가에 업로드...

Cloudflare Pages 대시보드에서 직접 업로드할 수도 있다. 하지만 매번 빌드하고 드래그앤드롭하는 건 2026년에 할 짓이 아니다.

게다가 Cloudflare Pages의 자체 빌드 기능은 있지만:

  • 빌드 환경 커스터마이징이 제한적이다
  • pnpm 버전을 정확히 지정하기 까다롭다
  • 빌드 로그가 GitHub Actions에 비해 빈약하다

결론: GitHub Actions에서 빌드하고, Wrangler로 결과물만 던지자.


📌 원인 분석: 뭐가 필요한가

알고 보니 📌 원인 분석: 뭐가 필요한가은 이 한 줄 때문이었다
알고 보니 📌 원인 분석: 뭐가 필요한가은 이 한 줄 때문이었다

자동 배포 파이프라인에 필요한 것:

  1. GitHub Actions workflowmain 브랜치 push 시 트리거
  2. pnpm + Node.js 세팅 — 의존성 설치와 빌드
  3. Cloudflare API Token — Pages 배포 권한
  4. Wrangler CLIpages deploy 명령

📌 해결: 단계별 구축

몇 시간 삽질 끝에 📌 해결: 단계별 구축 해결 완료
몇 시간 삽질 끝에 📌 해결: 단계별 구축 해결 완료

1단계: Cloudflare API Token 발급

Cloudflare 대시보드 → My Profile → API Tokens → Create Token

필요한 권한:

  • Cloudflare Pages — Edit
  • Account — Read (Account ID 조회용)

⚠️ Zone 권한이 아니라 Account 레벨 Pages 권한이다. Zone DNS 토큰과 혼동하지 말 것.

발급받은 토큰과 Account ID를 GitHub Repository Settings → Secrets and variables → Actions에 등록:

Secret 이름
CLOUDFLARE_API_TOKENPages 배포용 API 토큰
CLOUDFLARE_ACCOUNT_IDCloudflare 계정 ID
PUBLIC_GA_IDGoogle Analytics 측정 ID (빌드 시 주입)

2단계: Cloudflare Pages 프로젝트 생성

대시보드에서 Pages 프로젝트를 먼저 만든다:

  • 프로젝트 이름: jongmolee-blog (이후 Wrangler에서 참조)
  • 프로덕션 브랜치: main
  • 빌드 설정: 건너뛴다 (GitHub Actions에서 빌드할 거니까)

커스텀 도메인 연결:

  • jongmolee.com → Pages 프로젝트에 Custom Domain 추가
  • Cloudflare가 자동으로 DNS CNAME을 잡아준다

3단계: GitHub Actions Workflow 작성

.github/workflows/deploy.yml:

name: Deploy to Cloudflare Pages

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      deployments: write
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - run: pnpm install --frozen-lockfile
      - run: pnpm build
        env:
          PUBLIC_GA_ID: ${{ secrets.PUBLIC_GA_ID }}

      - uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy dist --project-name jongmolee-blog --branch main

핵심 포인트:

pnpm/action-setup@v4: pnpm을 먼저 설치해야 actions/setup-nodecache: pnpm이 동작한다. 순서를 바꾸면 캐시가 안 잡힌다.

--frozen-lockfile: lockfile과 package.json이 어긋나면 CI에서 바로 에러를 터뜨린다. 로컬에서 pnpm install 후 lockfile 커밋하는 습관이 필수.

permissions: deployments: write가 있어야 GitHub Deployments 탭에 배포 상태가 기록된다.

cloudflare/wrangler-action@v3: Wrangler를 직접 설치할 필요 없이 Action이 알아서 해준다. commandpages deploy를 넣으면 끝.


📌 삽질 로그

삽질 1: cache: pnpm이 안 먹힌다

Error: Could not determine pnpm store directory

원인: actions/setup-node보다 pnpm/action-setup이 먼저 와야 한다. pnpm이 설치되지 않은 상태에서 cache를 잡으려 하면 store 경로를 못 찾는다.

해결: 위 workflow처럼 pnpm/action-setupactions/setup-node 순서를 지킨다.

삽질 2: 빌드는 되는데 페이지가 안 뜬다

Wrangler가 성공했다고 뜨는데 실제 URL에 접속하면 404.

원인: --project-name이 Cloudflare Pages 대시보드의 프로젝트 이름과 정확히 일치해야 한다. 대소문자, 하이픈 하나라도 다르면 새 프로젝트가 생기거나 404가 뜬다.

해결: 대시보드에서 프로젝트 이름을 복사해서 붙여넣기.

삽질 3: 환경변수가 빌드에 안 들어간다

Astro에서 import.meta.env.PUBLIC_GA_IDundefined.

원인: Cloudflare Pages의 환경변수 설정에 넣어봤자, 우리는 Pages 자체 빌드를 안 쓰고 있다. GitHub Actions에서 빌드하니까 GitHub Secrets에서 env로 넘겨야 한다.

해결: workflow의 pnpm build 스텝에 env를 명시적으로 지정.

- run: pnpm build
  env:
    PUBLIC_GA_ID: ${{ secrets.PUBLIC_GA_ID }}

삽질 4: Wrangler 버전 충돌

Error: Unknown argument: pages

원인: wrangler-action@v2가 Wrangler 2.x를 설치하는데, pages deploy는 Wrangler 3.x 기능이다.

해결: cloudflare/wrangler-action@v3을 쓴다. v3 Action이 Wrangler 3.x를 설치해준다.


📌 예방: 이런 패턴을 쓰자

이런 삽질을 또 하지 않으려면
이런 삽질을 또 하지 않으려면

CI 전 로컬 검증 체크리스트

pnpm install --frozen-lockfile  # lockfile 정합성 확인
pnpm build                      # 빌드 에러 사전 차단

이 두 명령이 로컬에서 통과하면 CI에서도 통과한다.

배포 상태 모니터링

GitHub → Actions 탭에서 배포 이력 확인 가능. 실패하면 이메일 알림이 온다.

Cloudflare Pages 대시보드 → Deployments에서도 배포 이력과 롤백이 가능하다.

Preview 배포 (보너스)

main이 아닌 브랜치에서도 배포하고 싶다면:

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

PR을 올리면 Cloudflare Pages가 Preview URL을 자동 생성한다. 프로덕션 반영 전에 미리 확인 가능.


📌 정리

드디어 📌 정리 문제를 잡았다
드디어 📌 정리 문제를 잡았다

항목내용
구성GitHub Actions → pnpm build → Wrangler pages deploy → Cloudflare Pages
비용전부 무료 (GitHub Actions Free Tier + Cloudflare Pages Free)
빌드 시간캐시 적용 후 약 40~60초
핵심 삽질pnpm/action-setup 순서, project-name 정확히 일치, 환경변수는 GitHub Secrets에서
예방로컬에서 --frozen-lockfile + build 통과 확인 후 push

git push origin main — 이 한 줄이면 블로그가 올라간다. 수동 배포는 이제 역사 속으로.