AI로 일하기

발행됐다고 떴는데, 사이트엔 아무것도 없었습니다 🔍

답답해하는 초록이 아빠

이 블로그는 발행 과정 대부분을 자동화해 두었습니다. 초안은 AI와 함께 만들고, 제가 읽고 고친 뒤 승인하면 사이트에 배포되고 네이버에도 예약이 걸려요. 그날 아침도 늘 하던 대로 검토를 마치고 다른 일을 하고 있었습니다.

점심때쯤 사이트를 열어봤더니, 글이 한 편도 없었습니다.

처음엔 다른 곳을 의심했습니다

로그부터 봤습니다. 배포는 “완료”라고 찍혀 있었어요. 그런데 사이트엔 아무것도 없었습니다. 앞뒤가 안 맞았습니다.

처음 의심한 건 iCloud 동기화 지연이었습니다. 초안 파일이 클라우드로 오가다 보니 어딘가에서 못 읽은 거라고 생각했어요. 그런데 아니었습니다. 진짜 원인은 명령어 하나에 실행 경로가 빠져 있던 거였습니다. 무인 실행 환경에는 제가 평소 쓰던 셸 설정이 안 실려서, 빌드 도구를 못 찾은 채 조용히 지나간 겁니다.

같은 날 다른 오진도 하나 더 했습니다. 예전 글의 이미지가 “존재하지 않는다”는 오류가 떴는데, 알고 보니 화면 없이 도는 헤드리스 브라우저에서만 그렇게 보이는 착시였고 실제 기기에서 열면 멀쩡했습니다.

두 번 헛짚고 나서야, 진짜 원인을 찾기 시작했습니다.

로그를 뒤져보니, 진짜 원인은 세 개였습니다 🧩

삽질하다 멘붕한 초록이 아빠

배포가 멈춘 이유가 하나가 아니라 세 개였습니다. 각자 따로 만들어졌는데, 하필 같은 날 한꺼번에 겹쳤어요.

하나, 두 기기가 로그인을 나눠 쓰고 있었습니다

배포를 실행하는 서버와, 제가 평소 코드를 만지는 노트북이 같은 클라우드 계정으로 로그인돼 있었습니다. 편해서 그렇게 해뒀는데, 문제는 한쪽에서 로그인하면 그 순간 다른 쪽 세션이 조용히 무효화된다는 점이었습니다. 로그인 토큰이 새로 발급될 때마다 예전 토큰은 버려지니까요. 제가 노트북에서 뭔가 확인하려고 로그인했던 게, 서버 쪽 배포 권한을 끊어버린 셈이었습니다.

둘, 실패가 로그엔 “완료”로 남고 있었습니다

배포 스크립트는 배포 명령의 출력을 로그 파일에 남기면서, 너무 길다는 이유로 뒤쪽 몇 줄만 잘라 저장하고 있었습니다. 그런데 그 자르는 명령을 파이프로 연결해두니, 스크립트가 확인하는 성공·실패 신호가 배포 명령이 아니라 그 자르는 명령 것으로 바뀌어 있었어요. 배포가 실패해도 자르는 명령 자체는 항상 성공하니까, 로그엔 늘 “완료”로 찍혔던 겁니다. 알림도 안 왔고요.

셋, 저장소가 아니라 계정이 문제였습니다

밀린 커밋을 올리려는데 푸시가 계속 거부됐습니다. 저장소가 사라졌나 싶어 한참 찾았는데, 원인은 그게 아니었습니다. 한 기기에 개인 계정과 회사 계정을 둘 다 등록해뒀는데, 마침 활성 계정이 회사 쪽으로 전환돼 있었어요. 개인 저장소에 권한이 없는 계정으로 밀어 넣으려 했으니, 거부는 당연한 결과였습니다.


자동화 러너의 자격증명은, 사람의 로그인과 같은 서랍에 두면 안 됩니다.

세 가지 원인이 다 다른 곳에서 왔는데, 파고 보면 하나로 모입니다. 사람이 쓰는 로그인 상태와, 무인으로 도는 스크립트가 같은 자격증명을 나눠 쓰고 있었다는 것. 사람은 필요할 때마다 로그인하고 계정을 바꿔가며 씁니다. 그런데 자동화는 그걸 모르고 늘 같은 상태를 기대해요. 둘이 부딪히면 자동화가 집니다.

고치고 나니

세 가지를 각각 떼어냈습니다. 배포 전용 API 토큰을 따로 발급해서 파일로 저장해두고, 사람 로그인과는 별개로 쓰게 했습니다. 종료코드를 삼키던 파이프는 걷어내고, 배포 명령의 결과를 직접 확인하게 바꿨습니다. 실패하면 그 자리에서 알림이 오도록요. 계정 전환 문제는 푸시 직전에 활성 계정부터 확인하는 습관으로 바꿨습니다.

그날 밀렸던 글 두 편을 다시 올렸고, 그 뒤로는 배포가 조용히 실패한 적이 없습니다.

따라 하기

같은 구조로 무인 자동화를 돌리고 계신다면, 아래 세 가지만 먼저 점검해보셔도 원인의 8할은 잡힐 겁니다.

1. 자동화 러너 전용 자격증명 분리하기

사람이 브라우저로 로그인하는 세션과, 무인으로 도는 배포 스크립트가 같은 계정 로그인을 쓰고 있는지부터 봅니다. 같이 쓰고 있다면 배포 전용 API 토큰을 따로 발급받아 파일로 저장하세요.

echo "CLOUDFLARE_API_TOKEN=<발급받은 토큰>" > ~/.config/<러너용-폴더>/deploy.env
chmod 600 ~/.config/<러너용-폴더>/deploy.env

왜 필요한가 — 사람의 로그인은 계속 바뀝니다. 새 기기에서 로그인하거나, 토큰을 갱신하거나. 그때마다 무인 스크립트의 권한이 같이 끊기면 원인 찾기가 훨씬 어려워집니다.

2. 파이프 뒤에서 종료코드가 사라지는지 확인하기

로그를 짧게 자르려고 명령 뒤에 tail이나 grep을 붙이는 경우가 많은데, 그러면 셸이 보는 종료코드는 마지막 명령 것으로 바뀝니다.

wrangler pages deploy ./dist --project-name <프로젝트명>
if [ $? -ne 0 ]; then
  echo "배포 실패"
  exit 1
fi

왜 필요한가 — 스크립트 맨 위에 set -o pipefail 한 줄을 넣어도 같은 효과를 봅니다. 파이프를 쓰고 있다면 둘 중 하나는 꼭 넣어야, 실패가 “성공”으로 둔갑하지 않습니다.

3. push 전에 활성 계정부터 확인하기

한 기기에 계정을 여러 개 등록해뒀다면, 거부당한 이유가 저장소 문제가 아니라 계정 문제일 수 있습니다.

gh auth status
gh auth switch --user <개인-계정>
git push origin main

왜 필요한가 — 에러 메시지만 보면 저장소 권한 문제로 읽히기 쉽습니다. 활성 계정부터 확인하면 헛수고를 줄일 수 있어요.

마무리

이 블로그를 무인으로 돌리기로 했을 때는, 글 쓰는 부분만 자동화하면 될 줄 알았습니다. 실제로 손이 많이 가는 건 오히려 그 뒤, 조용히 실패하는 지점을 찾는 일이었습니다.

화이팅하는 초록이 아빠

오늘도 차분히 한 걸음. 🐴

이 글은 산업 구조와 만드는 과정을 정리한 개인 기록입니다.
특정 종목의 매수·매도 추천이 아니며, 투자 판단과 그 결과는 독자 본인에게 있습니다.

← 글 목록으로