제 자동매매 봇에는 문제가 생기면 저한테 알려 주는 장치가 있습니다. 만들어 놓고 마음이 편했어요.
그런데 그 알림은 도착한 적이 한 번도 없었습니다. 성공 0회. 어느 날 고장 난 게 아니라, 처음부터 나갈 수 없는 상태였습니다.
여러분 휴대폰에도 켜 둔 알림이 여럿 있을 텐데요, 더 이상한 건 이겁니다. 알림이 죽었다는 사실을 알려 주는 알림은, 당연히 오지 않습니다.
개발기 ⑯에서는 정반대 이야기를 했습니다. 경고가 매일 와서 아무도 안 읽게 된 이야기였죠. 오늘은 경고가 아예 안 오는 쪽입니다.
화면에서 이 둘은 똑같이 생겼습니다. 알림함이 조용한 건 매한가지거든요. 뒤에서 벌어지는 일만 정반대입니다.
2주 전에 “원인은 확인 중”이라고 닫았던 자리
개발기 ⑭에서 저는 실패 알림이 발송에 실패했고 원인은 아직 확인 중이라고 적었습니다.
그 원인이 나왔습니다. 그리고 원인은 알림 코드 안에 있지 않았어요.
8월 6일 새벽 3시 30분, 자동 작업 하나가 시간 초과로 강제 종료됐습니다. 감지하는 쪽은 제 몫을 다했습니다. 몇 분이 걸렸는지, 기록이 얼마나 쌓였는지, 손댄 파일이 13개라는 것까지 전부 적었거든요.
그 기록의 마지막 줄이 이랬습니다.
실패 알림 예외 — 미발송.
편지 부칠 사람은 정해 뒀는데, 그 사람이 우표를 몰랐습니다

봇은 파이썬이라는 언어로 돌아갑니다. 그런데 제 컴퓨터에 그 파이썬이 두 벌 깔려 있었어요.
알림 심부름을 시킨 쪽에는 인터넷으로 메시지를 부치는 도구가 아예 안 깔려 있었습니다. 다른 한 벌에는 있었습니다.

표로 보기
| 알림 통로 | 봇이 부른 쪽 | 있다고 믿은 쪽 |
|---|---|---|
| 부치는 도구 | 없음 | 있음 |
| 도착 횟수 | 0회 | – |
| 실패 방식 | 늘 같은 자리 | – |
쉽게 말하면 편지 쓸 사람은 정해 뒀는데 그 사람이 우표도 우체통 위치도 모르는 상태였어요.
여기서 제일 아픈 대목은 “0회”가 아닙니다. 부를 때마다 같은 자리에서 실패한다는 점입니다.
우연히 실패한 게 아니니까 다음에도, 그다음에도 안 갑니다. 그 장치를 붙인 날부터 계속 그랬습니다.
게다가 실패한 이유조차 안 남았습니다. 오류가 나면 그 내용을 통째로 버리고 넘어가는 줄이 있었거든요. 결국 제가 손으로 하나하나 재현해 보고서야 원인을 알았습니다.
고쳤는데, 이번엔 나간 알림을 “안 나갔다”고 적었습니다
수리는 어렵지 않았습니다. 도구가 있는 쪽으로 심부름을 보내면 되니까요.
문제는 성공했는지 판정하는 방식이었습니다. 심부름꾼이 “OK”라고 말하면 성공으로 치기로 했거든요.
그런데 그 심부름꾼은 성공했을 때도, 실패했을 때도 자기 나름의 문장을 먼저 말합니다. 그래서 돌아온 대답이 이랬어요. “알림 발송 성공. OK.”
제 판정은 글자가 정확히 “OK”일 때만 성공으로 쳤습니다. 앞에 문장이 하나 붙었으니 영영 안 맞죠.

