이 글은 월 1만 5천 원짜리 VPS에 24시간 AI 비서를 올렸다 시리즈의 일곱 번째 글이다.
앞 편: 텔레그램 봇이 그룹에서 아무 대답을 안 한다
봇이 대답하기 시작했으니 이제 쓸 수 있는 상태가 됐다. 그런데 며칠 써보니 다른 문제가 생겼다.
전부 한 방에 쏟아졌다. 비서와 나눈 대화, 아침에 수집된 뉴스 열 건, 초안이 생겼다는 알림, 에러 통보가 시간순으로 뒤섞였다. 어제 대화를 다시 보려면 알림 수십 개를 거슬러 올라가야 했다.
대시보드를 따로 만들까 생각했다가 접었다. 안 들어가게 될 게 뻔했다. 텔레그램은 어차피 하루에 수십 번 연다.
그래서 텔레그램 안에서 나누기로 했다.

그룹은 하나다. 안에서 주제로 나누고, 봇은 역할로 나눴다.
왜 디스코드가 아니라 텔레그램이었나
처음엔 디스코드를 봤다. 채널을 나누는 게 기본이고, 미디어 재생도 강하다.
갈린 지점은 파일 크기였다.
| 봇이 올릴 수 있는 크기 | |
|---|---|
| 디스코드 | 8MB (무료 기준) |
| 텔레그램 기본 | 50MB |
| 텔레그램 + 로컬 Bot API 서버 | 2GB |
텔레그램은 봇 API 서버를 직접 띄울 수 있고, 그러면 업로드 한도가 2GB가 된다. 어차피 서버를 세우는 김에 컨테이너 하나를 더 올리면 되는 일이다.
이게 중요했던 이유는 나중에 영상을 다룰 생각이었기 때문이다. 렌더링한 영상을 메신저에서 바로 확인할 수 있느냐 없느냐는 차이가 크다. 8MB로는 1분짜리 영상도 안 들어간다.
그리고 알고 보니 텔레그램도 채널을 나눌 수 있었다. 그걸 주제(Topics)라고 부른다.
주제 모드 켜기
텔레그램 그룹에는 포럼처럼 쓰는 모드가 있다. 켜면 그룹 하나가 주제별로 나뉘고, 각 주제가 독립된 대화방처럼 동작한다. 주제마다 알림을 따로 끄고 켤 수 있다.
그룹 설정에서 관리자가 켜고, 누가 주제를 만들 수 있는지도 따로 정한다. 혼자 쓰는 그룹에서도 켜진다.
주의할 점이 하나 있다. 주제는 슈퍼그룹 기능이다. 일반 그룹으로 만들었다면 설정에 주제 항목이 안 보일 수 있다. 안 보이면 그룹을 새로 만드는 게 빠르다.

