AI 감사 도구가 모든 걸 확인한다고 어떻게 확신하죠? 제 도구는 확신했지만, 실제론 아니었습니다.
How do you know your AI audit tool actually checked everything? I was fairly confident that my skill suite did. It didn't.
핵심 요약
AI 에이전트가 코드의 '부재'를 감지하지 못하는 문제를 해결하기 위해 '전체 열거 후 검증' 방식을 도입한 경험담.
- 감지 한계 — AI가 잘못된 패턴의 존재는 찾지만, 올바른 패턴의 부재는 놓치는 문제 발생함.
- 열거 후 검증 — 특정 파일들을 먼저 나열한 뒤 검증하는 방식으로 누락 없는 스캔 구현함.
- 불확실성 랭킹 — 검증 결과 중 의심스러운 항목을 우선순위별로 정렬해 효율적인 코드 리뷰 수행함.
- 도구의 한계 — 린터가 알지 못하는 프로젝트별 규칙은 커스텀 에이전트가 필요하지만 신뢰성 확보가 관건임.
커스텀 스캐닝 도구나 에이전트를 만드는 분들 중에 이 문제에 대해 고민해 본 사람이 있는지 궁금합니다. 저도 제 도구가 코드베이스의 위반 사항 절반 이상을 자신 있게 놓치는 걸 보기 전까지는 전혀 생각하지 못했던 부분입니다.
저는 Multiplatform iOS/macOS 프로젝트의 디자인 시스템 문제를 스캔하는 Claude Code 스킬(재사용 가능한 프롬프트 기반 도구)을 만들고 있습니다. 이 도구들은 알려진 안티 패턴을 grep으로 찾고, 파일을 읽고, 결과를 보고합니다. 그중 하나는 특정 시각적 처리가 필요한 아이콘을 스캔합니다. 예를 들어 단색 배경, 흰색 아이콘, 드롭 섀도우 같은 것들이죠. 디자인 시스템에는 정의되어 있지만 개발자들이 깜빡하고 적용하지 않는 그런 종류의 것들입니다.
도구는 10개 파일에서 31개의 위반 사항을 찾아냈습니다. 저는 그것들을 모두 수정하고, 다시 빌드하고, 앱을 열었습니다. 그런데 화면에 40개의 위반 사항이 더 있었습니다. 도구는 자신 있게 결과를 보고했고, 저는 그에 따라 조치를 취했지만, 실제 문제의 절반 이상은 도구의 눈에 띄지 않았던 겁니다. 제가 직접 앱을 클릭하며 확인하지 않았다면, 깨끗하다고 생각하고 커밋했을 것입니다.
근본 원인은 복잡하지 않았습니다. 많은 아이콘에 명시적인 색상 코드가 없었습니다. 기본적으로 시스템 강조 색상을 상속받고 있었죠. grep으로 찾을 수 있는 게 아무것도 없었습니다. .foregroundStyle(.blue)도, .opacity(0.15)도 없었고, 코드 어디에도 "나는 그냥 아이콘이야"라고 말하는 부분이 없었습니다. 아이콘은 그냥 파란색으로 보일 뿐, 검색 가능한 안티 패턴은 없었습니다.
도구는 잘못된 것처럼 보이는 것들을 찾고 있었습니다. 아무것도 아닌 것처럼 보이는 것들은 찾을 수 없었던 거죠.
솔직히 말해서, 이것들은 단순한 grep-and-report 스크립트가 아닙니다. 이미 결과에 대한 신뢰도 태깅, 나중에 더 정확한 결과가 나오면 이전의 오탐을 철회하는 교차 단계 검증, 가장 위험한 영역에 집중하는 위험 기반 스캔 같은 기능들을 수행합니다. 그런데도 이런 일이 발생했습니다. 저는 Swift 동시성 패턴, API 모범 사례, 접근성 요구 사항 같은 알려진 프레임워크 규칙에 대해 감사하는 도구들도 실행합니다. 그런 도구들은 규칙이 보편적이고 잘 정의되어 있기 때문에 철저할 수 있습니다. 하지만 격차는 프로젝트별 관례, 즉 디자인 시스템이나 내비게이션 패턴 같은 곳에 존재합니다. 규칙은 여러분이 만드는 것이고, 여러분은 그 규칙이 나타나는 모든 코드 형태를 다 설명하지 못했을 수도 있습니다.
그때 진짜 문제가 무엇인지 깨달았습니다. 이건 grep의 문제가 아닙니다. AI 에이전트에게 프로젝트 규칙을 가르치고 그 결과물을 신뢰할 때 발생하는 문제입니다. 에이전트는 여러분이 설명한 모든 안티 패턴을 부지런히 찾을 것입니다. 하지만 위반 사항에 코드 시그니처가 없다면, 즉 잘못된 패턴의 '존재'가 아니라 올바른 패턴의 '부재'라면, 에이전트는 그것을 그냥 지나치고 모든 게 괜찮다고 말할 것입니다.
저는 도구가 스캔하는 방식을 두 가지로 변경했습니다:
열거하고, 검증하라. 나쁜 패턴을 grep으로 찾아서 일치하는 항목을 보고하는 대신, 대상이 포함된 모든 파일(제 경우에는 아이콘이 있는 모든 파일)을 나열한 다음, 각각 올바른 패턴이 있는지 확인하십시오. 누락된 파일을 보고하십시오. grep 방식은 31개의 위반을 찾았지만, 열거 방식은 71개를 찾았습니다. 같은 코드베이스, 같은 오후에 말이죠.
불확실한 결과를 랭킹하라. 열거 방식은 "올바른 패턴을 찾을 수 없음"이라는 결과를 많이 만들어냅니다. 어떤 것은 진짜 위반이고, 어떤 것은 정당한 예외입니다. 저는 그것들을 의도적인 것일 경우 얼마나 놀랄지에 따라 정렬합니다. 같은 파일에 이미 확인된 위반이 있는지, 형제 파일들은 올바른 패턴을 사용하는지, 어떤 종류의 뷰인지 등을 확인합니다. 이렇게 하면 거의 확실한 문제들의 짧은 목록과, 한번 훑어볼 만한 긴 목록을 얻을 수 있습니다.
누군가는 "그냥 린터를 써"라고 말할 거라는 걸 압니다. 린터는 그들이 아는 것들에 대해서는 훌륭합니다. 하지만 SwiftLint는 제 프로젝트가 아이콘을 채워진 RoundedRectangle이 있는 ZStack으로 감싸야 한다는 것을 모릅니다. ESLint는 여러분 팀의 카드 컴포넌트가 특정 그림자를 가져야 한다는 것을 모릅니다. 이런 것들은 린터의 규칙 세트가 아니라 설정 파일이나 여러분의 머릿속에 존재하는 프로젝트별 관례입니다. 이것이 바로 커스텀 도구를 만드는 이유이며, 신뢰 문제가 불편해지는 지점이기도 합니다. 린터의 커버리지는 잘 이해되어 있습니다. 커스텀 에이전트의 커버리지는 여러분이 프롬프트를 작성할 때 가정했던 바로 그것입니다.


