들어가며#
정적 블로그를 운영하다 보면 “예약 발행”과 “빌드 제한”이 동시에 문제가 됩니다. 특히 매번 빌드하는 방식은 Cloudflare Pages 같은 플랫폼에서 빌드 제한에 쉽게 걸릴 수 있어요.
이 글에서는 제가 실제로 운영 중인 방식, GitHub Actions로 3시간마다 퍼블리시 체크를 돌려 예약 발행을 처리하는 방법을 정리합니다. 핵심은 “정해진 시간에만 확인하고, 필요할 때만 퍼블리시한다”는 전략이에요.

이미지: Nanobanana AI로 생성
왜 3시간 주기 체크가 필요했나#
1) 예약 발행을 안정적으로 처리하기 위해#
정적 사이트는 기본적으로 시간이 지나도 자동으로 글이 공개되지 않습니다. 빌드가 다시 돌아야만 게시 상태가 바뀌죠. 그래서 “예약 발행”을 하려면 정해진 시점에 빌드가 트리거되어야 합니다.
2) Cloudflare 빌드 제한을 피하기 위해#
글을 자주 다듬거나 여러 포스트를 준비하다 보면 빌드 횟수가 급격히 증가할 수 있어요. 매 커밋마다 빌드가 도는 구조는 제한을 쉽게 소진합니다. 그래서 저는 3시간 주기로 상태를 확인하고, 필요할 때만 빌드되도록 흐름을 바꿨습니다.

사진: Unsplash의 Matias Argandona
동작 방식 요약#
전체 흐름은 다음과 같습니다.
- 3시간마다 GitHub Actions가 실행 (UTC 기준)
- 저장소에서 예약 발행 대상이 있는지 확인
- 조건을 만족하면 퍼블리시 스크립트 실행
- 변경 사항을 커밋/푸시 → Cloudflare가 빌드
이렇게 하면 “예약 발행 체크”는 주기적으로 일어나지만, 실제 퍼블리시는 조건이 맞을 때만 발생합니다.

이미지: Nanobanana AI로 생성
정적 사이트 빌드에서 공개 여부가 결정되는 순간#
Hugo 같은 정적 사이트는 빌드 시점에 draft와 date를 평가합니다. 즉, 빌드가 돌아가는 순간에 다음 조건을 만족하면 그 글은 공개 페이지로 생성돼요.
draft: false일 것date가 현재 시간보다 과거일 것
그래서 “예약 발행”은 빌드 시점에만 성립합니다. 빌드가 없으면 예약 시간은 지나가도 페이지가 만들어지지 않아요. 결국 예약 발행 = 빌드 타이밍 설계라는 뜻입니다.

이미지: Nanobanana AI로 생성
퍼블리시 조건 설계#
제가 쓰는 방식의 핵심은 “퍼블리시 조건”입니다. 예를 들어:
draft: false로 전환된 글이 있는가?- 예약 시간(
date)이 현재 시간보다 과거인가? - 이미 배포된 커밋인가?
이 조건을 충족할 때만 실제 퍼블리시 스크립트를 돌립니다. 이렇게 하면 헛도는 빌드를 최소화할 수 있어요.
실제 조건 확인은 워크플로우에서 .github/scripts/check-scheduled-posts.sh를 실행해 판단합니다. 이 스크립트가 true를 반환하면 퍼블리시 단계로 넘어가요.
실제 체크 로직 요약 (내 워크플로우 기준)#
스크립트는 예약 글과 새 댓글 두 가지를 확인합니다.
예약 글 체크
draft: false인 글만 대상date가 현재 UTC 시간보다 과거일 것- 최근 6시간 이내에 발행 시간이 도래한 글만 포함 (이미 오래된 글은 제외해서 불필요한 빌드를 막음)
새 댓글 체크(요약) 댓글 갱신을 반영하기 위해 간단한 체크만 수행합니다. 자세한 구조와 운영 방식은 이전 글에서 설명했어요. → 정적 사이트에서 동적 댓글 시스템 만들기: Giscus + GraphQL API
즉, “예약 발행”과 “댓글 반영” 두 조건 중 하나만 충족해도 빌드가 트리거되도록 설계했습니다.
GitHub Actions 스케줄 설정#
GitHub Actions는 schedule로 정기 실행을 지원합니다. 저는 3시간마다 실행하도록 설정했고, 필요할 때 수동으로 돌릴 수 있게 workflow_dispatch도 함께 사용합니다. (UTC 기준)
on:
schedule:
- cron: "0 */3 * * *"
workflow_dispatch:정각 근처에 실행되기 때문에 예약 발행과 잘 맞습니다. 다만 GitHub Actions의 실행 시간은 지연될 수 있으니, 몇 분 정도 여유를 두고 예약 시간 설정을 하는 편이 안전해요. 참고로 위 크론은 UTC 기준이라, 한국 시간(KST)으로는 **3시간 간격(예: 09:00, 12:00, 15:00)**으로 동작합니다.
참고: schedule 워크플로우는 기본 브랜치에 있는 워크플로우 파일로 운영하는 편이 안전합니다.