주제를 다섯 개로 나눈 기준
나눈 기준은 주제가 아니라 내가 그걸 언제 보는가였다.
| 주제 | 들어오는 것 | 내가 보는 때 |
|---|---|---|
| 비서 | 에이전트와의 대화 | 내가 말을 걸 때 |
| 조사 | 아침 수집 결과 + 버튼 | 아침에 한 번 |
| 글-승인 | 초안이 생겼다는 알림 | 버튼을 누른 뒤 |
| 브리핑 | 정기 리포트 | 하루 한 번 |
| 알림 | 에러·완료 통보 | 뭔가 이상할 때 |
이렇게 나누고 나서 생긴 가장 큰 변화는 알림을 주제별로 끌 수 있게 된 것이다.
조사와 브리핑은 알림을 껐다. 정해진 시각에 오는 것들이라 울릴 이유가 없다. 내가 보고 싶을 때 들어가서 보면 된다. 반대로 알림은 켜뒀다. 뭔가 잘못됐을 때만 오는 곳이니 울려야 맞다.
하나의 방이었다면 이게 불가능하다. 에러 알림을 받으려면 뉴스 열 건도 같이 울려야 하고, 그러다 보면 전부 꺼버리게 되고, 그러면 에러를 놓친다.
주제를 더 늘리지 않은 이유도 적어둔다. 주제가 많아지면 어디에 뭐가 오는지 기억이 안 난다. 다섯 개가 한 화면에 들어오는 한계였다.
봇을 두 개로 나눈 건 취향이 아니었다
여기서 설계상 중요한 제약을 하나 만났다.
텔레그램은 한 봇의 메시지를 한 곳만 받아갈 수 있다. 봇이 받은 메시지를 가져가는 방법은 두 가지인데, 어느 쪽이든 동시에 둘은 안 된다. 둘이 같이 받으려 하면 뒤에 온 쪽이 409 Conflict로 거절당한다.
앞 편에서 봇이 받은 메시지를 확인하려고 getUpdates를 호출했을 때 이야기한 그 제약이다.
이게 왜 문제가 되냐면, 내 시스템에는 메시지를 받아야 하는 쪽이 둘이기 때문이다.
- 에이전트 — 내가 보낸 말에 대답해야 한다
- 워크플로 도구 — 내가 누른 버튼을 받아야 한다
봇이 하나면 둘 중 하나만 동작한다. 그래서 봇을 두 개 만들었다.
| 쓰는 쪽 | 하는 일 | |
|---|---|---|
| 대화용 봇 | 에이전트 | 사람이 말을 걸면 대답 |
| 알림·버튼용 봇 | 워크플로 도구 | 기계가 결과를 보내고 버튼을 받음 |
제약 때문에 어쩔 수 없이 나눈 건데, 쓰다 보니 이쪽이 더 나았다.
대화 중에 알림이 끼어들지 않는다. 비서와 길게 이야기하는 중에 “초안이 생겼습니다”가 중간에 끼면 맥락이 끊긴다. 봇이 다르면 애초에 다른 주제로 가니 그럴 일이 없다.
그리고 봇 이름만 봐도 사람이 보낸 건지 기계가 보낸 건지 구분된다. 나중에 기록을 되짚을 때 이게 생각보다 유용하다.
토픽 ID를 알아내는 법
봇이 특정 주제로 메시지를 보내려면 주제 번호가 필요하다. 보낼 때 message_thread_id라는 값을 같이 넘기는데, 이걸 안 넣으면 전부 첫 번째 방으로 간다.
번호를 알아내는 방법은 두 가지다.
하나. 메시지 링크를 복사한다.
해당 주제에서 아무 메시지나 길게 눌러 링크를 복사하면 이런 형태가 나온다.
https://t.me/c/<그룹번호>/<주제번호>/<메시지번호>
가운데 숫자가 주제 번호다.
둘. 봇이 받은 걸 그대로 본다.
curl "https://api.telegram.org/bot<TOKEN>/getUpdates"
각 주제에서 아무 말이나 한 뒤 호출하면, 응답 안에 message_thread_id가 들어 있다. 앞 편에서 말했듯 에이전트가 돌고 있으면 이 명령이 거절당하므로 잠깐 멈추고 해야 한다.
번호가 띄엄띄엄한 게 정상이다
내 그룹의 주제 번호는 이랬다.
비서 2 · 브리핑 4 · 글-승인 6 · 조사 9 · 알림 11
1, 2, 3, 4, 5가 아니다. 주제 번호는 그 주제가 만들어질 때 생긴 메시지의 번호를 그대로 쓰기 때문이다. 주제를 만드는 사이에 다른 메시지가 끼면 그만큼 번호가 건너뛴다.
그러니 번호를 추측하지 말고 반드시 확인해서 적어둬야 한다. 그리고 한 번 메모해두면 끝이다. 주제를 지우지 않는 한 바뀌지 않는다.
마지막 함정 — 어제는 됐는데 오늘은 400
창구를 다 만들고 워크플로를 붙였더니, 초안 알림이 가끔 안 왔다.
Bad Request: can't parse entities:
Can't find end of the entity starting at byte offset 151
이상한 건 같은 워크플로가 어제는 됐다는 점이었다. 설정을 바꾼 게 없는데 어떤 날은 되고 어떤 날은 안 됐다.

