Vibe Coding의 확장: 왜 10명의 개발자에게 하나의 스캐너가 필요한가
요약
AI 어시스턴트를 사용하는 개발자들이 늘어날수록 각기 다른 코딩 스타일과 비결정성으로 인해 코드베이스의 취약점이 누적됩니다. BrassCoders는 이러한 문제를 해결하기 위해 AI의 확률론적 특성과 대조되는 결정론적 정적 분석 스캐너를 사용하여 팀 전체의 코드 품질을 일관되게 관리합니다.
핵심 포인트
- AI 어시스턴트 사용 시 개발자별 비결정성으로 인한 코드 품질 불균형 발생
- 팀 규모가 커질수록 개별 개발자의 AI 사각지대가 코드베이스의 취약점으로 합집합됨
- BrassCoders는 12개의 정적 분석 스캐너를 통해 결정론적인 코드 검증 제공
- AI의 확률론적 생성 방식과 대비되는 고정된 규칙 기반의 보안 검사 필요성 강조
AI 어시스턴트를 사용하는 10명의 개발자가 하나의 일관된 코드베이스를 만들어내지는 않습니다. 그들은 10가지의 코딩 스타일, 10가지의 프롬프트 전략, 그리고 10가지의 서로 다른 취약점 노출 영역(vulnerability surfaces)을 만들어낸 뒤, 이를 하나의 저장소(repo)로 병합합니다. 이러한 실패는 무작위로 발생하는 것이 아닙니다. 이는 구조적인 문제입니다. 각 개발자의 AI 어시스턴트는 저마다의 생성 습관, 저마다의 사각지대(blind spots), 그리고 저마다의 비결정성(non-determinism)을 가지고 있습니다. 이러한 간극을 잡아내는 것은 또 다른 AI 리뷰어가 아닙니다. 코드를 누가 작성했는지와 상관없이, 모든 PR(Pull Request)에 대해 매번 동일한 규칙을 실행하는 스캐너입니다.
개인의 Vibe Coding 습관이 합쳐지지 않는 이유
BrassCoders는 모든 개발자의 코드를 동일하게 취급합니다. Bandit, Pylint, Pyre/Pysa, Semgrep, ast-grep, detect-secrets, 그리고 6개의 커스텀 탐지기를 포함한 12개의 정적 분석 스캐너(static-analysis scanners)를 실행하고, 그 결과물들의 합집합을 YAML 형식으로 출력합니다. 출력 결과에는 개발자의 이름이 나타나지 않습니다. 그들이 사용한 AI 어시스턴트도 나타나지 않습니다. 나타나는 것은 취약점 패턴, 파일 경로, 그리고 이를 잡아낸 스캐너입니다.
이것이 중요한 이유는 팀 규모에서 Vibe Coding 습관이 서로 상쇄되지 않기 때문입니다. 어떤 개발자는 AI 어시스턴트에게 의존성(dependencies)을 보수적으로 다루라고 프롬프트를 입력합니다. 다른 개발자는 빠르게 배포하도록 프롬프트를 입력합니다. 세 번째 개발자는 완전히 다른 모델을 사용합니다. 이들 각각은 서로 다른 구조적 특성을 가진 코드를 생성합니다. 어떤 AI 어시스턴트는 특정 SQL 생성 패턴을 선호하는 경향이 있습니다. 어떤 것들은 다른 것들과는 다른 방식으로 예외 처리(exception-handling)의 엣지 케이스(edge cases)를 놓치기도 합니다. 병합할 때 이 중 어느 것도 평균화되지 않습니다. 각 개발자는 자신의 AI 어시스턴트가 가진 특유의 사각지대를 공유 코드베이스에 기여하게 됩니다.
전체 취약점 노출 영역은 10명 개발자 위험도의 평균이 아닙니다. 그것은 합집합입니다. 어떤 AI 어시스턴트가 가진 모든 사각지대는 코드베이스의 잠재적인 구멍이 됩니다.
비결정성은 팀 규모에서 증폭됩니다
BrassCoders는 오프라인이며 결정론적 (deterministic)입니다. 즉, 동일한 코드에 동일한 스캐너 버전을 사용하면 매 실행마다 동일한 YAML 출력을 생성합니다. 샘플링 (sampling), 온도 (temperature), 프롬프트 변동 (prompt variation)이 없습니다. 이것은 정적 분석 (static analysis)의 문서화된 특성입니다. 스캐너는 추상 구문 트리 (AST)를 읽고 고정된 규칙을 적용합니다.
AI 어시스턴트는 그 반대로 작동합니다. GitHub Copilot, Claude Code, 그리고 다른 모든 LLM 기반 코딩 어시스턴트들은 확률론적 (probabilistically)으로 제안을 생성합니다. 모든 주요 제공업체의 모델 문서에 따르면, LLM의 출력은 동일한 입력에 대해서도 호출 시마다 달라집니다. 온도 (temperature)와 샘플링 (sampling)은 이러한 모델들이 작동하는 방식입니다. 월요일에 Copilot의 제안을 수락한 개발자가 화요일에 다시 프롬프트를 입력한다면, 동일한 함수 시그니처 (function signature)에 대해 의미 있게 다른 코드를 얻을 수도 있습니다. 보안 특성 (security properties)은 이 두 제안 사이에서 변할 수 있습니다.
이를 한 스프린트 동안 10명의 개발자로 확장해 보십시오. 각 개발자는 비결정론적 (non-deterministic) 시스템으로부터 제안을 수락하고 있습니다. 서로 다른 프롬프트, 서로 다른 온도, 서로 다른 컨텍스트 윈도우 (context windows). 그들이 배포하는 결과물의 보안 특성은 그들이 테스트한 결과물의 특성으로부터 예측할 수 없습니다. 그리고 만약 코드 리뷰 (code review) 또한 LLM 기반이라면, 첫 번째 비결정론적 계층 위에 두 번째 비결정론적 계층을 추가한 셈이 됩니다.
개발자의 AI 어시스턴트가 생성하는 것과 결정론적 스캐너가 보는 것 사이의 간극이 바로 버그가 존재하는 곳입니다. AI 리뷰어는 학습 데이터에서 본 것들을 탐지하는 데 탁월합니다. 반면 정적 분석 (static-analysis) 스캐너는 모델이 지난주에 무엇을 보았는지에 따라 변하지 않는 규칙을 적용합니다.
하나의 일관된 게이트(Gate)가 팀에 가져다주는 것
CI에서 BrassCoders를 실행한다는 것은 어떤 개발자가 푸시했는지, 어떤 AI 어시스턴트를 사용했는지, 혹은 그들의 로컬 리뷰 습관이 어떠한지와 관계없이 모든 PR (Pull Request)에 대해 동일한 규칙이 실행됨을 의미합니다. 발견된 결과 세트는 스캐너의 결과 세트입니다. 한 개발자의 직관도, 한 AI 어시스턴트의 의견도 아닙니다. 바로 규칙입니다.
이것이 BrassCoders를 개별 개발자의 선호 사항이 아닌 팀 수준의 게이트 (gate)로 만드는 속성입니다. 개별 개발자의 선호 사항은 건너뛸 수 있지만, CI 게이트는 건너뛸 수 없습니다. SQL 인젝션 (SQL injection)이 GitHub Copilot에 의해 작성되었든 Claude Code에 의해 작성되었든 — 어떤 경우든 동일한 AST 패턴과 일치하기 때문에 — Bandit에게 똑같이 보인다면, 스캐너는 그 출처와 상관없이 이를 잡아냅니다.
일관성이 가져다주는 또 다른 이점은 감사 가능성 (auditability)입니다. 스캐너가 동일한 코드에 대해 동일한 YAML을 생성할 때, 시간에 따른 실행 결과들을 비교할 수 있습니다. PR #47에서 나타났던 결과가 PR #52에서도 여전히 존재한다면, 이는 해결되지 않은 결과임을 의미합니다. 커밋 사이에 사라진 결과는 수정되었거나 억제 (suppressed)된 것이며, BrassCoders는 그중 무엇인지 추적합니다. LLM 리뷰어는 이를 제공할 수 없습니다. 동일한 PR에 대해 동일한 LLM 리뷰어를 두 번 실행하면 서로 다른 출력을 생성하며, 이들은 서로 비교(diff)할 수 없습니다.
CI 통합 경로
BrassCoders는 단일 워크플로 (workflow) 파일로 GitHub Actions에 연결됩니다. 단 한 번의 스캔 단계, 동일한 규칙, 모든 PR에 적용되며, 개발자별 설정이 필요하지 않습니다. 푸시 (push)하기 전에 결과가 포착되기를 원하는 팀은 git commit이 완료되기 전에 로컬 스캐너를 실행하는 프리커밋 훅 (pre-commit hook)을 추가할 수도 있습니다.
전체 GitHub Actions 설정은 add-brasscoders-to-github-actions에 문서화되어 있으며, 프리커밋 훅 설정은 pre-commit-hook-ai-coder-bugs에서 확인할 수 있습니다.
이러한 분리는 팀 역학 (team dynamics) 측면에서 중요합니다. 프리커밋 스캐닝은 선택 사항입니다. 훅을 설치하지 않은 개발자라도 CI 단계에서 여전히 포착됩니다. CI 스캐닝은 필수 사항입니다. 모든 PR은 머지 (merge)되기 전에 스캐너를 거쳐야 합니다. 자율성이 높고 프로세스 오버헤드가 낮은 팀은 CI만으로 시작할 수 있습니다. 개발자들이 더 빠른 피드백을 원하는 팀은 그 위에 프리커밋 레이어를 추가합니다.
어떤 방식이든, CI 게이트는 일관성이 살아있는 곳입니다. 그것은 모두의 코드에 대해 동일한 조건 하에 실행되는 단 하나의 스캔입니다.
라이선스 하나당 3대의 머신을 커버할 수 있습니다. 개발자는 원한다면 CI 러너(runner)를 활성화(activation)로 간주하여 자신의 노트북, 스테이징 박스(staging box), 그리고 CI 러너에서 BrassCoders를 실행할 수 있습니다. 또는 팀 전체가 공유 라이선스 하에 CI 스캔을 실행할 수도 있습니다. BrassCoders 유료 플랜은 개발자 1인당 월 $12입니다.
팀 단위 탐지 결과의 모습
AI 보조 개발(AI-assisted development)로 2주간의 스프린트를 마친 후 BrassCoders가 공유 코드베이스를 스캔하면, 탐지 결과 YAML은 10명 개발자 전체의 기여도를 합산하여 반영합니다. 특정 개발자의 코드로 라벨이 붙지 않습니다. auth.py의 147번 라인에 있는 탐지 결과는 하드코딩된 비밀번호(hardcoded secret)이거나 아니거나 둘 중 하나입니다. db_utils.py의 SQL 생성 방식은 Bandit의 B608 인젝션(injection) 규칙에 걸리거나 걸리지 않거나 둘 중 하나입니다.
이러한 평면적인(flat) 탐지 결과 뷰는 한계가 아니라 기능입니다. 단 한 명의 개발자 코드에서만 나타나는 탐지 결과는 "그 사람의 문제"가 아니라, 공유 코드베이스의 공백입니다. 이를 개인의 문제로 취급하는 것은 공유되어야 할 신호를 개별화하는 것입니다. 스캐너는 누가 코드를 작성했는지 알지 못합니다. 보안 검토자(사람 또는 분류(triage)를 수행하는 AI 어시스턴트) 또한 이를 알 필요가 없어야 합니다.
결정론적 논거(determinism argument)에 대해서는 여기에서 자세히 다룹니다. 요약하자면, 동일한 입력에 대해 서로 다른 출력을 생성하는 게이트는 게이트가 아닙니다. 그것은 제안일 뿐입니다.
10명의 개발자가 AI 보조 개발 스프린트를 진행하는 팀에게 문제는 특정 개발자가 취약한 함수를 작성할지 여부가 아닙니다. 누군가는 반드시 작성하게 됩니다. AI 어시스턴트는 그럴듯해 보이지만 약간 잘못된 무언가를 생성할 것이고, 개발자는 이를 수락할 것입니다. 문제는 팀에 머지(merge)되기 전에 이를 잡아낼 수 있는 게이트가 있느냐 하는 것입니다. CI 환경에서의 BrassCoders가 바로 그 게이트입니다.
pip install brasscoders
brasscoders activate
brasscoders scan .
무료이며 Apache 2.0 라이선스를 따르는 OSS 코어로 시작하세요. 1,500개 이상의 탐지 결과를 실행 가능한 수준으로 줄여주는 노이즈 감소(noise-reduction) 패스가 필요할 때 BrassCoders 유료 플랜을 추가하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기