캡처: scheduled-publish.yml 화면
실제 워크플로우 구성 포인트#
제가 쓰는 워크플로우(scheduled-publish.yml)는 크게 네 단계입니다.
- 체크아웃 + 원격 동기화
- 예약 발행 조건 검사 (
check-scheduled-posts.sh) - 조건 충족 시 빈 커밋으로 빌드 트리거
- 요약 리포트 출력
아래는 핵심 구조만 요약한 부분입니다.
jobs:
check-and-publish:
steps:
- uses: actions/checkout@v4
- run: git fetch origin main && git pull --rebase origin main
- run: .github/scripts/check-scheduled-posts.sh
- if: steps.check.outputs.should_build == 'true'
run: |
git commit --allow-empty -m "chore: trigger build for scheduled posts"
git push이 구성 덕분에 예약 글이 없을 때는 빌드가 발생하지 않도록 제어할 수 있습니다.
운영하면서 얻은 장점#
1) 예약 발행이 안정적으로 가능해짐#
“정해진 시간마다 체크”하는 구조 덕분에 예약 발행이 자동화됩니다.
2) 빌드 제한 대응#
필요할 때만 퍼블리시 하므로 Cloudflare 빌드 제한을 크게 완화할 수 있었어요.
3) 운영 리듬이 단순해짐#
글을 쓰고 draft: false로 바꾸기만 하면, 다음 3시간 주기에 자동 발행됩니다. 운영 부담이 줄어듭니다. 이런 자동화 접근은 블로그의 다른 기능에도 적용하고 있어요. 다국어 블로그의 언어 전환 UX 개선처럼, 한번 설정해두면 운영 부담 없이 돌아가는 구조를 선호합니다.
GitHub/Cloudflare 무료 사용량 (2026-01 기준)#
운영 전략을 짤 때 무료 사용량을 명확히 알고 있는 것이 가장 중요합니다. 아래는 공식 문서 기준의 핵심 요약이에요.
GitHub Actions (무료 구간 요약 카드)#
- Public 저장소: 기본 GitHub-hosted runner 사용 시 Actions 사용량 무료
- Private 저장소: 요금제별 무료 분/스토리지 제공
- GitHub Free 개인: 2,000분/월, 500MB 스토리지
- 분당 가중치: Linux 1x, Windows 2x, macOS 10x
Public 저장소는 기본 runner 기준 무료지만, larger runner 사용 시 과금될 수 있습니다. 자세한 기준은 아래 참고 링크를 확인하세요.
Cloudflare Pages (무료 구간 요약 카드)#
- 빌드 제한: 월 500회 빌드
- 빌드 타임아웃: 20분
- 동시 빌드: 계정 단위로 집계
Cloudflare Pages의 Free 플랜 제한은 공식 Limits 문서를 기준으로 정리했습니다.
이 글의 핵심 전략은 “필요할 때만 빌드”하도록 해 500회 빌드 제한을 지키는 것입니다.
참고 링크 (공식 문서)#
[1] GitHub Actions billing and usage (public repo 무료, 요금제별 무료 분/스토리지, larger runner 과금)
https://docs.github.com/en/actions/concepts/billing-and-usage
[2] GitHub Actions product billing (플랜별 무료 분/스토리지 표)
https://docs.github.com/en/enterprise-cloud@latest/billing/concepts/product-billing/github-actions
[3] Cloudflare Pages limits (Free 플랜 빌드 500회, 20분 타임아웃, 동시 빌드 계정 단위 집계)
https://developers.cloudflare.com/pages/platform/limits/예약 발행 팁: “정각 지정”은 지연을 부를 수 있어요#
GitHub Actions는 스케줄 기반이지만 몇 분 지연이 발생할 수 있습니다. 특히 예약 시간을 정각에 맞춰두면, 스케줄이 이미 지나간 뒤에 실행되어 다음 3시간 주기에 빌드될 수도 있어요.
제가 쓰는 해결 방법은 다음과 같습니다.
예약 시간을 정각보다 5~10분 앞당겨 설정 예: 12:00 공개를 원하면
date: 11:50처럼 약간 앞당겨 둡니다.필요할 때는
workflow_dispatch로 수동 실행 정각에 맞춰야 하는 경우, 예약 시간 직후 수동 트리거로 즉시 빌드를 돌립니다.(선택) 스케줄을 더 촘촘하게 설정 빌드 제한 여유가 있다면 3시간 → 1시간으로 줄이는 것도 가능합니다. 다만 이 경우 빌드 횟수와 비용을 다시 계산해야 합니다.
예약 발행은 “정확한 시각”보다 안정적인 공개가 중요하다는 점을 기준으로 설계하는 편이 현실적이었습니다.
Cloudflare 빌드 트리거 방식#
예약 발행이 필요한 경우, 워크플로우는 빈 커밋(empty commit) 을 만들어 Cloudflare Pages 빌드를 트리거합니다. 실제 변경이 없더라도 빌드가 일어나므로 예약 발행에 적합해요.
git commit --allow-empty -m "chore: trigger build for scheduled posts"
git push저장소에 기록이 남기 때문에, 빌드가 언제 왜 트리거되었는지 추적하기도 쉽습니다.
무료 한도 초과를 피하는 운영 전략#
GitHub Actions 쪽 전략#
Linux runner 고정 macOS/Windows는 분당 가중치가 커서 무료 분을 빠르게 소진합니다. 가능하면 Linux로 고정하는 편이 안전합니다.
스케줄 최소화 + 조건부 실행 지금처럼 “조건 만족 시에만 실행”하도록 하면 분 소비가 크게 줄어듭니다.
불필요한 아티팩트/캐시 줄이기 저장소 사용량도 무료 한도에 포함되므로, 필요 이상으로 아티팩트를 보관하지 않습니다.
Cloudflare Pages 쪽 전략#
빌드 트리거를 한 곳으로 모으기 배포 훅/빈 커밋 등 트리거 방식을 하나로 통일하면 중복 빌드를 막을 수 있습니다.
예약 발행 주기와 빌드 주기 정렬 예약 발행 시간은 스케줄 주기에 맞춰 배치해 “한 번의 빌드에 여러 글이 포함”되도록 조정합니다.
빌드 실패 방지 빌드 타임아웃(20분)을 넘지 않도록 이미지 최적화/캐시를 사전에 관리합니다.
Cloudflare API 키(토큰) 연동 방식#
현재 워크플로우는 빈 커밋으로 Cloudflare Pages 빌드를 트리거하는 구조라서, Cloudflare API 키가 필수는 아닙니다. 다만 다음과 같은 경우에는 API 토큰이나 Deploy Hook을 사용하는 편이 더 명확합니다.
- GitHub 커밋 없이 외부 이벤트로 빌드를 트리거하고 싶을 때
- 빌드 트리거를 정교하게 제어하고 싶을 때
방법 A) Deploy Hook 사용 (가장 단순)#
Cloudflare Pages의 Deploy Hook은 URL 한 번 호출로 빌드를 트리거합니다. 별도의 인증이 없기 때문에, URL 자체를 비밀로 관리해야 합니다. 따라서 이 URL을 GitHub Secrets에 저장해 사용하는 방식이 안전합니다.
curl -X POST "$CLOUDFLARE_DEPLOY_HOOK_URL"방법 B) Cloudflare Pages API 사용 (토큰 기반)#
Pages API를 사용하려면 API Token을 만들고, Cloudflare Pages 권한을 Edit 수준으로 부여합니다. 이 토큰은 GitHub Secrets에 저장합니다.
예시 (Secrets 이름):
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_IDCLOUDFLARE_PROJECT_NAME
curl "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/pages/projects/$CLOUDFLARE_PROJECT_NAME/deployments" \\
--request GET \\
--header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
캡처: Cloudflare API tokens
GitHub Secrets에 키 저장하기#
GitHub Actions에서는 Secrets에만 키를 저장하고, 워크플로우에서 환경 변수로 사용합니다. UI 또는 gh CLI로 등록할 수 있어요.
gh secret set CLOUDFLARE_API_TOKEN
캡처: GitHub Actions secrets
주의: 토큰/훅 URL은 로그에 노출되지 않도록 절대 echo로 출력하지 않습니다. Secrets는 기본적으로 마스킹되지만, 직접 출력하면 사고가 날 수 있어요.
추가로 생각하는 개선 방향#
현재는 “퍼블리시 체크”만 자동화되어 있습니다. 앞으로는 다음도 고려하고 있어요.
- 배포 전 자동 검증 (링크, alt 텍스트, 메타데이터 길이) — 코드 블록 접근성 개선에서 경험한 것처럼, 수동 점검에 의존하면 놓치는 부분이 생기거든요.
- 실패 알림 (Slack/Discord 알림)
정리하며#
GitHub Actions로 3시간마다 퍼블리시 체크를 돌리는 방식은 정적 블로그의 예약 발행 문제를 실무적으로 해결해줍니다. 동시에 Cloudflare 빌드 제한 같은 운영 이슈도 함께 대응할 수 있어요.
정적 사이트 운영에서 “자동화”는 선택이 아니라 생존 전략이더라고요. 비슷한 고민을 하고 있다면, 이 방식이 도움이 될 수 있습니다.