워크플로는 그대로다. 매번 달라지는 건 제목뿐이었다.
되던 날과 안 되던 날의 메시지를 나란히 놓고 봤다.
1:04 '특정 키워드'로 연구비 일괄 취소? ← 에러
1:09 키워드 검색으로 연구비 취소? 미국… ← 정상
범인은 따옴표였다.
텔레그램은 메시지를 보낼 때 서식 문법을 해석하는 기능이 있다. _, *, [, ` 같은 문자를 서식 기호로 읽는데, 짝이 안 맞으면 메시지 전체를 거절한다.
그리고 이 알림의 제목은 매번 모델이 새로 쓴다. 어떤 날은 따옴표가 들어가고 어떤 날은 안 들어간다. 그래서 랜덤하게 실패했던 것이다.
제목을 빼고 보내봤더니 또 터졌다. 이번엔 링크가 범인이었다.
https://…/wp-admin/edit.php?post_status=draft
↑ 밑줄 하나, 짝이 없다
어떻게 고쳤나
가장 깔끔한 해결은 서식 해석을 아예 끄는 것이다. 메시지를 평문으로 보내면 특수문자가 뭐가 들어오든 상관없어진다. 서식이 꼭 필요하면 특수문자 앞에 일일이 역슬래시를 붙여 escape 해야 하는데, 모델이 쓴 문장에 그걸 적용하려면 처리 단계가 하나 더 붙는다.
나는 더 단순하게 갔다. 알림 문구를 고정했다.
초안이 임시글로 저장되었습니다
https://…/wp-admin/edit.php
제목을 알림에 넣지 않고, 링크도 물음표 뒤를 떼어냈다. 알림은 “왔다”만 알려주고 내용 확인은 어차피 블로그 관리화면에서 한다.
쓰다 보니 이쪽이 더 나은 흐름이기도 했다. 알림에 제목이 길게 들어가면 그것만 보고 판단하게 되는데, 어차피 열어서 봐야 하는 글이다.

이게 제일 나쁜 종류의 고장이다
매일 새벽 혼자 도는 자동화에서는, 가끔 깨지는 게 아예 안 되는 것보다 나쁘다.
아예 안 되면 바로 안다. 그런데 열흘에 한 번 실패하면, 실패했다는 사실 자체를 모른다. 알림이 안 왔으니까. “오늘은 수집된 게 없었나 보다” 하고 넘어간다.
[[확인 필요: 이 문제를 며칠 만에 발견했는지. “며칠 뒤에야 알았다” 같은 구체적인 기간이 들어가면 이 문단이 훨씬 설득력 있다. 기억이 안 나면 이 줄은 지워도 된다]]
그래서 자동화를 만들 때는 **”실패하면 어떻게 아는가”**를 같이 설계해야 한다. 이 시리즈 뒤쪽에서 캐시 설정 하나 때문에 자동 발행이 조용히 죽는 이야기가 또 나온다. 형태가 똑같다.
이 편에서 막힌 것
문제 — 대화·수집 결과·알림·에러가 한 방에 뒤섞여서 아무것도 찾을 수 없었다.
해결 — 텔레그램 그룹을 주제 모드로 바꾸고 다섯 개로 나눴다. 기준은 주제가 아니라 “내가 언제 보는가”. 덕분에 주제별로 알림을 따로 끌 수 있게 됐다.
제약 — 한 봇의 메시지는 한 곳만 받아갈 수 있다(409 Conflict). 그래서 대화용과 알림용으로 봇을 둘로 나눴고, 결과적으로 맥락이 안 끊겨서 더 나았다.
알아둘 것 — 주제 번호는 1부터 순서대로가 아니다. 생성 메시지 번호를 그대로 쓰므로 반드시 확인해서 적어둬야 한다.
마지막 함정 — 모델이 쓴 제목에 따옴표가 들어간 날만400에러가 났다. 텔레그램 서식 문법이 짝이 안 맞는 특수문자를 거절한다. 알림 문구를 고정해서 해결했다.
다음 편 — 원클릭 설치가 안 해주는 것들. 5분 만에 올라간 뒤에 직접 해야 했던 세 가지.
참고
- 텔레그램 Bot API 문서 — https://core.telegram.org/bots/api
