매일 아침 AI 뉴스를 자동으로 받아보고 싶었다. 블로그 소재 말고, 그냥 대중이 기다리는 AI 서비스 소식 같은 걸 위키에 쌓고 Discord로도 받는 파이프라인.
처음엔 집에 둔 Hermes 봇으로 만들었다. Hermes는 OpenAI Codex를 OAuth로 물고 있어서 별도 API 키 없이 도는데, 코덱스 토큰이 어차피 남으니 그걸로 cron을 돌리면 됐다. 서버 cron이 매일 아침 RSS를 긁어서 raw에 저장하고, Codex가 한국어로 요약해서 Discord로 보내는 구조. 잘 돌았다.
문제는 그 다음에 터졌다.
어느 날 발행이 안 됐다
아침에 보니 그날 다이제스트가 안 올라와 있었다. 서버를 확인해보니 429였다. 한도 초과.
Hermes를 Claude CLI로 바꿀 수 있나 싶어서 알아보기 시작했다. 그런데 처음 받은 답이 틀렸다.
Hermes 설정만 보고 “이건 Claude 구독 OAuth를 타니까 Claude Code랑 같은 풀을 쓴다, CLI로 바꿔도 같은 한도라 소용없다”는 분석이 나왔다. 그럴듯했는데 내가 알기론 아니었다.
“아니야, 코덱스 OAuth 쓰고 있어.”
이 한마디로 분석이 통째로 뒤집혔다. 서버 인증 파일을 직접 까보니 정말 OpenAI Codex OAuth였다. 그러면 그 429는 Codex(ChatGPT 구독) 한도였던 거고, Claude 구독은 완전히 별개 풀이다. 즉 digest를 claude -p로 돌리면 Codex 한도에서 완전히 벗어난다. “같은 풀이라 소용없다”던 첫 답과 정반대 결론이었다.
여기서 배운 것: 봇이 어떤 인증으로 도는지는 추측하면 안 된다. auth.json을 직접 열어 확인해야 한다.
”-p 모드는 크레딧 빠지는 거 아냐?”
다음 걱정은 이거였다. claude -p(헤드리스 모드)로 돌리면 종량제 크레딧으로 새는 거 아닌가. 그러면 구독을 두고 굳이 돈을 더 쓰는 셈이라 의미가 없다.
이건 정확해야 하니 메모리로 답하지 않고 따로 조사했다. 결론:
-p는 과금 방식을 안 바꾼다. 인터랙티브claude와 같은 인증을 쓴다. 구독 OAuth로 로그인돼 있으면-p도 구독 한도로 돌고 크레딧 소모가 없다.- 함정은
ANTHROPIC_API_KEY환경변수다. 이게 설정돼 있으면 구독보다 우선시돼서 크레딧 종량제로 빠진다. cron 환경에서 이 변수를 비워두는 게 핵심이다. claude --bare -p(bare 모드)는 OAuth를 건너뛰고 API 키만 받는다. 그러니 종량제를 원할 때만 쓴다.
정리하면, ANTHROPIC_API_KEY만 안 두면 claude -p는 구독으로 조용히 돈다.
cron에 딱 맞는 건 setup-token
비대화형 cron에서 가장 안정적인 방법은 claude setup-token이었다.
이게 구독 계정으로 장기 OAuth 토큰(sk-ant-oat01-...)을 발급해준다. 크레딧이 아니라 구독 기반이고, 유효기간이 길다(대략 1년). 한 번 발급받아서 CLAUDE_CODE_OAUTH_TOKEN으로 넣어두면 끝이다.
# 맥에서 1회 발급 (브라우저 OAuth라 사람이 직접)
claude setup-token
# → sk-ant-oat01-... 출력
# 서버 .env에 저장 (크레딧 안 쓰고 구독으로 cron 실행)
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-... \
claude -p "오늘 뉴스 요약해줘" --allowedTools "Read,Bash"
토큰 발급은 브라우저가 필요해서 맥에서 한 번 받고, 그 토큰만 서버로 옮기면 된다. 만료되거나 폐기할 때만 다시 발급한다.
호스트에 깔까, 컨테이너에 깔까
마지막 결정이 남았다. claude를 서버 어디에 설치하느냐.
A. 호스트에 직접 설치. 간단하지만 claude가 hong 유저 권한으로 돌아서 /home/hong 전체가 다 보인다. trading_mvp도, hansaiam도, 다른 프로젝트 키까지. 봇한테 주기엔 너무 많이 열린다.
B. Hermes 컨테이너 안에 설치. Hermes는 일부러 샌드박스된 컨테이너다. 그 안의 claude는 /workspace(second 위키)와 hermes 상태 디렉터리만 본다. docker.sock도 없고 인바운드 포트도 없고 다른 앱 시크릿도 안 마운트된다.
격리 관점에선 B가 확실히 낫다. claude가 Hermes와 똑같은 샌드박스에 갇히니까. 그래서 B로 갔다.
B의 장점은 재빌드에 강하다는 거다.
- 설치는 Dockerfile에 구워서 이미지에 박힌다(영속)
- 토큰은
.env에 있고.env는 마운트라 살아남는다(영속). 시크릿이라 이미지엔 안 넣는다
그래서 최초 1회만 하면 된다. Dockerfile에 네이티브 설치(claude.ai/install.sh stable, node 불필요) 추가하고, .env에 토큰 한 줄 넣고, digest 호출을 docker exec hermes-discord claude -p ...로 배선. 이후엔 재빌드해도 자동이다.
정리하면
[Codex cron] 429 한도
↓
[host cron] → fetch-ai-news.py → claude -p (구독 OAuth) → digest → git push → 재빌드
↑
ANTHROPIC_API_KEY 비우기 + CLAUDE_CODE_OAUTH_TOKEN(setup-token)으로 구독만 사용
↑
컨테이너 격리(B) 유지: claude가 second 위키만 보게
뉴스 fetch 자체는 Hermes 없이 호스트 cron만으로도 100% 재현된다. cron이 스케줄러, claude가 실행기. [[헤르메스-claude-code-분담-운용|기존에 hermes chat을 부르던 자리에]] claude -p만 끼우면 된다.
지금은 매일 돌고 있다. Codex는 자동화에서 완전히 뺐다. Discord 채팅 자체가 지금은 거의 없어서 봇의 채팅 기능은 Codex 무료 토큰으로 놔뒀는데, 솔직히 Discord로 뭔가를 물어보는 일이 거의 없다. 생각이 생기면 블로그 소재로 박아두지, 챗으로 날리지 않게 됐다.