당신의 모니터링 봇은 감시 대상 목록을 어디서 읽습니까? 코드 안에서요, 아니면 원천에서요?
제겐 App Store 심사 상태를 감시하는 봇이 있습니다. 두 시간마다 각 앱의 대기 버전, 인앱 이벤트, 커스텀 제품 페이지를 긁어서 이전 스냅샷과 비교하고, 상태가 바뀌면 알림을 쏘고 그날의 로그에 한 줄 적습니다. 몇 달째 조용히 잘 돌았습니다.
어제 새 앱 하나를 출시 배선하다가 — 랜딩, 디렉터리, 사이트맵, 매니페스트를 차례로 손보다가 — 이 봇의 코드를 열었습니다. 그리고 목록을 셌습니다.
APPS = [
("aurnia", "6764247734"), ("biasly", "6762536099"),
... # 손으로 적은 21줄
]계정에는 앱이 44개 있었습니다.
23개는 아무도 안 보고 있었다
API로 전체 목록을 받아 코드와 대조하니 빠진 앱이 23개였습니다. 지도 산책 게임, 작명 앱, 한자 학습 앱, 이명 트래커, 끝말잇기 게임… 그리고 바로 어제 심사에 넣은 신규 앱도 그 안에 있었습니다. 심사가 통과되든 반려되든 저는 알림을 못 받았을 겁니다.
여기서 중요한 건, 봇이 한 번도 거짓말을 하지 않았다는 겁니다.
- 두 시간마다 정확히 실행됐습니다.
- 종료 코드는 늘 0이었습니다.
- "변경 없음"이라고 출력했고, 그건 21개 앱에 대해서는 사실이었습니다.
exit 0이 거짓말한 게 아닙니다. 질문이 좁았을 뿐입니다. 예전에 모든 지표가 초록인데 결과가 0이던 대시보드를 쓴 적이 있는데, 그때는 지표가 잘못된 것을 재고 있었습니다. 이번엔 지표가 덜 재고 있었습니다. 후자가 더 조용합니다. 화면에 빈칸이 안 생기거든요.
당신이라면?
23줄을 마저 타이핑하시겠습니까, 아니면 21줄을 지우시겠습니까?
저는 지웠습니다. 한 줄을 더 추가하는 순간, 다음 신규 앱에서 같은 일이 또 벌어지도록 확정하는 것이기 때문입니다. 목록이 손으로 유지되는 한 목록은 반드시 뒤처집니다. 문제는 21이 아니라 하드코딩이었습니다.
리스트를 지우고 원천을 읽게 만들기
바꾼 건 네 가지입니다.
1. 목록을 API에서 읽는다. GET /v1/apps 한 번이면 계정의 전 앱이 나옵니다. 앞으로 앱을 새로 내도 코드를 안 고쳐도 됩니다.
2. 기존 키를 보존한다. 로그와 상태 파일은 aurnia, zone2 같은 짧은 키로 앱을 부릅니다. 새 키 규칙(번들 ID 마지막 조각)을 그냥 적용하면 21개 앱의 과거 상태가 전부 "처음 보는 앱"이 돼서 알림이 폭발합니다. 그래서 기존 21개의 ID→키 매핑만 남겨두고, 나머지는 자동 생성하게 했습니다.
3. 비교를 정적 목록이 아니라 두 스냅샷의 합집합으로 한다. 이전엔 for key in APPS로 돌면서 비교했습니다. 목록에 없으면 데이터가 있어도 비교되지 않는 구조였습니다. 이제 이전 스냅샷과 현재 스냅샷의 키를 합쳐서 순회합니다.
4. 상태 파일을 조용히 채워둔다. 그냥 다음 실행에 맡기면 새로 감시되는 23개 앱의 모든 버전·이벤트가 "신규 발견"으로 쏟아집니다. 알림 수백 줄은 그 자체로 다음번 진짜 신호를 묻습니다. 그래서 새 앱들의 현재 상태만 먼저 스냅샷에 채워 넣고, 실제 변화는 다음 폴링부터 잡게 했습니다.
같은 병을 같은 날 하나 더 찾았다
이 봇을 고친 날, 공개 앱 디렉터리 페이지도 손보고 있었습니다. 페이지는 "앱 40개"라고 적혀 있었는데, 실제 카드는 41개였습니다.
원인이 똑같았습니다. 몇 주 전 앱 하나를 "NEW" 배지 행으로 목록 맨 위에 얹으면서, 아래 숫자들을 손으로 안 고친 겁니다. 카테고리별 개수도 하나 어긋나 있었습니다. 사람이 유지하는 숫자는 사람이 바쁠 때 틀립니다. 그래서 카운트도 행에서 계산하도록 바꿨습니다.
자가진단 3개
- 감시 목록이 코드 상수인가, 원천 조회인가? 상수라면, 마지막으로 항목이 추가된 게 언제인지
git log로 확인해 보세요. - 감시 대상 개수와 실제 자산 개수를 한 줄로 비교하는 지표가 있는가? 두 숫자가 화면에 같이 안 뜨면 어긋나도 아무도 모릅니다.
- 감시 범위를 넓힐 때 알림 폭발을 막는 장치가 있는가? 없으면 범위를 넓히는 순간의 소음 때문에 다음부터 범위를 안 넓히게 됩니다.
솔직한 부분
이 봇은 정확히 설계된 대로 동작했습니다. 버그는 코드가 아니라 제 손목에 있었습니다 — 앱을 새로 낼 때마다 여기 한 줄을 추가하겠다는 약속을, 저는 23번 지키지 못했습니다. 자동화에 사람의 성실성을 요구하는 지점이 남아 있으면, 그 지점이 결국 실패 지점이 됩니다.
지금 당신의 모니터가 몇 개를 보고 있는지 세어보세요. 그리고 원천에서 개수를 한 번 조회해 보세요. 두 숫자가 다르면, 그게 오늘의 버그입니다.