블로그 자동 발행을 launchd로 무인화한 기록 — 게이트·빌드·푸시를 하나로
자체 블로그는 git push만으로 Cloudflare Pages에 배포돼요. 그 점을 이용해 거짓정보 게이트 → 빌드 → 커밋 → 푸시를 매일 자동으로 돌리는 발행 파이프라인을 launchd로 묶은 과정을 정리했어요.
이 블로그는 글 파일을 저장소에 커밋하고 푸시하면 Cloudflare Pages가 받아서 빌드·배포해요. 그래서 "발행"이라는 동작이 사실상 git push 한 번이에요. 이 점을 이용해 매일 글을 자동으로 올리는 파이프라인을 만들었어요.
파이프라인 단계
발행을 네 단계로 나눴어요.
- 거짓정보 게이트 — 초안에 사실이 아닌 내용이 있는지 점검해요. 통과한 글만 다음 단계로 가요.
- 빌드 —
pnpm build로 정적 페이지를 생성해요. 여기서 frontmatter 검증이나 타입 오류가 있으면 멈춰요. - 커밋 — 통과·빌드된 글만 저장소에 커밋해요.
- 푸시 —
git push로 Cloudflare Pages 배포를 트리거해요.
fail-closed 원칙
핵심은 "막히면 발행하지 않는다"예요. 게이트에서 사실 검증을 통과하지 못한 글은 사이트로 나가지 않고 quarantine/ 폴더로 따로 빼둬요. 삭제가 아니라 격리라서, 나중에 사람이 직접 보고 판단할 수 있어요.
# 게이트 실패 시: 발행 중단 + 격리 (배포 트리거 없음)
if ! blog-fact-gate.sh "$draft"; then
mv "$draft" "$QUARANTINE_DIR/"
exit 1 # push 단계까지 가지 않음
fi"실수로 잘못된 글이 공개되는 것"보다 "발행이 한 번 안 되는 것"이 회복하기 쉬우니까요.
왜 launchd인가
제 작업 환경이 macOS라서, 정해진 시각에 스크립트를 돌리는 일은 launchd로 맡겼어요. StartCalendarInterval에 시각을 넣으면 그 시간에 발행 스크립트를 실행해요. 노트북이 꺼져 있던 시간대의 작업은 다음에 깨어났을 때 실행되도록 동작해요.
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key><integer>9</integer>
<key>Minute</key><integer>0</integer>
</dict>발행 후 확인
스크립트가 끝났다고 글이 실제로 보이는 건 아니에요. 배포에는 시간이 걸리고, 빌드가 성공했는데도 페이지가 비어 있을 수 있어요. 그래서 마지막에 실제 글 주소가 200으로 응답하는지, 본문이 렌더되는지까지 확인하는 단계를 넣었어요. 작업 상태가 "성공"이라고 떠도 실제 화면을 한 번 더 보는 게 안전하더라고요.
다음 글에서는 이 게이트가 거짓정보를 어떻게 거르는지 더 자세히 다뤄볼게요.