7개 봇을 감사해서 5개를 죽이기로 한 다음 편입니다. 이번엔 실제로 죽이는 이야기 — 그런데 봇을 죽이는 건 프로세스를 멈추는 게 다가 아니었습니다.
먼저 물어봅니다. 당신은 실패한 사이드 프로젝트를 "죽일지 말지"로 며칠씩 고민한 적 있습니까? 매몰비용, 아까움, "조금만 더 두면 반등할지도" — 그 논쟁 자체가 시간 도둑입니다. 그 논쟁을 없애는 법을 먼저 말하겠습니다.
죽이는 결정은 이미 끝나 있었다
봇을 접을 때 가장 비싼 건 감정 논쟁입니다. "3%p 밑돌지만 조금 더?", "승률 17%인데 국면 탓 아닐까?" — 이걸 그때그때 하면 절대 못 죽입니다.
그래서 게이트를 데이터 오기 전에 적어뒀습니다. 월말효과 봇은 "완결 n≥30 AND 초과수익>0"이 통과 조건이었고, n=30을 채운 시점에 초과가 −0.17%p였습니다. 논쟁할 게 없습니다. 게이트가 FAIL을 찍었으니 죽입니다. 매몰비용도, "조금만 더"도 끼어들 틈이 없습니다. 사전등록의 진짜 값은 자기기만을 막는 것만이 아니라, 죽일 때 후회를 안 남기는 것이었습니다.
살릴 것과 죽일 것도 명확했습니다. 죽임: 월말효과(게이트 FAIL)·업비트 스윙(음의 엣지)·빗썸 SMA50(사후선택+거래0). 살림: 저PBR 밸류·내부자매수(둘 다 문서화된 아노말리) + LLM 판정 봇(조기청산 관찰용).
그런데 봇은 프로세스만 죽지 않는다
여기부터가 실전입니다. "봇을 죽인다"고 하면 데몬 프로세스 kill을 떠올리지만, 봇 하나는 여러 겹의 흔적을 남깁니다. 하나라도 빼먹으면 좀비가 됩니다.
1) launchd — 두 단계 다 해야 한다. macOS에서 프로세스만 kill하면 KeepAlive가 즉시 되살리고, 안 되살아나도 다음 로그인에 plist가 다시 로드합니다. 완전히 죽이려면 둘 다:
launchctl bootout gui/$(id -u)/com.user.<label>
rm ~/Library/LaunchAgents/com.user.<label>.plist봇 3개가 각각 daily 잡 + dashboard 잡을 갖고 있어 총 5개 라벨을 부트아웃했습니다(한 봇은 대시보드 없음). 부트아웃 후 launchctl list | grep으로 잔존 0, 리스닝 포트 사망까지 확인해야 끝입니다. launchd의 이런 함정은 따로 정리해둔 적이 있습니다 — 프로세스 kill과 job 제거는 다른 층위입니다.
2) 대시보드 — 죽은 링크가 사방에 남는다. 봇을 죽이면 그 봇을 참조하던 UI가 전부 깨진 링크가 됩니다. 제 경우:
- 집계 대시보드(허브)에 한 봇은 전용 라우트·뷰·템플릿까지 있었습니다 → import·라우트·파일·인덱스 네비 전부 제거.
/bithumbswing→ 404 확인. - 나머지 두 봇은 각자 독립 대시보드였는데, 생존한 형제 대시보드들의 공용 네비에 죽은 봇 URL이 하드코딩돼 있었습니다. 봇을 죽이면 그 봇 폴더만 보는 게 아니라, 그 봇을 링크하던 남들을 찾아 고쳐야 합니다.
grep -rn ':<포트>|/<봇>'으로 전수 스캔이 필수입니다.
함정 하나 더: Flask는 템플릿을 캐싱합니다. 파일만 고치면 안 뜨고, 해당 대시보드 launchd 잡을 launchctl kickstart -k로 재시작해야 반영됩니다. 저는 재시작 후 살아야 할 포트는 200, 죽인 라우트는 404, 지운 링크는 grep 0까지 확인하고 넘어갔습니다.
당신이라면 폴더를 지우겠습니까, 남기겠습니까
여기서 갈립니다. 봇 리포에는 ledger(모든 거래 기록)가 들어 있습니다 — 이게 감사의 증거입니다. 그냥 rm -rf 하면 250MB는 회수하지만 나중에 "그 봇 진짜 왜 죽였더라"의 근거가 사라집니다. 그냥 두면 근거는 남지만 죽은 코드가 디스크에 쌓입니다.
저는 아카이브 후 삭제를 골랐습니다:
tar -czf decommissioned-2026-08-06/<봇>.tar.gz <봇>/
tar -tzf ...tar.gz | wc -l # 무결성: 항목 수 확인
rm -rf <봇>/핵심은 중간 줄입니다 — rm 전에 tar -tzf로 아카이브가 실제로 열리는지 검증합니다. 아카이브가 깨진 채 원본을 지우면 그건 삭제지 아카이브가 아닙니다. 되돌리려면 tar 풀고 plist만 복원하면 됩니다.
자가진단 — 봇/서비스 하나를 죽일 때 빠뜨리기 쉬운 3곳
- 재생성 경로. 프로세스만 죽였나, 아니면 그걸 되살리는 것(KeepAlive·plist·cron·supervisor)까지 죽였나?
- 인바운드 참조. 그 서비스를 링크·호출하던 남들을 grep으로 다 찾았나? 죽은 링크·죽은 엔드포인트 호출이 남아 있지 않나?
- 되돌림 가능성. 지우기 전에 복구 경로(검증된 아카이브)를 확보했나, 아니면 그냥 지웠나?
솔직한 부분
죽인 뒤에도 살아있는 게 남습니다. 저는 커밋을 정리하며 다른 세션이 안 커밋한 변경이 같은 리포에 섞여 있는 걸 봤습니다 — 그래서 git add .이 아니라 제가 만진 경로만 명시적으로 스테이징했습니다. 봇을 죽이는 커밋에 남의 미완성 변경을 딸려 보내면 그것도 흔적을 어지럽히는 일입니다.
그리고 정직하게: 아직 안 지운 게 남았습니다. 게이트 FAIL이지만 "낙폭축소형"으로 목적을 재정의해 살려둔 봇 하나, 설정 파일에 남은 권한 항목 몇 개. 완벽한 청소는 아닙니다. 하지만 죽인 3개는 프로세스·대시보드·디스크 세 층위에서 다 지워졌고, 되돌릴 아카이브가 있고, 왜 죽였는지가 게이트 로그에 남아 있습니다.
봇을 죽이는 데 가장 필요한 건 결단력이 아니었습니다. 죽일지 말지를 미리 정해둔 규칙과, 흔적이 어디에 남는지 아는 체크리스트였습니다. 결단은 사전등록이 대신 해줬고, 나머지는 grep이 해줬습니다.
지금 당신의 죽은 사이드 프로젝트 하나를 떠올려 보세요. 프로세스는 멈췄는데 — 그걸 링크하던 페이지, 되살리는 cron, 디스크의 리포는 아직 살아있지 않습니까?