표로 보기
| 단계 | 내용 | 설명 |
|---|---|---|
| 1단계 | 답을 말한다 | 성공 문장 뒤 OK |
| 2단계 | 글자 완전일치 | 영영 안 맞음 |
| 3단계 | 알림은 나간다 | 받는 쪽은 정상 |
| 4단계 | 기록은 미발송 | 수리 실패로 오독 |
8월 6일 사고 때 남은 문장과 글자 하나 다르지 않은 문장이 그대로 또 남습니다. 이번엔 알림이 진짜로 갔는데도요.
죽이려던 거짓말이 부호만 뒤집혀 살아남은 셈입니다. 앞의 거짓말은 “안 갔는데 갔다고 믿게” 했고, 이번 것은 “갔는데 안 갔다고 믿게” 합니다. 둘 다 다음 사람을 헛다리 짚게 만드는 건 똑같습니다.
검사 63개가 전부 초록불이었는데, 왜 못 잡았을까요
제일 부끄러운 대목입니다. 저 버그를 안고 검사를 돌렸는데 63개가 전부 통과했거든요.
검사할 때 진짜 알림을 보낼 수는 없습니다. 그래서 가짜 부품을 대신 끼웁니다. 그 가짜는 “성공”이라고만 돌려주고 아무 말도 안 했어요.
진짜는 말을 합니다. 그 한 줄 차이가 버그의 전부였는데, 제 가짜 부품이 그 차이를 재현하지 않았습니다.

표로 보기
| 항목 | 값 | 설명 |
|---|---|---|
| 검사 통과 | 63 | 전부 초록불 |
| 도착한 알림 | 0 | 장치를 붙인 뒤 |
| 진짜 서버 연결 | 18 | 가짜 부품 뺀 검사 |
세 번째 숫자가 뒤늦게 나온 겁니다.
제 검사 중에는 “진짜로는 안 보낸다”를 확인하는 검사가 있었습니다. 그런데 그게 하는 일이 자기 파일을 읽어서 “내가 이렇게 써 뒀네” 하고 확인하는 것이었어요. 같은 말을 두 번 하는 것에 가깝습니다.
검증을 맡긴 다른 AI가 그걸 못 믿겠다며 회선 자체를 감시했습니다. 그랬더니 가짜 부품을 뺀 상태에서 진짜 알림 서버로 연결 시도가 실제로 나가고 있었습니다.
막는 장치가 없었다면 시험을 돌리는 동안 제 휴대폰에 열여덟 통이 쏟아졌을 겁니다.
오해하지 마세요. 검사가 쓸모없었다는 이야기가 아닙니다. 검사는 제가 시킨 것을 정확히 했어요. 다만 제가 시킨 것이 실제 상황과 달랐을 뿐입니다.
여기까지가 알림 한 통 이야기입니다. 며칠 뒤엔 경보가 0건인 자리가 나왔습니다.
“경보 0건”이 정상의 증거가 아니었습니다
0건을 저는 오래 좋은 소식으로 읽었습니다.
제 서버는 하루 한 번꼴로 일하던 일꾼을 스스로 죽이고 있었습니다.
정확히는 이렇습니다. 서버 관리자 격인 프로그램이 5초 안에 대답하지 못한 일꾼을 강제로 끕니다. 10일 동안 아홉 번 그랬어요.

표로 보기
| 시점 | 사건 | 설명 |
|---|---|---|
| 16시 40분 00초 | 작업 시작 | 1803개 훑기 시작 |
| 16시 44분 26초 | 500개 진행 | 실패 0개 |
| 16시 44분 50초 | 일꾼 사라짐 | 1303개 증발 |
| 그날 저녁 | 경보 0건 | 한 통도 없었다 |
피해도 실제로 났습니다. 그 시간대에 모으던 자료가 사흘치 통째로 비어 있었어요.

그런데 제 봇에는 감시 장치가 세 겹이나 있었습니다. 셋 다 잘 돌고 있었습니다.

