네 번 반복된 버그, 그리고 아무도 눈치채지 못한 이유
Claude Code로 Blockly 기반 대학 프로그래밍 실습 플랫폼을 처음 만든 개발자의 경험에서 이야기를 시작해야 한다. 코드 에디터 없이, 오직 Telegram 채팅만으로 Claude Code에게 지시를 내려 앱을 완성했다. 결과물은 23일 만에 80개 커밋으로 동작하는 플랫폼이었다. 빠르다. 그런데 같은 버그가 4번 반복됐다.
홈페이지 목록이 비었다 → 403, Spring Security permitAll() 누락. 카테고리 필터 → 403, 같은 클래스의 같은 파일, 다른 줄. 운동 상세 페이지 → 또 403. 좋아요 버튼 → 역시 403. 파일은 하나(SecurityConfig.java), 버그 유형도 하나, 수정 패턴도 하나. 그런데 네 번 모두 발견한 건 배포 후였다.
문제는 모델이 아니라 '연속성의 부재'
흥미로운 점은 Claude Code의 네 번 수정이 모두 기술적으로 옳았다는 것이다. 에러를 읽고, 원인을 찾고, 한 줄 추가하고, 완료 메시지를 보냈다. 각 턴을 독립적으로 채점하면 전부 통과다.
문제는 네 번의 결정을 연결하는 맥락이 없었다는 데 있다. 숙련된 엔지니어는 같은 파일을 세 번째 건드릴 때 '잠깐, 이 파일 전체를 다시 훑어봐야겠다'는 직관이 발동한다. 이전에 거기 있었다는 기억 덕분이다. 채팅 드리븐 워크플로우에서는 모델도, 사람도 그 파일의 '형태를 시간에 걸쳐 추적'하지 않는다. 모델은 현재 턴의 에러만 보고, 사람은 '해결됐습니다' 메시지만 읽고 다음으로 넘어간다.
더 구조적인 문제는 두 번째 레이어다. 일부 결정은 사람의 승인을 받았지만, 그 사람은 코드를 한 번도 열어본 적이 없었다. 레포를 체크아웃하지 않았고, SecurityConfig.java를 본 적도 없었다. 'human in the loop'는 그 사람이 모델이 갖지 못한 무언가를 가지고 있을 때만 의미가 있다. 이 케이스에서 그 조건은 충족되지 않았다.
독립적인 두 번째 AI가 찾아낸 것들
비슷한 맥락에서 주목할 만한 실험이 있다. 한 개발자가 자신이 작성한 보안 감사 체크리스트를 완성한 뒤, 그것을 쓰지 않은 다른 AI에게 '이 규칙셋을 깨봐라'라고 요청했다. 결과는 세 가지 구조적 허점이었다.
모든 발견이 자동 블로커로 처리되는 규칙은 오히려 '범위 외'로 재분류하는 탈출구를 만든다. '해결됨' 표시에 증거가 필요 없으면 느낌만으로 체크가 가능하다. 권한 설정이 '활성화 여부'로만 평가되면, 활성화됐지만 느슨하게 구성된 권한은 통과된다. 세 가지 모두 작성자가 읽을 때는 보이지 않았다. 쓰지 않은 독자가 읽자마자 보였다.
같은 세션에서 스타일 가이드 요약도 검증했다. '문장당 약 20자'라고 썼는데 원문은 '20자 이하'였다. 50자 캡이라는 수치는 원문 어디에도 없었다—반쯤 기억된 다른 소스에서 조용히 합쳐진 것이었다. AI가 원문과 줄 단위로 대조하자 평균 문장 길이 13자, 20자를 넘는 줄은 전체의 4개뿐임이 드러났다.
작성자는 의도를 통해 텍스트를 읽는다. 독립적인 두 번째 독자는 실제 쓰여진 것만 읽는다.
검증 루프를 구조로 분리하라
두 사례가 공통적으로 가리키는 설계 원칙은 하나다. 생성과 검증은 같은 컨텍스트 안에 있어서는 안 된다.
실용적인 워크플로우 설계로 번역하면 이렇다.
1. 생성 에이전트와 검증 에이전트를 분리하라. 같은 세션, 같은 컨텍스트 창에 있는 AI는 자신이 만든 코드의 패턴 오류를 체계적으로 발견하지 못한다. Claude Code로 코드를 작성했다면, 검증은 별도 세션의 다른 AI 인스턴스가 맡아야 한다. 'skeptical stranger' 페르소나를 명시적으로 부여하고, 코드를 깨뜨릴 근거를 찾도록 지시하라.
2. CLAUDE.md에 반복 패턴 규칙을 선제적으로 작성하라. 앞서 소개한 사례에서 CLAUDE.md는 버그가 4번 발생한 뒤에야 만들어졌다. "엔드포인트를 추가할 때는 반드시 SecurityConfig.java의 permitAll() 목록을 함께 확인하라"는 규칙이 먼저 있었다면, 첫 번째 수정 이후 두 번째는 없었을 것이다. 재발 패턴이 보이면 즉시 CLAUDE.md에 적어라.
3. 가시성 없는 승인은 승인이 아니다. 레포를 한 번도 열지 않은 상태에서 AI의 결정을 승인하는 것은 두 번째 검토가 아니라 단순한 확인 버튼 클릭이다. 실제 diff를 읽을 수 있는 환경 없이 human in the loop를 운영하는 것은 품질 관리 비용만 추가하고 효과는 없다.
시사점: AI 생성 코드의 품질 관리는 '이중 AI 구조'로 설계하라
이 두 사례가 현장에 주는 메시지는 명확하다. AI 코딩 에이전트를 도입한다는 것은 코드 생성 속도 문제를 푸는 것이지, 품질 검증 문제를 동시에 푸는 것이 아니다. 생성이 빨라질수록 검증 구조에 대한 설계 부채는 오히려 더 빠르게 쌓인다.
당장 내일 적용 가능한 최소 구조는 이렇다. CI 파이프라인에 'AI 리뷰어' 스텝을 추가한다. 이 스텝은 PR이 열릴 때마다 코드 생성에 사용된 AI와 다른 컨텍스트에서 동작하며, 변경된 파일 범위 안에서 동일 유형의 패턴 오류가 이전에 발생했는지를 히스토리 기반으로 검토한다. ErrorLog와 CLAUDE.md를 컨텍스트로 주입하면 재발 패턴 탐지 정확도가 올라간다.
전망: 검증 루프가 없는 AI-First 팀은 속도의 역설에 빠진다
AI 코딩 에이전트가 팀에 정착할수록, 코드 리뷰의 의미 자체가 달라진다. 사람이 사람의 코드를 리뷰하던 구조에서, AI가 생성한 코드를 AI가 1차 검증하고 사람이 2차로 판단하는 구조로 이행하는 것은 사실상 불가역적이다. 속도 압력이 그 방향을 밀기 때문이다.
그런데 이 이행이 제대로 설계되지 않으면 오히려 반대 효과가 난다. 생성이 빨라진 만큼 검토되지 않은 코드가 더 빠르게 쌓이고, 같은 패턴의 버그가 더 빠른 주기로 반복된다. 앞서 본 사례처럼, 80 커밋짜리 프로젝트에 설계 문서는 없고 사후 에러 로그만 18개 남는 형태로.
AI가 짠 코드를 믿으려면, 그 코드를 의심하는 구조를 먼저 설계해야 한다. 검증 루프의 독립성, CLAUDE.md의 선제적 규칙화, 가시성이 확보된 human review—이 세 가지가 갖춰지지 않은 채로 에이전트 속도만 올리는 팀은 품질 부채를 조용히, 그리고 빠르게 쌓고 있다.