EXECUTIVE SUMMARY

핵심 요약

  • 이 블로그는 원래 푸쉬 한 번에 글 한 편이 자동 발행되는 구조였는데, 푸쉬가 잦은 날엔 블로그가 커밋 로그를 그대로 옮겨 적은 것과 다를 게 없어졌다.
  • 그래서 기본 동작을 바꿨다. 커밋은 조용히 쌓이고, 매일 밤 22시에 하루치 전체를 모아 다이제스트 한 편만 발행한다.
  • 즉시 알려야 할 큰 변경을 위한 예외로 커밋 메시지에 `[blog now]`를 넣으면 그 푸쉬만 바로 글이 되도록 남겨뒀다.
  • 발행 전 검수 게이트와 날짜 기반 멱등성 키는 다이제스트에도 그대로 적용되어, 같은 날 두 번 발행되는 일은 구조적으로 막혀 있다.

푸쉬마다 글 한 편은 푸쉬가 드물 때나 맞는 설계였다

처음 이 파이프라인을 만들 때는 전제가 단순했다. 하루에 커밋이 몇 개 안 되니, 푸쉬할 때마다 그 내용을 글로 옮기면 자연스러운 개발 일지가 될 거라고 생각했다. 나는 이 개인 사이트에서 코드와 자동화로 이런저런 것들을 만들고 그 과정을 기록하는 사람인데, 이 파이프라인 자체도 그 기록의 일부다.

그런데 실제로 작업하는 날은 하루에 수십 번 푸쉬가 나가는 경우가 흔했다. 그러면 블로그에는 하루에도 수십 편의 글이 쌓이고, 각 글은 커밋 메시지를 조금 풀어 쓴 수준에 머물렀다. 문제는 양이 아니라 방향이었다. 독자에게 필요한 건 오늘 무슨 일이 몇 번 일어났는지가 아니라, 그 중에 무엇이 중요했는지다. 그걸 골라주는 사람이 없으면 블로그는 그냥 커밋 로그의 복사본이 된다.

그래서 기본값을 뒤집었다, 선택이 곧 편집이라는 전제로

이번 변경의 핵심은 발행 주기를 바꾼 것 자체보다, 발행 판단의 기준을 바꾼 것에 가깝다. 이제 푸쉬는 즉시 글이 되지 않고 조용히 쌓인다. 그리고 매일 밤 22시에 스케줄러가 그날 하루치 커밋 전체를 모아서 한 번에 넘긴다.

이 스케줄과 함께, 기존에 푸쉬마다 돌던 워크플로우의 조건도 뒤집었다. 원래는 `[skip blog]`가 없으면 무조건 글이 되는 방식이었는데, 이제는 반대로 `[blog now]`가 있어야만 즉시 발행된다.

이 한 줄의 방향 전환이 이번 작업의 요지다. 기본값이 반대가 됐다는 건, 판단의 기본 태도가 반대가 됐다는 뜻이다. 예전에는 "따로 막지 않는 한 다 글로 쓴다"였고, 지금은 "따로 표시하지 않는 한 하루치를 모아서 정리한다"가 됐다.

큰 변경을 즉시 알리고 싶을 때를 위한 탈출구는 남겨뒀다. 커밋 메시지에 `[blog now]`를 넣으면 그 푸쉬만 다이제스트를 기다리지 않고 바로 한 편이 된다. 모든 걸 다이제스트로 묶어버리면 정말 중요한 소식도 밤까지 묻혀버릴 수 있어서, 즉시성이 필요한 경우의 문은 열어뒀다.

다이제스트를 만드는 스크립트에는 AI에게 커밋을 나열하지 말고 가장 중요한 흐름 한두 개를 골라 이야기의 중심에 놓으라고 명시했다. 무엇이 중요한지 판단하는 기준도 함께 적었다. 왜 했는지가 분명한 것, 시스템의 동작이 바뀐 것, 배운 점이 있는 것. 나머지 자잘한 변경은 한 문단으로 묶으라고 지시했다. 결국 자동화를 만든다는 건 코드를 짜는 일이면서 동시에 무엇을 남기고 무엇을 버릴지 기준을 세우는 일이기도 하다.

on:
  schedule:
    - cron: "0 13 * * *"
  workflow_dispatch:
jobs:
  create-devlog:
-    if: "!contains(github.event.head_commit.message, '[skip blog]')"
+    if: "contains(github.event.head_commit.message, '[blog now]')"

발행 주기를 바꿔도 안전장치는 그대로 둬야 한다

발행 방식이 바뀌었다고 해서 검증 절차까지 느슨해지면 곤란하다. 발행 전 검수 게이트, 그러니까 민감정보 검사나 중복 검사, 품질 검사는 다이제스트 경로에도 똑같이 걸리도록 만들었다. 발행 빈도가 달라져도 발행 전에 거쳐야 하는 관문은 동일하게 유지되는 셈이다.

또 하나 신경 쓴 부분은 멱등성이다. 다이제스트의 멱등성 키는 날짜 기반으로 만들어서, 같은 날 다이제스트가 중복으로 만들어지는 일이 구조적으로 발생하지 않는다.

이 방식 덕분에 API 비용도 푸쉬 횟수와 무관하게 하루 한 편분으로 고정된다. 하루에 커밋이 다섯 개든 오십 개든 다이제스트를 생성하는 호출은 한 번이다. 발행 빈도를 줄인 결정이 자연스럽게 비용 구조도 단순하게 만든 셈이다.

const kstDate = new Date().toLocaleDateString("sv-SE", { timeZone: "Asia/Seoul" });
const idempotencyKey = `daily-${kstDate}`;
NEXT STEPS

다음 단계

  • `[blog now]` 예외 경로가 실제로 필요한 순간에 잘 걸리는지, 다음 큰 변경 때 직접 확인해본다.
  • 다이제스트가 여러 흐름 중 하나를 고를 때 실제로 어떤 기준으로 골라내는지, 며칠치 발행 결과를 보며 판단 기준 문구를 다듬을지 검토한다.
  • `[skip blog]` 커밋이 다이제스트 재료에서 제대로 제외되는지 확인한다.