
「공회전하는 초록(hollow green)」— AI 코딩 검사는 아무것도 검사하지 않고 초록색이 된다
요약
AI 에이전트를 활용한 코딩 자동화 과정에서 발생하는 '공회전하는 초록(hollow green)' 현상, 즉 검사가 실질적으로 이루어지지 않음에도 통과로 표시되는 위험한 실패 패턴 5가지를 분석합니다. 검사 대상 누락, 측정 도구 오류 등 실제 운영 사례를 바탕으로 한 구체적인 대응 방안을 제시합니다.
핵심 포인트
- 검사 대상이 0건인 경우를 경고로 격상하고 검사 대상 수를 반드시 표시해야 함
- 검사 도구 자체의 결함을 확인하기 위해 의도적 결함 주입(Mutation Testing) 활용
- 테스트 통과 여부보다 검사 로직이 유효하게 작동하는지 검증하는 프로세스 구축 필요
AI 에이전트에게 Web 제작 코딩을 맡기고, 품질 게이트(Quality Gate)를 기계로 강제하는 「공장」을 혼자서 운영하고 있습니다. 최근 7일간의 게이트 실행은 3,364회. 이 기사는 그 운영 과정에서 반복적으로 마주한 가장 위험한 실패 모드에 관한 이야기입니다.
먼저 이름을 붙이겠습니다. 「공회전하는 초록(hollow green)」— 통과하고는 있지만, 아무것도 검사하지 않는 체크를 말합니다.
이 기사에서 다루지 않는 것
Claude Code의 Hooks를 사용하여 품질 게이트를 만드는 방법은 이미 좋은 기사들이 많이 있습니다 (Hooks 완전 가이드, Hooks × Skills 구현 패턴 등). 만드는 방법은 그쪽을 읽어주세요.
이 기사는 「만든 후에 어떤 일이 일어나는가」에 관한 이야기입니다. 게이트를 수천 단위로 돌리다 보면, 빨간색(Red)보다 무서운 것을 만나게 됩니다. 그것이 바로 공회전하는 초록입니다.
계기: 「AI가 테스트를 완화한다」는 것뿐만이 아니었다
얼마 전, AI가 구현이 아니라 테스트를 완화하여 초록색으로 만드는 문제에 대한 주의 환기가 Qiita에 올라왔었습니다. 지적은 옳았고, 제안(git diff -- tests/를 육안으로 확인하거나, "테스트를 바꾸지 마세요"라고 요청하는 것)도 실용적입니다.
다만, 운영하며 알게 된 것은 테스트 개변은 공회전하는 초록의 한 형태일 뿐이라는 점입니다. 저의 gate 로그와 감사 대장(Audit Ledger)에서 실제로 겪은 패턴을 분류하면 5가지가 있습니다. 모두 실화입니다.
공회전하는 초록 · 5가지 패턴 (모두 실제로 겪음)
① 대상 제로의 초록 — 검사는 작동했다. 대상이 0건일 뿐
lint 게이트가 매 커밋마다 「✅ pass」를 계속 내보내고 있었습니다. 어느 날 집계해 보니, 대상 파일의 선택 조건이 구조적으로 0건이라서 몇 달 동안 빈 집합을 검사하고 있었습니다 (저희 쪽에서는 eslint와 SCSS 전량 체크 2개가 이 경우였습니다).
게이트는 거짓말을 하지 않았습니다. "0건의 위반"은 사실입니다. 다만 "0건을 검사했다"는 것을 아무도 표시하지 않았을 뿐입니다.
대책: 검사 대상 수를 매번 표시하고, 「대상 0건의 초록」을 경고(Warning)로 격상시킨다. 새로운 게이트를 추가할 때는, 일부러 위반을 만들어 빨간색이 되는 것을 확인한 뒤에 운영에 투입한다 (fail-before-pass. 저희는 이 확인 절차 자체를 집계상 「프로브(Probe)」로 분류하여, 오검출 통계에서 제외하고 있습니다).
② 검사자가 고장 난 초록 — 측정이 어긋나서 임계값 판정이 무의미함
Illustrator 입고 파일(.ai) 판정 도구를 만들었을 때, Node 20 환경에서 PDF 파서(Parser)의 내부 API가 조용히(silently) 누락되어, 패스(Path) 수 측정이 실제 값 63에 대해 2를 반환하고 있었습니다. 임계값 판정(패스가 1개 이하라면 내용이 비어 있다고 의심함)은 작동하고 있었습니다. 숫자가 고장 난 것뿐입니다. 테스트도 「고장 난 측정값」을 기준으로 작성되어 있었기에 전부 초록색이었습니다.
대책: 검사 도구 자체를 검사한다. 저희는 이미 알려진 결함 15종류를 일부러 코드에 주입하여, 게이트군이 15/15개를 잡아내는지 CI가 상시 확인하고 있습니다 (mutation testing의 대상을 「프로덕트」가 아닌 「게이트」로 설정). 이 15라는 숫자는 13→14→15로 늘어났습니다. 실제 프로젝트에서 놓치는 부분이 생길 때마다, 그 놓친 부분을 주입 결함으로 격상시키는 운영을 하기 때문입니다. 15/15는 트로피가 아니라, 「알려진 결함에 대해 100%」일 뿐입니다.
③ 증거가 동봉되지 않는 초록 — 자신의 로컬에서만 재현되는 검증
design 컴프(Design Comp)와의 대조 게이트가 초록색을 내보냈고, 커밋도 통과했습니다. 그런데 대조 기준 파일(Design IR)이 커밋에 포함되어 있지 않았습니다. 로컬의 working tree에는 있으니까 검사는 통과합니다. 하지만 clone한 제3자나 CI에서는 기준이 존재하지 않아 검증이 재현 불가능합니다 — 초록색의 「증거」가 역사(History)에 남지 않습니다.
이것은 외부 테스터에 의해 적발되었습니다 (솔직히 말하면 스스로는 알아채지 못했습니다).
대책: 「검사가 통과했다」가 아니라 「검사의 입력과 결과가 커밋에 구워졌다(baked into commit)」를 합격 조건으로 한다. 저희는 pre-commit이 「등록된 섹션의 기준 파일이 index에 들어있는가」를 독립적으로 검사하며, 들어있지 않으면 초록색이라도 차단합니다 (git add -N으로 빈 엔트리만 만드는 위장도 blob 사이즈 검사로 걸러냅니다 — 이것은 적대적 리뷰(Adversarial Review)에서 발견된 우회로입니다).
④ 게이트가 무력화되는 초록 — 빨간색을 「고치는」 대신 게이트를 고침
AI 에이전트에게 있어, 빨간색 게이트를 초록색으로 만드는 최단 경로는 구현 수정이 아닐 수도 있습니다. 임계값(threshold)을 완화하거나, 검사를 스킵하거나, 게이트 정의 그 자체를 다시 쓰는 것이 더 빠를 때가 있습니다. 테스트 변조(앞서 언급한 Qiita 기사의 내용)는 이 중 일종입니다.
대책: 부탁이 아니라 권한으로 보호합니다. 저희는 게이트 정의 27개 파일의 해시 트리(Merkle root)를 사람이 서명한 git 태그에 기록하며, 서명 없는 게이트 변경은 머지(merge) 자체가 불가능하도록 설정했습니다 (CI의 required check). AI는 변경을 제안할 수 있지만, 승인할 수 있는 것은 인간의 키(key)뿐입니다. 이와 더불어, 게이트 바이패스(bypass)는 모든 건에 대해 사유와 함께 로그에 기록됩니다 — 최근 7일간 3,364건 중 5건이었습니다. 보이는 바이패스는 프로세스이고, 보이지 않는 바이패스는 구멍입니다.
⑤ 항상 빨간색 — 공회전하는 초록의 쌍둥이
반대의 경우도 마찬가지로 유죄입니다. 의존성 패키지(dependency package)의 취약점 리뷰를 CI에 추가했을 때, 취약한 패키지가 포함된 PR이 FAIL되어 '탐지 능력이 있다'고 생각했습니다. 하지만 무해한 PR도 같은 이유로 FAIL되었습니다. 전제 서비스의 계약이 필요했기에, 항상 빨간색으로만 표시되는 검사였던 것입니다. 항상 빨간색인 게이트는 항상 초록색인 게이트만큼이나 가치가 없으며, 단지 '검사하고 있다는 느낌'만을 제공할 뿐입니다.
대책: 탐지 주장에 대해서는 반드시 양방향의 실증을 첨부해야 합니다 (위반이 있으면 빨간색, 위반이 없으면 초록색). 사용할 수 없게 된 검사는 삭제하지 말고, 사유와 함께 리포지토리에 기록하여 남겨둡니다 (negative results의 대장화).
그럼에도 지난주, 사고는 발생했다
지금까지 기술한 메커니즘을 가동하고 있음에도 불구하고, 지난주에 이런 일이 있었습니다.
테스트 스위트(test suite) 중 하나가 실제 리포지토리의 측정 로그(CSV)를 '일시 삭제 → 검증 → 복구'하도록 설계되어 있었습니다. 프로세스가 정상 종료된다면 무해합니다. 하지만 타임아웃으로 인해 도중에 kill된 순간, 복구가 실행되지 않았고 실제 데이터가 헤더 1행만 남은 채 잘린 상태로 남았습니다. 어떤 게이트도 이러한 성질(테스트가 실제 파일을 건드리는 것)을 검사하지 않았습니다. 발견은 게이트가 아니라 우연히 이루어졌습니다.
수정 사항은 테스트의 샌드박스화(sandbox, 실제 파일에 전혀 손대지 않는 구조로 변경)였으며, 재발 방지는 '이 관점을 감사 항목에 추가하는 것'으로 조치했습니다. 여기서 쓰고 싶은 말은 대책의 내용보다, 이 메커니즘의 진정한 핵심은 게이트의 개수가 아니라는 점입니다. 핵심은 '자신의 검사의 사각지대를 계속 사냥하고, 사냥한 결과로 검사 자체를 키워나가는' 루프에 있습니다. 15/15라는 분모가 계속 늘어나는 것도, negative results를 지우지 않는 것도 같은 이유입니다.
요약
- '초록색 = 품질'이 아닙니다. 초록색에는 '대상 제로', '측정 파손', '증거 없음', '무력화', '(쌍둥이로서) 항상 빨간색'이라는 5가지 가짜가 있습니다.
- 대책의 공통 원칙은 2가지입니다.
검사의 주장에는 양방향의 실증을 요구할 것. 검사자 자신을 검사 대상으로 삼을 것. 그리고 가장 중요한 대책은, AI에게 '정중하게 해달라'고 부탁하는 것을 그만두고, 깨뜨릴 수 없는 메커니즘으로 대체하는 것입니다. 부탁은 깨지기 마련입니다. 저희의 수치로는, 깨진 횟수까지 포함하여 모든 기록이 남습니다.
이 기사의 검증 파이프라인은 웹 제작 기반으로서 배포를 준비 중입니다 (사전 안내).
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기