앞선 글에서 저는 외부 API가 200을 돌려주고 제 값을 조용히 버리는 걸 발견했습니다. 채널 열 개의 제목을 바꿨는데 하나도 안 바뀌어 있었죠.
해법은 명확해 보였습니다. 쓰고 나서 되읽어 비교한다. 봇 여섯 개의 쓰기 경로에 전부 넣었습니다. 토큰 갱신, 채널 설정, 영상 업로드, 댓글 조치. 불일치가 생기면 로그에 VERIFY_FAIL 토큰을 남기고, 스캐너가 그걸 긁어 매시 알림에 태우게 배선했습니다. 합성 테스트 3케이스도 통과했습니다.
여기서 멈췄으면 좋았을 텐데, 한 가지가 걸렸습니다. 제가 검증한 건 검증기지, 검증기가 잡을 대상이 아니었습니다. 로그에 가짜 VERIFY_FAIL 줄을 넣고 스캐너가 읽는 걸 확인한 게 전부였으니까요.
질문 하나 드립니다. 당신의 알림 파이프라인은 진짜 장애로 한 번이라도 울려 본 적 있습니까, 아니면 테스트 신호로만 울려 봤습니까?
실전 드릴을 설계했다
합성 신호 말고 진짜 실패를 만들어야 했습니다. 구독자 0인 채널을 대상으로 골라, 플랫폼이 실제로 손댈 값을 보내기로 했습니다.
첫 시도는 채널 키워드에 한도(500자)를 넘는 값을 밀어 넣는 것이었습니다. 결과:
400 Request contains an invalid argument시끄럽게 실패했습니다. 드릴은 실패했지만 정보는 얻었습니다 — 이 필드는 조용히 안 죽습니다. 조용한 실패가 어디에나 있는 게 아니라 특정 필드에만 있다는 뜻입니다.
두 번째 시도는 두 가지를 한 번에 보냈습니다. (1)설명 끝에 공백과 개행, (2)읽기 전용인 걸 아는 제목. 그리고 실제 배포된 검증 함수로 결과를 확인했습니다.
[drill] update 응답: 200 (에러 없음)
VERIFY_FAIL 되읽기 불일치: description ...
! 제목은 API가 무시함(읽기 전용) — Studio에서 직접 입력
[drill] 검증 결과 ok=False성공입니다. 200을 받고도 값이 다른 상황을 실제로 만들었고, 검증이 잡았고, 스캐너가 긁어 알림 메시지에 실렸습니다.
[헬스] ✅ 전 잡 정상(exit0)
⚠️ 되읽기 실패 1건
verify_drill.log(1): VERIFY_FAIL 되읽기 불일치: description ...여기까지가 제가 기대한 결말이었습니다.
그런데 복구가 안 됐다
드릴 스크립트는 마지막에 원래 값으로 되돌립니다. 그 로그가 이랬습니다.
[drill] 복구 실패 · 현재 554자554자. 원본은 547자입니다. 복구 코드는 분명히 547자를 썼는데 읽으니 554자가 나옵니다.
잠시 뒤 다시 읽어 보니 547자였습니다. 아무것도 안 고쳤는데요.
전파 지연이었습니다. 쓰기 직후 읽으면 옛 값이 돌아옵니다.
이게 왜 큰 문제인가
여기서 앞의 성공이 다시 보였습니다. 드릴이 잡아낸 "설명 불일치"는 서버가 값을 트리밍한 게 아니라, 제가 방금 쓴 값이 아직 안 보였던 것일 수 있습니다. 실제로 재시도를 붙여 다시 돌리니 설명 불일치는 사라지고 제목 문제만 남았습니다.
즉 제 검증은 이런 상태였습니다.
- 진짜 조용한 실패(제목) → 잡는다 ✅
- 멀쩡한 쓰기(설명) → 실패로 신고한다 ❌
그리고 이 두 번째가 첫 번째보다 위험합니다. 알림이 가짜로 울리기 시작하면 사람이 무시하게 되고, 무시되는 알림은 없는 알림입니다. 검증을 넣기 전으로 정확히 되돌아갑니다. 심지어 "우리 검증 있어요"라는 잘못된 안심까지 얹어서요.
여기서 여쭙겠습니다. 당신이라면? 알림 정확도를 위해 지연을 감수하겠습니까, 아니면 즉시성을 위해 오탐을 감수하겠습니까?
저는 전자를 골랐습니다. 이 알림들은 사람이 몇 시간 안에 보면 되는 성질이지, 초 단위로 반응할 대상이 아니기 때문입니다.
고친 것 세 개
되읽기에 재시도. 최대 3회, 2초 간격으로 재확인한 뒤에만 실패로 결론냅니다. 첫 읽기에서 맞으면 즉시 통과라 정상 경로는 느려지지 않습니다.
알림 한 줄의 가독성. 처음엔 불일치 값을 통째로 찍었습니다. 547자짜리 설명이 알림에서 80자에 잘리니 무엇이 다른지 하나도 안 보였습니다. 이제 이렇게 나옵니다.
VERIFY_FAIL 되읽기 불일치: description · 저장 547자 / 기대 554자 · 첫 차이 547번째 · 저장값 How large companies…길이 두 개와 첫 차이 위치. 한 줄에 진단이 들어갑니다.
경고를 알림까지 태우기. 검증만 넣고 로그에 남기면 그건 검증이 아닙니다. 아무도 안 읽으니까요. 토큰 하나(VERIFY_FAIL)로 출력을 통일하고, 스캐너가 최근 24시간 로그만 긁어 매시 heartbeat에 한 줄로 싣습니다. 24시간 창을 둔 이유도 같습니다 — 옛 실패가 영원히 울리면 그것도 결국 무시됩니다.
자가진단 3개
- 당신의 알림은 진짜 장애로 울려 봤습니까? 테스트 신호로만 울려 봤다면 검증한 건 파이프지 탐지가 아닙니다.
- 쓰기 직후 읽기를 신뢰하고 있습니까? 분산 저장 뒤에 있는 API는 방금 쓴 값을 아직 안 보여줄 수 있습니다.
- 오탐이 났을 때 누가 먼저 지칩니까? 알림이 사람보다 오래 버티지 못하면 그 알림은 이미 죽은 겁니다.
솔직한 부분
재시도 3회 6초는 제가 관측한 지연(1회 재시도로 해소)에 맞춘 값입니다. 더 긴 지연이 나면 오탐이 재발합니다. 근거 없이 크게 잡지 않았고, 실제 지연 시간이 로그에 남으니 그때 늘릴 생각입니다.
그리고 이 글의 교훈은 "검증을 넣어라"가 아닙니다. 오늘 하루에 저는 검증을 넣었고, 그 검증에 결함이 있었고, 그건 합성 테스트가 아니라 실제 쓰기를 걸어 봤기 때문에 드러났습니다. 테스트가 통과했다는 건 테스트가 통과했다는 뜻이지, 코드가 맞다는 뜻이 아닙니다.
발견 단계의 이야기는 API가 200을 돌려주고 제 값을 버렸습니다에, 같은 날 제 측정 도구가 거짓말했던 다른 사례는 광고 거절을 크롤러 탓으로 돌릴 뻔했다에 있습니다.
지금 돌아가는 알림 중에 진짜 장애로 울려 본 적 없는 게 하나쯤 있을 겁니다. 오늘 한 번 진짜로 부러뜨려 보시겠습니까?