봇은 36일 동안 매일 정상이라고 말했다

로그는 깨끗했다. 그게 제일 무서웠다.
36일
내 봇 중에 FTD 갱신봇이라는 게 있다. 매일 오후 3시 45분에 시장 데이터를 받아서 계산하고, 결과를 공개 페이지에 올린다. 만든 지 몇 달 됐고, 그동안 별 탈이 없었다.
다른 걸 점검하다가 우연히 그 페이지를 열었다.
데이터 기준: 2026-08-07 마감
그날은 9월 12일이었다. 36일 전 숫자가 떠 있었다.
바로 로그를 봤다. 로그는 멀쩡했다. 매일 그날 날짜로 계산이 찍혀 있었다.
{"asof": "2026-09-11 마감", "phase": "FTD", "count": 30, ...}
프로세스도 살아 있었다. 스케줄러에도 정상 등록돼 있었다. 계산 결과를 저장하는 파일도 최신이었다. 봇은 매일 제 할 일을 했다. 다만 그 결과가 세상에 나가지 않았을 뿐이다.
원인은 두 달 전의 나였다
기록을 뒤져보니 7월 27일에 이런 수정을 했다.
배포가 실패하면 판정 알림이 통째로 안 나가는 문제가 있다. 판정은 배포 성공 여부와 상관없는 정보다. 순서를 뒤집어 분리한다.
맞는 판단이었다. 그래서 알림 보내는 부분을 배포보다 앞으로 옮기고, 문구 만드는 코드를 별도 함수로 떼어냈다.
그러면서 배포 블록이 따라 들어갔다. 이렇게.
if kospi_fresh:
msg += "\n" + buy_guide(live, ...)
return msg
# ── 배포 ──
token = ENV.get("VERCEL_TOKEN", "")
...
return msg 아래다. 그 뒤로 한 줄도 실행되지 않았다.
파이썬은 이걸 에러로 잡아주지 않는다. 문법적으로 완벽한 코드다. 그냥 영원히 도달하지 않을 뿐이다.
증거는 로그에 있었다. 8월 초를 마지막으로 배포 관련 문구가 한 줄도 없었다. 성공도 실패도 아니고 그냥 없었다. 나는 그걸 몇 번이나 지나쳤다. 없는 걸 알아채는 건 있는 걸 읽는 것보다 훨씬 어렵다.
블록을 원래 함수로 옮기고 한 번 돌렸다.
REST 배포 실패(HTTP Error 403: Forbidden) — vercel CLI로 폴백
배포: CLI 배포
36일 만에 페이지가 9월 11일자로 바뀌었다.
내 메모가 나를 속였다
더 민망한 게 있다.
내 작업 메모에는 이 봇의 상태가 이렇게 적혀 있었다.
2026-08-08 확인: 토큰 만료로 배포 실패
그래서 나는 한 달 내내 "아, 그거 토큰 문제지" 하고 넘겼다. 토큰은 실제로 죽어 있었다. 그것도 사실이었다. 다만 진짜 원인이 아니었다. 토큰이 살아 있었어도 그 코드는 실행되지 않았다.
적어둔 원인이 맞는 것처럼 보이면, 그 앞에서 생각이 멈춘다. 나는 한 달 동안 틀린 답을 들고 있으면서 답을 안다고 믿었다.
그래서 감시자를 만들었다
같은 계열의 사고를 이번이 처음 겪은 게 아니다.
전에는 거래 봇 두 개가 두 달 동안 계좌가 빈 채로 돌고 있었는데 몰랐다. 프로세스는 살아 있었고 로그도 쌓이고 있었다. 주문이 전부 거부당하고 있다는 걸 나중에 세어보니 38만 번이었다.
공통점이 보였다. 조용한 게 정상인지 죽음인지 구분이 안 된다.
그래서 그날 밤에 감시봇을 하나 만들었다. 하루짜리로 못 박고 시작했다. 원칙은 둘이었다.
첫째, 프로세스가 아니라 결과물을 본다. FTD가 가르쳐준 거다. 로그가 매일 찍히고 프로세스가 살아 있어도 아무 의미가 없다. 사람이 보는 화면이 최신인지를 봐야 한다.
둘째, 문제가 없어도 매일 보낸다. 이상할 때만 알리면, 감시자가 죽었을 때도 똑같이 조용하다. 그게 지금까지 내 봇들이 죽어온 방식이다.
첫 실행에서 바로 세 개가 잡혔다. 4일, 6일, 18일씩 조용한 봇들이었다.
그리고 다음 날 아침, 감시자가 거짓말을 했다
봇 점검 · 9/13 08:05
전부 정상
정상 17개
아무것도 안 고쳤는데 전날 잡힌 셋이 다 정상이 됐다.
새벽 2시 14분에 서버가 재부팅됐다. 모든 봇이 다시 시작하면서 시작 문구를 한 줄씩 찍었다.
[훈수로그봇] 시작 - 2026-09-13 02:16:36
[watch] 스마트지갑 2개 민팅 감시 (매 90초)
내 감시자는 파일이 방금 수정됐다는 것만 보고 "살아 있다"고 판단했다. 시작 문구는 일한 게 아니다. 하루도 안 돼서 내가 막으려던 바로 그 실패를 감시자가 저질렀다.
"프로세스 말고 결과물을 봐야 한다"고 원칙까지 적어놓고, 정작 서버 쪽은 파일 수정 시각을 보고 있었다. 같은 함정을 다른 자리에서 밟았다.
이틀째
기준을 고치고, 며칠 지켜봤다.
봇 점검 · 9/14 08:05
이상 2개
· mint-radar — 29시간째 조용하다
· whales — 29시간째 조용하다
정상 15개
이번엔 제대로 잡았다. 확인해보니 두 봇 다 60초·90초마다 도는 건데, 시작 문구를 찍은 뒤로 20시간 동안 한 줄도 더 쓰지 않고 있었다. 프로세스는 멀쩡히 떠 있었고 CPU도 조금씩 쓰고 있었다. 에러 파일은 7월 이후로 0바이트였다.
죽지도 않고, 에러도 안 내고, 아무 일도 안 하는 상태로 며칠을 서 있었던 거다.
둘 다 내렸다. 어차피 쓰지 않는 봇이었다.
남은 것
봇을 여러 개 굴리다 보면 어느 순간부터 "몇 개가 돌고 있다"는 사실 자체가 뿌듯해진다. 나도 그랬다. 서버에 열일곱 개가 떠 있었다.
그런데 그중 몇 개가 실제로 일을 하고 있는지는 세어본 적이 없었다. 세어보니 하나는 36일, 하나는 6일, 하나는 18일째 아무것도 안 하고 있었다.
로그가 쌓인다는 건 살아 있다는 뜻이 아니다. 파일이 방금 수정됐다는 것도 아니다. 프로세스가 떠 있다는 것도 아니다.
그 봇이 만들라고 시킨 물건이 지금 세상에 최신 상태로 놓여 있는가. 그것만 살아 있다는 뜻이다.
그리고 하나 더. 적어둔 원인은 의심해야 한다. 내 메모에 "토큰 만료"라고 적혀 있던 한 달 동안, 진짜 원인은 return 한 줄 아래에 조용히 앉아 있었다.