자동 게시 실패를 운영 신호로 바꾸는 방법
Futory의 Next.js Markdown 블로그 자동 발행 흐름에서 스킵, 검증 실패, 공개 확인 실패를 어떻게 운영 신호로 다루는지 정리했습니다.
요약
AI와 함께 블로그를 운영하면 글 작성부터 Markdown 저장, 빌드, PM2 재시작, 공개 URL 확인까지 한 번에 자동화할 수 있습니다. 하지만 자동화의 목적은 단순히 매일 글을 올리는 데서 끝나지 않습니다. 더 중요한 목표는 실패를 조용히 덮지 않고, 운영자가 이해할 수 있는 신호로 바꾸는 것입니다.
Futory는 Next.js 기반 Markdown 블로그입니다. 글은 content/posts/*.md에 쌓이고, 배포 전에는 콘텐츠 형식 검증과 테마 검증, 빌드 검증을 거친 뒤 PM2와 Nginx 뒤에서 서비스됩니다. 이 구조에서는 “오늘 글이 올라갔는가”만큼 “올라가지 않았다면 왜 올라가지 않았는가”가 중요합니다. 선택된 시간이 아니라서 건너뛴 것인지, 이미 오늘 글이 있어서 중복을 막은 것인지, 검증 실패로 배포를 멈춘 것인지가 분리되어야 합니다.
이 글은 자동 게시 실패를 장애가 아니라 운영 판단에 필요한 데이터로 바꾸는 방법을 Futory 운영 맥락에서 정리한 기록입니다.
자동 게시에서 실패는 세 가지로 나뉜다
자동 발행 흐름을 만들 때 모든 실패를 하나의 오류로 묶으면 운영이 어려워집니다. Futory 같은 블로그 자동화에서는 최소한 세 가지 상태를 구분해야 합니다.
첫째는 정상 스킵입니다. 한국 시간 기준 14시부터 18시 사이에만 랜덤한 한 시간을 골라 게시한다면, 선택되지 않은 시간에 실행된 cron은 아무것도 하지 않는 것이 맞습니다. 이때는 실패가 아니라 “아직 게시 시간이 아니다”라는 운영 상태입니다.
둘째는 중복 방지입니다. 이미 오늘 날짜의 글이 content/posts 안에 있다면 새 글을 만들면 안 됩니다. 매일 한 편씩 쌓는 운영 리듬에서는 같은 날짜에 두 개의 글이 생기는 것이 더 큰 문제입니다. 따라서 이 경우도 배포 실패가 아니라 자동화가 안전장치대로 동작한 결과입니다.
셋째는 실제 실패입니다. 새 글은 작성했지만 npm run test:content가 실패했거나, 다크/라이트 모드 검증인 npm run test:theme이 실패했거나, npm run build가 통과하지 못했다면 배포를 멈춰야 합니다. PM2를 재시작하지 않고 기존 서비스를 그대로 두는 것이 올바른 대응입니다.
Futory의 자동 게시 흐름을 기준으로 보기
Futory의 운영 흐름은 비교적 단순하지만, 각 단계가 서로 다른 위험을 막습니다. 먼저 cron이 실행되면 KST 현재 시간을 계산합니다. 서버가 UTC 기준으로 실행되더라도 블로그 운영 기준은 한국 시간이기 때문에 날짜와 시간이 어긋나지 않아야 합니다.
그다음 오늘 날짜를 기준으로 랜덤 게시 시간을 결정합니다. 중요한 점은 완전한 무작위가 아니라 날짜를 seed로 사용하는 결정적 무작위라는 것입니다. 이렇게 하면 같은 날 여러 번 실행되어도 오늘의 게시 시간은 항상 같습니다. 운영자는 “왜 이번 실행에서는 건너뛰었는가”를 재현할 수 있고, 로그를 봤을 때 같은 날짜의 판단이 흔들리지 않습니다.
선택된 시간이라면 기존 글을 확인합니다. date: "YYYY-MM-DD"가 이미 있으면 중복 게시를 막고 종료합니다. 없다면 기존 글 제목과 주제를 읽어 다음 연재 주제를 정합니다. 현재 전략은 여러 카테고리로 넓히는 것이 아니라 바이브코딩이라는 하나의 카테고리 안에서 AI 개발, 자동화, 배포, 운영을 깊게 쌓는 것입니다.
검증 실패는 배포 중단 신호다
자동 게시에서 가장 위험한 순간은 “글 파일은 추가됐으니 바로 서비스에 반영하자”라고 생각하는 때입니다. Markdown 글 하나가 추가됐더라도 전체 앱 빌드는 실패할 수 있고, 콘텐츠 검증에서 frontmatter 누락이 발견될 수 있으며, 기존 테마 토글이 사라졌을 수도 있습니다.
Futory에서는 새 글을 추가한 뒤 npm run test:theme && npm run test:content && npm run build를 먼저 실행합니다. 이 순서는 단순한 개발 편의가 아니라 운영 기준입니다. 테마 검증은 components/ThemeToggle.tsx, app/theme-init.tsx, CSS의 data-theme 변수처럼 기존 사용자 경험을 지키기 위한 장치입니다. 콘텐츠 검증은 제목, 설명, 날짜, 카테고리, 태그, FAQ 섹션, 최소 분량 같은 글의 기본 품질을 확인합니다. 빌드는 실제 Next.js 앱이 배포 가능한 상태인지 확인합니다.
이 중 하나라도 실패하면 호스트 라이브 경로로 동기화하지 않습니다. PM2도 재시작하지 않습니다. 실패한 배포보다 안전한 미배포가 낫기 때문입니다. 자동화는 빠르게 반영하는 도구이기도 하지만, 위험한 반영을 멈추는 브레이크이기도 합니다.
공개 URL 확인은 마지막 운영 검증이다
빌드가 성공하고 PM2를 재시작했다고 해서 게시가 끝난 것은 아닙니다. Next.js 앱이 내부 포트에서 정상이어도 Nginx 설정, 캐시, 도메인 연결, 프로세스 재기동 지연 때문에 공개 페이지에서 바로 보이지 않을 수 있습니다. 그래서 마지막에는 공개 도메인에서 /posts와 새 글 URL이 HTTP 200으로 응답하는지 확인해야 합니다.
Futory의 경우 공개 블로그는 https://futory.oig.kr입니다. 새 글 slug가 automated-publishing-failure-signals라면 /posts/automated-publishing-failure-signals가 열려야 하고, 글 목록에서도 오늘 날짜가 보여야 합니다. 이 확인이 있어야 “배포 명령을 실행했다”가 아니라 “독자가 실제로 볼 수 있다”라고 말할 수 있습니다.
운영 자동화에서는 이 차이가 큽니다. 서버 내부 작업이 성공한 것과 사용자에게 보이는 결과가 성공한 것은 다릅니다. 자동화가 마지막 공개 확인까지 수행하면 보고 내용도 더 명확해집니다.
스킵 로그도 콘텐츠 운영에 도움이 된다
매일 실행되는 cron에서 스킵 로그는 소음처럼 보일 수 있습니다. 하지만 잘 설계된 스킵 메시지는 운영 상태를 빠르게 알려줍니다. 예를 들어 선택된 시간이 아니라면 SKIP: not selected hour, 이미 오늘 글이 있다면 SKIP: already published today처럼 짧고 분명한 메시지가 좋습니다.
이런 메시지는 나중에 자동화 기록을 볼 때 유용합니다. “왜 오늘 15시에 글이 안 올라갔지?”라는 질문이 생겼을 때, 오늘 선택 시간이 18시였다는 로그가 있으면 문제가 아니라 일정 설계였음을 알 수 있습니다. 반대로 선택된 시간인데도 검증 실패가 있었다면 그 로그는 수정해야 할 작업으로 이어집니다.
즉, 좋은 자동화는 조용히 성공하는 것뿐 아니라 조용히 건너뛸 때와 크게 멈춰야 할 때를 구분합니다. 이 구분이 쌓이면 블로그 운영자는 불필요한 걱정을 줄이고, 실제로 봐야 할 실패에 집중할 수 있습니다.
운영자가 확인해야 할 신호
자동 게시가 실패했을 때 가장 먼저 볼 것은 실패 지점입니다. 시간 조건에서 멈췄는지, 중복 게시 방지에서 멈췄는지, 콘텐츠 검증에서 멈췄는지, 빌드에서 멈췄는지, 공개 URL 확인에서 멈췄는지를 나누어야 합니다.
콘텐츠 검증 실패라면 새 글의 frontmatter와 본문 구조를 확인합니다. 날짜가 오늘 KST 기준인지, 카테고리가 바이브코딩인지, FAQ와 결론이 들어갔는지 보는 식입니다. 테마 검증 실패라면 글과 상관없어 보이는 UI 파일이 스테이징에서 누락되지 않았는지 확인합니다. 빌드 실패라면 Next.js의 에러 메시지를 보고 import, 타입, 환경 변수 문제를 나눠 봅니다.
공개 확인 실패라면 내부 앱과 외부 도메인을 분리해서 봐야 합니다. PM2 프로세스가 살아 있는지, 내부 포트가 응답하는지, Nginx가 올바르게 프록시하는지, 도메인에서 새 페이지가 200을 반환하는지 순서대로 확인하면 원인을 좁히기 쉽습니다.
자주 묻는 질문
자동 게시가 건너뛰어지면 문제가 있는 건가요?
항상 그런 것은 아닙니다. 선택된 게시 시간이 아니라면 건너뛰는 것이 정상입니다. 이미 오늘 날짜의 글이 있다면 중복을 막기 위해 건너뛰는 것도 정상입니다. 문제는 선택된 시간이고 오늘 글도 없는데 검증이나 배포 단계에서 실패한 경우입니다.
새 글만 추가했는데 테마 검증이 왜 필요한가요?
자동 배포는 전체 프로젝트를 기준으로 움직입니다. 스테이징과 라이브를 동기화하는 과정에서 기존 파일이 사라지거나 CSS 변수가 바뀔 수 있습니다. Futory는 다크/라이트 모드를 기존 기능으로 유지해야 하므로, 콘텐츠 배포에서도 테마 검증을 함께 실행하는 것이 안전합니다.
PM2 재시작은 언제 해야 하나요?
스테이징 검증과 호스트 검증, 빌드가 모두 통과한 뒤에 해야 합니다. 빌드가 실패한 상태에서 PM2를 재시작하면 기존에 정상 동작하던 서비스까지 불안정해질 수 있습니다. 자동화의 원칙은 실패한 결과를 서비스에 반영하지 않는 것입니다.
공개 URL 확인이 꼭 필요한가요?
필요합니다. 내부 빌드와 프로세스 재시작이 성공해도 실제 독자가 보는 페이지가 실패할 수 있습니다. /posts 목록과 새 글 상세 URL에서 200 응답과 오늘 날짜가 확인되어야 게시 성공이라고 말할 수 있습니다.
결론
자동 게시 운영에서 실패는 모두 같은 실패가 아닙니다. 선택 시간이 아니라서 건너뛴 것, 중복을 막은 것, 검증 실패로 배포를 중단한 것, 공개 URL 확인에 실패한 것은 각각 다른 의미를 가집니다. 이 차이를 구분해야 자동화가 믿을 수 있는 운영 도구가 됩니다.
Futory의 목표는 AI와 함께 만들고, 자동화하고, 운영하는 과정을 기록하는 것입니다. 그래서 블로그 자동 게시도 단순히 글을 생산하는 장치가 아니라 운영 원칙을 실험하는 시스템이어야 합니다. 날짜 기반 랜덤 게시, 중복 방지, 콘텐츠 검증, 테마 검증, 빌드, PM2 재시작, 공개 URL 확인이 하나의 흐름으로 이어질 때 자동화는 속도와 안정성을 함께 제공합니다.