봄,여름, 가을,겨울 사계절 AI에이전트 팀 세팅 후 후일담
지난 글에서 나는 5분 만에 에이전트를 한 명 더 영입하는 법을 썼다. 텔레그램 봇 토큰 하나,
.env 파일 한 줄, Node.js 코드 40줄. 그렇게 Winter를 팀에 합류시켰고, 꽤 뿌듯해했다.
Winter는 잘 돌아가는 것처럼 보였다. 메시지를 보내면 Claude가 답했으니까. 그러다 대화 한 두번 만에 바로 알았다. 돌아가는 것과 일하는 것은 전혀 다른 문제라는 걸.
"내 드라이브에서 논문 파일 좀 읽어봐."
저는 파일 시스템에 접근할 수 없습니다. 파일을 직접 업로드해 주시면 분석해 드리겠습니다.
Google Drive가 바로 옆에 마운트되어 있다. 맥미니 안에서 돌아가고 있으면서, 자기 옆에 뭐가 있는지 모른다. "네 이름이 뭐야?"라고 물으면 "저는 Claude입니다"라고 한다. 아무리 이름을 알려줘도 다음 대화에서 다시 Claude라고 자기소개를 한다.
파고들수록 못 하는 것 투성이였다. bash 명령어 실행 불가. 파일 읽기 불가. 폴더 탐색 불가. 코드 실행 불가. 텔레그램으로 들어온 메시지를 API에 전달하고, 돌아오는 텍스트를 그대로 전송하는 것 — 그게 Winter가 할 수 있는 전부였다.
돌이켜보면 이상한 일이었다. Spring은 내가 가장 먼저 만든 봇이다. 그런데 처음부터 파일을 읽었고, bash를 실행했고, 시뮬레이션 스크립트를 짜서 돌려보기까지 했다. 아무런 삽질 없이.
이유는 뜯어보니 단순했다. Spring은 내가 짠 게 아니었다. GitHub에서 claude-code-telegram 이라는 오픈소스 프로젝트를 찾아 설치한 것이다. 누군가가 Claude Code SDK 연동부터 tool use, 파일 접근, bash 실행, 보안 설정까지 전부 만들어둔 완성품이었고, 나는 그걸 리눅스 서버에 깔고 .env 에 토큰을 넣고 실행 버튼을 눌렀을 뿐이다. Spring이 똑똑했던 게 아니다. Spring을 만든 사람이 똑똑했던 것이다.
문제는 두 번째 봇을 만들 때 벌어졌다. "맥미니에서 하나 더 돌리자"고 마음먹은 순간, Spring이 어떻게 돌아가는지 들여다보지 않았다. 대신 Node.js로 새로 짜기 시작했다. 텔레그램 봇 토큰 받고, Messages API 호출하고, 응답을 돌려주는 40줄짜리 코드. 간단해 보였다.
그렇게 만든 게 앵무새 Winter였고, GitHub에서 받아 설치한 게 에이전트 Spring이었다. 같은 Claude가 응답하는데 차이가 난 건 모델의 성능 때문이 아니었다. 인프라의 차이였다. Spring 내부에는 Claude Code SDK가 들어 있다. 파일 읽기, bash 실행, 코드 편집, 웹 검색까지 도구가 전부 내장된 구조. 자동차로 치면 엔진과 변속기와 서스펜션이 다 있는 완성차다. 내가 Node.js로 만든 Winter는 핸들만 달린 프레임이었다. 방향은 잡을 수 있는데 굴러가질 않는.
해결책은 의외로 단순했다. Winter를 고치는 게 아니라, Spring을 복제하면 된다.
cp -r ~/claude-code-telegram ~/claude-code-telegram-winter
cd ~/claude-code-telegram-winter
nano .env
바꿀 줄은 딱 두 줄뿐이다.
TELEGRAM_BOT_TOKEN=여기에_Winter의_토큰
TELEGRAM_BOT_USERNAME=Winter의_유저네임
나머지는 Spring과 동일하게 둔다. Claude Code SDK 인증, 허용 도구 목록, 프로젝트 경로 — 이미 검증된 설정이 전부 들어가 있으니 건드릴 이유가 없다. 의존성 설치하고 실행하면 끝.
poetry install
poetry run claude-telegram-bot &
Winter가 Spring과 똑같이 파일을 읽고, bash를 실행하고, Google Drive를 탐색하기 시작했다. 40줄짜리 코드를 직접 짜고, dotenv 호환성 문제를 디버깅하고, tool use를 수작업으로 구현하던 오후가 무색해지는 순간이었다. Autumn과 Winter도 같은 방식으로 만들었다. 복제, 토큰 교체, 실행. 한 명당 5분. 이번에는 진짜로 5분이었다.
이 결론에 도달하기까지 꽤 돌아왔다. 기록으로 남길 만한 실수들이 있다.
.env 파일에 BOT_TOKEN= 을 빼먹고 토큰 문자열만 적었다. dotenv는 KEY=VALUE 형식만 인식하는데, 급하면 이런 걸 놓친다. 로그에 injecting env (0) — 변수를 0개 읽었다는 뜻 — 이 찍혀 있었고, 봇은 시동만 걸린 채 아무 일도 하지 않았다.
dotenv v17과 PM2의 궁합 문제도 겪었다. 터미널에서 직접 node index.js 로 실행하면 토큰을 잘 읽는데, PM2를 통해 돌리면 변수를 못 읽는다. 같은 디렉토리, 같은 파일인데. 결국 dotenv를 아예 빼고
fs.readFileSync 로 .env 를 직접 파싱하는 코드 4줄을 짰다. 외부 패키지를 걷어내고 수동 파싱을 넣는 게 더 안정적이라니, 좀 씁쓸하긴 했다.
API 키를 코드에 하드코딩했다가 대화 로그에 그대로 노출된 적도 있다. 즉시 재발급하고 .env 로 분리했다. 키는 반드시 환경변수로 관리해야 한다. 대화 로그, 스크린샷, Git 커밋 — 노출 경로는 생각보다 많다.
가장 헛된 시간은 Messages API 위에 tool use를 직접 구현하던 때였다. Claude가 list_directory 도구를 호출하면 Node.js 코드가 파일 시스템을 조회해서 결과를 돌려주는 구조를 짰다. 작동은 했다. 하지만 경로 매핑이 어긋나면 도구를 3회 호출한 뒤 "반복 초과"라는 메시지만 남겼다. Claude Code SDK에 이 모든 게 이미 들어 있는데, 왜 바퀴를 처음부터 깎고 있었는지.
결국 기존 봇의 설정 파일을 열어보고서야 깨달았다. 이미 작동하는 것을 재사용하라. 소프트웨어 엔지니어링의 가장 오래된 원칙이 여기서도 유효했다.
┌──────────┬─────────┬──────────────────────┐
│ name │ status │ role │
├──────────┼─────────┼──────────────────────┤
│ spring │ online │ 비서 / 스케줄 관리 │
│ summer │ online │ 학생 리포트 / 루틴 │
│ autumn │ online │ 논문 / 자료 수집 │
│ winter │ online │ 시뮬레이션 계산 │
└──────────┴─────────┴──────────────────────┘
네 명 모두 Claude Code SDK 기반으로 돌아간다. 파일을 읽고, bash를 실행하고, Google Drive를 탐색한다. 텔레그램 그룹에 네 명을 전부 초대해두면 한 공간에서 작업을 지시하고 결과를 받아볼 수 있다. Autumn이 논문 초안을 올리면 Winter가 계산 결과로 검증하는 식의 협업까지 가능해졌다.
다만 한 가지 주의할 점이 있다. Claude Max 구독 하나로 봇 여러 개를 돌리면 동시 호출 시 rate limit에 걸릴 수 있다. 네 명이 한꺼번에 일하면 한 사람이 네 배 속도로 쓰는 셈이니까. 자주 안 쓰는 봇은 API 키 방식으로 분리하거나, 작업을 순차적으로 지시하는 식의 교통 정리가 필요하다.
이제는 5명을 활용해 (그 사이에 season 이라는 친구 한 명 더 영입) 리뷰 논문 작성하는 팀을 구성해 볼 계획이다. 논문을 찾고, 쓰고, 검증하고 보완하는 일들을 구성하려고 한다. 어제는 텔레그램 단톡방에서 서로 서먹한 인사도 나누었다. AI에이전트 팀의 시작이다.