표로 보기
| 감시 장치 | 무엇을 보나 | 이 고장이 보이나 |
|---|---|---|
| 재시작 세기 | 서버가 다시 떴나 | 안 보임 |
| 미발화 경보 | 안 뜬 작업 | 대상 밖 |
| 오류 기록 | 남긴 오류 메시지 | 흔적 없음 |
가운데 줄은 개발기 ⑫에서 만든 장치입니다. 그때 저는 “약속한 시각에 안 뜬 작업”을 잡으려고 그걸 만들었어요.
이건 떴다가 도중에 죽은 작업입니다. 설계상 처음부터 대상이 아니었습니다.
그러니까 이런 겁니다. 화재경보기는 잘 작동하고 있었는데, 집에 난 건 물난리였어요.
그래서 “경보가 0건”은 정상의 증거가 아닙니다. 먼저 물어야 할 건 이거예요. 그 경보가 이 고장을 볼 수 있는 종류인가.
솔직하게 덧붙일 게 있습니다. 왜 그 일꾼이 5초 안에 대답을 못 했는지는 아직 모릅니다. 짐작은 있지만 확인은 못 했습니다. 지금 만든 장치는 원인을 몰라도 작동하도록 만들었고, 원인은 원인대로 계속 봅니다.
감시가 못 보는 것보다 나쁜 경우가 있습니다. 제가 직접 감시의 눈을 가려 둔 자리요.
알람을 끄려고 파일 날짜만 새로 찍었습니다
그 자리가 세 번째입니다. 제가 알람을 조용히 시키려다 만든 것이라 더 뼈아파요.

자료를 모으는 작업이 있는데, 휴일에는 모을 게 없어 0건으로 끝납니다. 그러면 “자료가 안 들어왔다”는 경보가 뜹니다.
그게 성가셔서 저는 파일 시각만 새로 찍는 코드를 넣었습니다. 내용은 그대로 두고 겉면 날짜만 오늘로 바꾼 거죠.
목적 자체는 정당했어요. 문제는 원인을 구분하지 않았다는 점입니다. 휴일이든, 자료를 주는 쪽이 우리를 막아 버렸든, 똑같이 “최신”으로 찍었습니다.

표로 보기
| 항목 | 설명 | 값 |
|---|---|---|
| 겉면 나이 | 파일 시각 기준 | 2일 |
| 속 내용 나이 | 수집 날짜 기준 | 13일 |
속을 열어 보니 700개가 넘는 종목이 전부 13일 전 자료였습니다.
여기서 피해가 두 갈래로 갑니다. 감시 장치는 파일 겉면만 보니 속을 뻔히 두고도 못 잡습니다. 그리고 그 자료를 갖다 쓰는 쪽에는 나이를 확인하는 절차가 아예 없었어요.
바꿔 말하면 유통기한 스티커만 새로 붙인 식품이 아무 검사도 없이 조리대까지 올라간 겁니다.
그럼 이런 건 어떻게 알아챌까요.
그래서 지금은 통로를 한 번 울려 보고 넘어갑니다
세 자리를 다 고쳤습니다. 고친 내용보다 어떻게 확인했는지가 중요합니다.

표로 보기
| 단계 | 내용 | 설명 |
|---|---|---|
| 1단계 | 실발송 2회 | 둘 다 발송 성공 |
| 2단계 | 검사 97개 | 고장 20종 검출 |
| 3단계 | 시험은 임시 자리 | 진짜 알림 보호 |
| 4단계 | 5분마다 신호 | 30분 낡으면 경고 |
세 번째 줄에 사연이 있습니다. 저는 “같은 알림을 하루에 여러 번 보내지 말자”는 규칙을 넣어 뒀거든요.
그런데 시험 삼아 한 번 보내면 그 표시가 남아, 그날 진짜 사고 알림을 막아 버립니다. 확인하려는 행동이 확인하려던 대상을 죽이는 셈이죠. 그래서 시험은 임시 자리에서 합니다.
네 번째 줄도 마찬가지예요. 신호가 낡았을 때 초록불로 접지 않고 “이 목록은 믿을 수 없음”이라고 표시합니다. 모르는 걸 정상으로 바꾸지 않는 게 요즘 제 원칙입니다.
하나 더 있습니다. 운영 중인 일꾼은 일부러 죽여 보지 않았습니다. 그 일꾼이 매도까지 처리하는 자라, 확인하겠다고 끊었다가 이 작업이 막으려던 사고를 제 손으로 낼 뻔했거든요. 대신 똑같이 생긴 판을 따로 만들어 거기서 죽여 봤습니다.
여러분 알림도 마지막으로 울린 게 언제인가요
여기까지가 봇 이야기인데, 이 구조는 여러분 휴대폰에도 그대로 있습니다.

