
WordPress 호스팅 비용 월 15달러를 0원으로 줄이면서 콘텐츠 자동화까지 유지하는 게 가능합니다. 저도 처음엔 반신반의했는데, GitHub Actions와 Hugo, Cloudflare Pages를 조합하고 나서 생각이 완전히 바뀌었습니다. 직접 파이프라인을 구성해보니 예상보다 실용적이었고, 동시에 예상치 못한 함정도 꽤 있었습니다.
스케줄 워크플로우로 매일 포스팅 자동화하기
파이프라인의 출발점은 on.schedule 트리거입니다. 여기서 on.schedule이란 GitHub Actions 워크플로우가 특정 시간에 자동으로 실행되도록 예약하는 설정으로, 리눅스의 cron 표현식과 동일한 문법을 사용합니다. 저는 .github/workflows/generate-post.yml 파일에 0 0 * * *(UTC 기준 자정, 즉 KST 오전 9시)을 지정해두고 매일 Python 스크립트가 OpenAI API를 호출해 마크다운 파일을 생성하도록 구성했습니다. 전체 실행 시간은 약 2분 안팎입니다.
그런데 막상 운영해보니 가장 골치 아픈 부분은 따로 있었습니다. GitHub Actions 러너의 스테이트리스(stateless) 특성 때문입니다. 스테이트리스란 워크플로우가 실행될 때마다 완전히 새로운 가상 환경이 생성되고, 이전 실행에서 만들어진 파일이나 상태가 전혀 남지 않는다는 의미입니다. 쉽게 말해 매번 초기화된 컴퓨터에서 작업을 시작하는 것과 같습니다. 이 때문에 "오늘 어떤 주제로 포스팅했는지"를 러너 자체에서 기억할 방법이 없습니다.
저는 이 문제를 해결하기 위해 이미 발행된 포스트 목록을 JSON 파일로 리포지토리에 커밋해두고, 다음 실행 시 이를 읽어 중복 여부를 체크하는 방식을 택했습니다. 아티팩트(Artifact) 업로드 방식도 검토했는데, 아티팩트란 워크플로우 실행 결과물을 GitHub 서버에 임시 저장하는 기능으로 기본 90일간 보관됩니다. 다만 이 방식은 워크플로우 간 참조 구조가 복잡해져서 JSON 커밋 방식이 더 단순하다고 판단했습니다. JSON 파일이 지속적으로 커지지 않도록 오래된 항목을 주기적으로 정리하는 별도의 유지보수 워크플로우도 함께 만들어뒀습니다.
이 아키텍처에 대해 "CI/CD를 콘텐츠 생성에 쓰는 건 도구를 오용하는 것"이라고 보는 시각도 있습니다. CI/CD란 소프트웨어를 자동으로 빌드·테스트·배포하는 개발 방법론인데, 원래 취지와는 다른 용도로 쓰이는 것은 사실입니다. 다만 저는 버전 관리, 자동 링크 체크, 롤백 기능 같은 이점이 전통적인 CMS에서는 구현하기 어려운 장점이라 이 선택이 실용적으로 맞다고 봅니다.
시크릿 관리, 생각보다 간단한데 함정이 있습니다
GitHub Secrets는 설정 자체는 단순합니다. 리포지토리 Settings의 Secrets and Variables 메뉴에 OPENAI_API_KEY를 등록하면, 워크플로우 YAML에서 ${{ secrets.OPENAI_API_KEY }} 형태로 환경 변수로 주입할 수 있습니다. 이 값은 워크플로우 실행 로그에도 마스킹 처리되어 노출되지 않습니다(출처: GitHub Docs).
그런데 제가 초반에 꽤 시간을 날린 지점이 있습니다. 포크(fork)된 리포지토리에서는 시크릿이 워크플로우에 전달되지 않는다는 사실을 몰랐던 겁니다. 테스트 목적으로 리포지토리를 포크해서 워크플로우를 돌렸는데, API 키가 undefined로 찍히면서 계속 실패했습니다. 원인을 찾는 데 한참 걸렸고, 이후로는 로컬에서 act(GitHub Actions 로컬 실행 도구)를 사용해 사전 검증하는 습관이 생겼습니다.
보안 관리에서 한 가지 더 챙겨야 할 부분은 OIDC(OpenID Connect) 기반 인증입니다. OIDC란 워크플로우 실행 시마다 단기 토큰을 자동 발급받아 인증하는 방식으로, API 키를 장기간 저장하지 않아도 되는 구조입니다. 시크릿 로테이션 자동화를 고려하는 분들에게는 HashiCorp Vault와 OIDC를 연동하는 방식도 검토할 만합니다. 제 경우 현재는 GitHub Secrets 수동 관리로도 충분하다고 판단해 적용하지 않았지만, 상업적 규모로 확장한다면 이 부분은 반드시 다시 검토할 것 같습니다.
한 가지 솔직하게 짚고 넘어가야 할 부분이 있습니다. GitHub Actions의 무료 티어는 월 2,000분이며, 이 파이프라인을 콘텐츠 생성 목적으로 대규모로 활용하는 것은 GitHub 서비스 약관의 회색지대에 해당할 수 있습니다. 실제로 반복적인 외부 API 호출을 수행하는 워크플로우에 대해 계정이 정지된 사례가 일부 보고된 바 있습니다. 상업적 목적이라면 유료 플랜을 사용하는 편이 안전합니다.
배포 속도, Cloudflare Pages Deploy Hook이 바꿔놓은 것
정적 사이트 생성기(SSG)인 Hugo를 사용하면 빌드 결과물은 순수한 HTML·CSS·JS 파일입니다. SSG란 콘텐츠를 미리 HTML로 변환해두는 방식으로, 사용자 요청 시 서버에서 동적으로 페이지를 생성하는 WordPress와 달리 별도의 서버 자원이 필요 없습니다. 이 파일들을 gh-pages 브랜치에 커밋하면 GitHub Pages가 자동으로 서빙합니다.
처음에는 이 방식으로만 운영했는데, 배포까지 평균 3~5분이 걸리는 게 은근히 불편했습니다. 포스팅이 올라갔는지 확인하려면 페이지를 새로고침하면서 기다려야 했으니까요. Cloudflare Pages의 Deploy Hook을 연동한 이후로는 이 문제가 해결됐습니다. Deploy Hook이란 특정 URL로 HTTP 요청을 보내는 것만으로 Cloudflare 엣지 네트워크 전체에 새 빌드를 즉시 배포하도록 트리거하는 기능입니다. Hugo 빌드가 끝난 직후 curl 한 줄로 Hook URL을 호출하면 약 20~30초 만에 전 세계 엣지에 반영됩니다(출처: Cloudflare Developers).이 아키텍처의 장점을 정리하면 다음과 같습니다.
- 호스팅 비용 완전 제거 (월 15달러 → 0원)
- CDN(콘텐츠 전송 네트워크) 캐시 무효화 자동 처리로 독자 측 캐시 지연 없음
- 버전 관리 및 롤백이 git 명령어 하나로 가능
- 링크 유효성 검사, 맞춤법 검사 등 콘텐츠 품질 검증을 워크플로우에 통합 가능
다만 정적 사이트 방식에는 분명한 한계도 있습니다. 댓글, 실시간 검색, 사용자 맞춤 추천 같은 동적 기능은 Disqus, Algolia 같은 외부 서비스와 별도로 연동해야 합니다. 독자와의 상호작용보다 정보 전달 자체에 집중하는 블로그라면 이 구조가 충분하지만, 커뮤니티 형성을 목표로 한다면 처음부터 이 한계를 인지하고 시작하는 편이 좋습니다.
이 파이프라인을 구성하면서 가장 크게 느낀 것은, CI/CD 개념을 소프트웨어가 아닌 콘텐츠에 적용하는 발상이 단순한 호기심 수준을 넘어 실질적인 운영 이점을 준다는 점입니다. 비용 절감이 출발점이었지만, 결과적으로 얻은 건 더 많았습니다. 다만 이 복잡성이 정말 필요한지는 본인의 블로그 목적과 규모에 따라 다르므로, 더 단순한 도구로 같은 목적을 달성할 수 있는지 먼저 검토해보시길 권합니다. 직접 구성해보고 싶다면 Hugo 공식 Quick Start와 GitHub Actions Workflow Syntax 문서부터 시작하는 것이 가장 빠릅니다.
참고:
[1] GitHub Actions — Workflow Syntax for GitHub Actions https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions
[2] Hugo Documentation — Quick Start https://gohugo.io/getting-started/quick-start/
[3] peaceiris/actions-gh-pages — GitHub Actions for GitHub Pages https://github.com/peaceiris/actions-gh-pages
[4] Cloudflare Pages — Deploy Hooks Documentation https://developers.cloudflare.com/pages/configuration/deploy-hooks/
[5] GitHub Docs — Using secrets in GitHub Actions https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions