알림과 타이머가 소음이 되지 않게

품앗이가 AI와 함께 업무 자동화를 하며 남긴 실패 기록을 다른 조직이 다시 쓸 수 있게 옮긴 글입니다. 묶음 전체는 API 없는 업무 프로그램 자동화에서 볼 수 있습니다.

요지

감시 스크립트, 정기 작업, 경보는 한 번 걸어 두면 사람이 보지 않는 곳에서 계속 돕니다. 그래서 잘못 만들면 오래, 조용히, 크게 틀립니다. 우리 기록에는 한 달 동안 같은 경보를 수천 통 보낸 감시, 멀쩡한 서버를 지우려고 천 번 넘게 방아쇠를 당긴 복구, 끝난 일을 600번 넘게 다시 한 타이머가 있습니다. 로그는 그동안 대부분 초록색이었습니다.

1. 알림은 처음부터 억제와 묶음으로

오진 하나를 되풀이해 쏜 감시 알림이 메일함에 4,524통 쌓였습니다. 5분마다 두 통씩이었고, 그 사이 진짜 업무 메일이 묻혔습니다. 알림을 많이 보내는 것은 성실함이 아닙니다. 읽히지 않는 알림은 0통과 같고, 받은편지함을 덮으면 0통보다 나쁩니다.

  • 같은 제목의 알림은 간격을 둡니다. 억제 값은 부르는 쪽마다 조건을 다는 것이 아니라 알림 함수 안에 둡니다.
  • 억제한 시각은 파일에 남깁니다. 주기마다 새로 떴다 죽는 감시 프로그램이면 메모리에 둔 억제는 아예 걸리지 않습니다. 4,524통의 직접 원인이 이것이었습니다.
  • 억제한 알림은 버리지 말고 세어서 하루 한 통 묶음에 넣습니다. 「같은 경보 287회」가 한 통으로 가면 그게 더 많은 정보입니다.
  • 즉시 알림은 사람이 지금 움직여야 하는 것만 보냅니다.
  • 이미 쌓인 알림은 지우지 말고 라벨을 붙여 받은편지함에서 빼 두고, 지울지는 사람이 정합니다.

2. 파괴적인 복구는 신호 하나로 당기지 마라

같은 감시 스크립트가 30일 동안 「세션 재생성」을 1,806번 발사했습니다. 세션을 다시 만들면 그 위에서 돌던 AI 모델 서비스가 통째로 사라지는데, 서버는 멀쩡했습니다. 방아쇠는 GPU 사용률 값이 「찾을 수 없음」으로 온 것이었습니다. 숫자를 못 읽으니 -1이 되었고, 코드는 -1을 「접속 실패」로, 접속 실패를 「세션이 회수됐다」로 읽었습니다. 1,806번 모두 복구 과정의 다른 단계에서 우연히 멈춘 덕에 서비스가 살았습니다. 성공률이 0이어서 살았다는 것은 설계가 아닙니다.

  • 복구의 끝이 삭제, 재생성, 초기화면 조건을 여럿 겁니다. 이 경우엔 「접속 실패」, 「다시 시도해도 접속 실패」, 「서비스가 실제로 응답하지 않음」 셋이 다 맞을 때만 당기도록 고쳤습니다. 서비스가 응답하고 있으면 재생성 대신 「살아 있어 보류」 알림만 보냅니다.
  • 측정 실패를 방아쇠로 쓰지 않습니다. 「값이 왔다」, 「접속은 됐는데 값이 없다」, 「접속 자체가 안 됐다」를 셋으로 가르고, 값이 없을 때는 읽히는 다른 지표로 판단합니다.
  • 재시도 횟수의 상한도 파일에 둡니다.
  • 상한은 남의 서버에도 빚입니다. 기록을 보니 이 감시는 5분마다 외부 관리 화면에 로그인을 던지고 있었습니다.

3. 완료 확인 없는 타이머는 끝난 일을 영원히 다시 한다

한 번만 하면 되는 자료 회수 작업을 30분 타이머에 걸었습니다. 작업은 첫날 성공했고 「완료」 표시 파일도 썼습니다. 그런데 그 표시를 읽는 줄이 없었습니다. 한 달 동안 613번, 매번 8.5GB를 다시 풀었고, 그때마다 성공 알림을 보냈습니다. 원본 파일을 지우지 않아 매 실행이 「새 파일 발견」으로 시작한 것도 원인이었습니다. 그동안 로그는 전부 초록색이었고, 실패한 것이 아니라 끝난 일을 계속 성공하고 있었습니다.

  • 한 번만 할 일을 타이머에 걸 때는 첫 줄에서 완료 표시를 확인하고, 있으면 바로 끝냅니다.
  • 「입력이 사라지면 끝」으로 설계했으면 마지막 단계에서 입력을 지웁니다.
  • 반복 작업의 성공 알림은 한 번만 보냅니다.
  • 같은 결과가 계속 되풀이되면 그건 진행이 아니라 헛바퀴입니다. 로그에서 같은 완료 문구가 몇 번 나왔는지 세어 봅니다.

4. 경보가 진짜 실패 문구로 울리는지 시험하라

백업 경보 문구에 「63MB < 10MB」처럼 부등호가 들어가자, 메시지의 서식 기능이 그 기호를 해석하다 거부해 메시지가 아예 나가지 않았습니다. 보내는 도구는 그 실패에도 종료 코드 0을 돌려줬습니다. 평소 시험 메시지는 잘 갔으니 「알림 잘 되네」 하고 넘어갔고, 가장 중요한 실패 경보만 골라 침묵하는 상태였습니다.

  • 서식이 필요 없는 경보에는 서식 기능을 켜지 않습니다.
  • 보낸 뒤에는 응답 코드를 검사해 실패를 기록합니다.
  • 경보는 정상 문구가 아니라 진짜 실패 상황의 문구로 실제로 울려 봅니다.

5. 같은 설정을 동시에 고치면 하나가 죽는다

배포 작업 둘이 같은 기계의 같은 터널 설정 파일을 96초 차이로 고쳤습니다. 늦게 쓴 쪽이 앞쪽의 내용을 덮으면 주소 등록은 남아 있는데 연결 규칙이 사라집니다. 그런데 두 작업 모두 자기 주소만 확인하니 둘 다 「성공」으로 끝납니다.

  • 같은 기계의 같은 파일을 만질 작업은 동시에 돌리지 않습니다.
  • 쓰기 직전에 파일이 읽었을 때와 같은지 다시 확인하고, 잠금을 겁니다.
  • 검증할 때는 같은 설정에 걸린 다른 주소까지 확인합니다.
  • 되돌릴 때는 백업본을 통째로 덮지 말고 자기가 넣은 줄만 뺍니다. 백업본에는 그사이 남이 넣은 내용이 없습니다.

6. 권한 질문이 반복되면 허용 목록이 아니라 모드를 본다

AI 도구가 같은 종류의 허락을 계속 물어서 허용 목록에 도구 23개를 넣었는데, 두 번째 도구에서 또 물었습니다. 허용 목록은 이미 쓴 도구만 막아 주는 사후 대응입니다. 같은 종류의 질문이 두 번 나오면 원인이 어느 층에 있는지 봅니다. 안전 장치(훅)가 막는 것인지, 권한 모드가 비어 있는 것인지, 특정 도구에만 걸리는 별도 규칙이 있는 것인지를 가릅니다. 「안 묻는 것」과 「안 막는 것」은 다른 층이라 모드를 바꿔도 안전 장치는 그대로 남습니다. 바꾼 뒤에는 실제로 안 묻는지 한 번 호출해 봅니다. 브라우저 도구에서 생긴 구체적인 사례는 Claude in Chrome 이 매번 허용을 묻는 문제에 따로 정리했습니다.

도구 메모

  • 헛바퀴 점검: journalctl -u <서비스> | grep -c "<완료 문구>"
  • 한 번만 할 일의 첫 줄: [ -f "$상태폴더/DONE" ] && exit 0
  • 경보 발송은 응답 코드를 받아(curl -w '%{http_code}') 200이 아니면 기록한다.

관련 노드