우리는 알림을 켜 뒀다는 사실은 기억합니다. 그런데 마지막으로 언제 울렸는지는 대개 기억 못 하죠.
이게 왜 돈 문제가 되냐면 이렇게 이어지거든요. 휴대폰이나 번호를 바꿉니다. 알림 통로가 조용히 끊깁니다. 그런데 결제도 만기도 연체도 그대로 진행됩니다.
그다음이 진짜입니다. 연체는 신용 평가에서 불리하게 작용하고, 신용 평가가 내려가면 다음에 빌릴 때 조건이 나빠질 수 있습니다. 얼마나 나빠지는지는 기관과 상품마다 다릅니다.

표로 보기
| 알림 통로 | 우리가 아는 것 | 열어 봐야 아는 것 |
|---|---|---|
| 은행 앱 알림 | 켜 둔 것 같다 | 마지막 울린 날짜 |
| 보험 안내 메일 | 받는 것 같다 | 스팸함 확인 |
| 번호 바꾼 뒤 | 그대로일 것 같다 | 예전 번호 여부 |
| 카드 이용 알림 | 다 온다 | 기준 금액 아래 |
마지막 줄은 저도 최근에 알았습니다. 카드사가 정해 둔 기준 금액이 있어서, 그 아래 결제는 처음부터 안 오게 돼 있는 경우가 많거든요.
물론 알림이 안 오는 게 늘 사고는 아닙니다. 정말 아무 일이 없어서 조용한 날이 훨씬 많고요. 제가 드리는 말씀은 조용함만으로는 둘을 구별할 수 없다는 것뿐입니다.
오늘 남은 문장
조용한 건 두 가지를 뜻합니다. 아무 일도 없었거나, 말할 입이 막혔거나.
화면은 그 둘을 구별해 주지 않습니다. 알림 통로는 한 번 울려 봐야 갈리고, 도중에 끊긴 작업은 끝났다는 기록을 따로 세야 갈립니다.
제 봇에서 이 세 자리는 전부 같은 모양이었습니다. 감지하는 쪽은 멀쩡했고, 기록도 예쁘게 남았고, 다만 그게 저한테 도착하는지는 아무도 재지 않았습니다.
오늘 딱 하나만 해 보신다면 이걸 권합니다. 휴대폰 알림함을 열어서 은행이나 카드사에서 마지막으로 온 알림이 언제인지 날짜 하나만 확인해 보세요.
언제였는지 떠오르지 않는다면, 그동안 정말 아무 일이 없었던 걸까요. 아니면 통로가 조용히 끊긴 걸까요. 저는 세 번 다 뒤늦게 알았습니다.
다음 글 예고 — 고장을 재는 자를 누가 재나요
오늘 이야기에는 아직 안 풀린 게 하나 남아 있습니다.
저는 이번에 재는 장치를 여러 개 새로 만들었습니다. 그런데 그 장치들이 제대로 재고 있는지는 또 누가 확인할까요.
실제로 제가 만든 검사 중에는 일부러 낸 고장이 죽긴 죽었는데 엉뚱한 이유로 죽은 것이 있었습니다. 검사는 초록불인데, 재려던 것을 한 번도 안 잰 셈이죠. 다음 편에서 그 이야기를 해 보겠습니다.
※ 이 글은 자동매매 시스템을 만들며 겪은 과정을 기록한 개발기입니다. 특정 종목이나 매매 방법을 권유하지 않으며, 본문의 기록은 실제 돈이 오가지 않는 모의계좌·개발 환경에서 일어난 일입니다. 카드·보험·통신 알림 설정과 신용 평가 기준은 회사와 상품에 따라 다르므로 개별 사안은 해당 기관에 확인하셔야 합니다. 투자 판단과 그 책임은 본인에게 있습니다. 정보·교육·참고용으로만 봐 주세요.


































































































































