에러 알림에서 폰으로 고치는 PR까지 — 온콜 봇 하루 만들기

자작 텔레그램 봇을 만들고 같은 날 폐기했다. 인바운드 포트를 하나도 열지 않고 폰에서 서버 에러를 고치는 구조

하려던 것

서비스가 셋으로 늘었는데 에러를 언제 아는지가 문제였다. 로그를 들여다볼 때만 알 수 있으니, 밖에 있으면 몇 시간씩 모른 채로 지나간다.

어제 저녁에 이렇게 시작했다.

“백엔드 에러 알림을 텔레그램 봇으로 받고 싶은데. 그리고 Claude Code CLI로 에러 수정해서 PR 올리게 하고 싶은데.”

알림만 받는 게 아니라 폰에서 버튼 하나로 수정 PR까지 나오게 하는 게 목표였다.

어떻게 시켰나

먼저 텔레그램 봇을 직접 만들었다. 롱폴링으로 메시지를 받고, 명령어를 파싱하고, 맥미니에서 Claude Code를 실행시키는 구조다. 오늘 오전에 완성했다.

중간에 사소한 삽질이 하나 있었다. 예전에 만들어둔 봇이 이미 있는데 토큰을 어디 뒀는지 몰라서 결국 새로 팠다. 이런 게 쌓이는 걸 보면 비밀값 관리를 언젠가 정리해야 한다.

어디서 막혔나

만든 걸 같은 날 밤에 버렸다.

자작 봇의 문제는 에러 수집 쪽이었다. 텔레그램으로 알림을 보내려면 먼저 에러를 잡아야 하는데, 그러려면 백엔드·프론트·모바일 각각에 수집 코드를 붙여야 한다. 스택이 셋이고 언어가 다르다. 그걸 직접 짜기 시작하니 봇이 아니라 에러 트래커를 만들고 있었다.

이건 이미 있는 물건이다. 그래서 수집은 Sentry로 일원화하고, 내가 만든 건 “Sentry가 알려준 걸 받아서 Claude Code를 돌리는 부분”만 남겼다.

어떻게 풀었나

구조를 둘로 쪼갰다.

relay — Cloudflare Worker. Sentry 웹훅을 받아서 텔레그램으로 보낸다. agent — 맥미니에 도는 Go 데몬. 텔레그램을 롱폴링으로 읽고, 명령이 오면 Claude Code를 실행한다.

이 분리에서 제일 중요한 건 인바운드 포트를 하나도 안 연다는 점이다. 맥미니는 텔레그램 API로 나가서 폴링만 한다. 집 네트워크에 외부에서 들어오는 경로가 생기지 않는다. 개인 장비에 서버를 두면서 제일 신경 쓰이는 부분인데, 롱폴링이 이걸 통째로 없앤다.

그리고 오늘 밤 첫 실전이 왔다.

[level=PANIC] runtime error: invalid memory address

폰으로 알림을 받고, 버튼을 눌러 Claude Code를 돌리고, 올라온 PR을 폰에서 확인했다. 밤 11시 반에 이러고 있었다.

“뭐하니?”

에이전트가 뭘 하고 있는지 궁금해서 던진 한마디인데, 이 날의 요약으로는 이만한 게 없다.

같은 날 웹 서비스의 비밀번호 게이트도 제거했다. 이제 아무나 들어올 수 있으니 에러도 아무 때나 날 수 있다. 온콜을 하루 먼저 만든 게 순서상 다행이었다.

배운 점