Claude Code와 Codex에게 '고장 낼 부분'을 요청하는 리뷰는 끝이 없다. 타인의 코드를 읽는 앱에서, 중단 기준을 정한 기록
요약
본 글은 AI로 만든 웹 애플리케이션을 점검하는 도구를 개발하며 겪은 과정을 기록한 내용입니다. 특히 Claude Code와 Codex 같은 LLM에게 '고장 날 만한 시나리오'를 요청하고, 이를 반복적으로 수정 및 검증하는 과정의 어려움과 발견된 지적들을 정리했습니다.
핵심 포인트
- LLM을 활용하여 코드 리뷰 도구를 개발 중이다.
- Claude Code와 Codex 등 여러 모델/에이전트를 이용해 교차 검증한다.
- 지속적인 '고장 내기' 요청에도 불구하고 허점 발견은 끝이 없다.
- 중단 기준(Stopping Criteria) 설정의 중요성을 강조하는 기록이다.
AI로 만든 웹 애플리케이션을 공개 전에 점검하는 도구(점검일과)를 Claude Code와 Codex로 만들고 있습니다. 이 도구가 읽는 것은 사용자의 코드입니다. 즉, 어떤 형태의 입력이 올지 저희가 결정할 수 없습니다.
만들기 시작한 날, Claude Code가 작성한 것을 Codex와, Claude Code의 확인자(다른 컨텍스트에서 작동하는 서브 에이전트)에게 리뷰하게 했습니다. '고장 낼 만한 시나리오를 찾아달라'고 요청하고, 고친 다음 다시 요청했습니다. 이를 반복하자, 몇 번을 해도 지적이 나왔고 끝이 나지 않았습니다.
이 글은 그때 무엇이 발견되었고, 무엇을 고쳤으며, 무엇을 남겼고, 어디서 멈추기로 결정했는지에 대한 기록입니다. Claude Code나 Codex에게 작성하게 하고, 다른 모델이나 다른 세션에 리뷰하게 하는 사람이, 중단 기준을 미리 정하는 데 자료가 되기를 바라는 마음으로 씁니다.
전제: Claude Code와 Codex의 사용 구분
현재는 다음과 같이 나누어 사용하고 있습니다.
| 역할 | 담당 업무 |
|---|---|
| Claude Code | 사령탑. 사람과 상담하여 결정하는 것, 설계하는 것, 작은 수정을 작성하는 것, Codex에게 요청할 문서를 작성하는 것. Codex가 만든 것과 Codex의 지적을 확인하고 통합함 |
| ... | |
| 리뷰의 목적은 작성된 것과는 다른 시각으로 보여주는 것입니다. 첫 번째는 Codex입니다. 서버나 데이터에 영향을 미치는 큰 변경 사항은, 그 후에 Claude Code의 확인자에게도 보여줍니다. 확인자는 Claude Code의 서브 에이전트입니다. 메인 세션과는 다른 컨텍스트에서 작동하며, 구현했을 때의 상호작용은 계승하지 않습니다. 전달된 프롬프트만 보고 시작합니다. Codex의 지적은, Claude Code가 실제로 발생할 수 있는지 확인한 후에 고칩니다. |
리뷰로 확인할 것과, 수정한 후의 진행 방식은 리포지토리의 AGENTS.md에 적혀 있습니다. Codex가 읽는 파일에서, Claude Code에게도 CLAUDE.md에서 불러오게 하므로, 둘 다 같은 규칙으로 작동합니다.
또 하나, 이 글에 여러 번 등장하는 것이 점검일과의 README입니다. 리포지토리에 둔 마크다운 파일로, 결정한 것, 리뷰에서 나온 지적과 그 처리 방식, 알고 있는 한계점을 Claude Code가 그때그때 추가하고 있습니다. 이 글의 개수나 인용은, 이 README와 마찬가지로 Claude Code가 작성한 시험 결과 파일에서 가져왔습니다.
이 형태로 정착된 것은 제작 도중입니다. 앞으로 쓸 첫날은, Claude Code가 작성했고, Codex와 Claude Code의 확인자 양쪽 모두에게 리뷰를 시켰었습니다.
원래 규칙
AGENTS.md에 리뷰에 대해 이렇게 쓰여 있습니다.
지적을 고쳤다면, 수정한 부분을 새로운 시각(다른 확인자/다른 모델)에게 '고장 낼 만한 시나리오'를 찾아보게 한 후에 보고한다.
'다른 모델'이 Codex이고, '다른 확인자'가 Claude Code의 확인자입니다. 수정할 때마다, 아직 보지 않은 시각으로 고장 내게 합니다. 서버나 데이터 저장처럼 범위가 정해진 변경을 위한 규칙으로, 그 부분에서는 지금도 사용하고 있습니다.
무슨 일이 일어났는가
첫 구현일의 이야기입니다.
- Codex에게 첫 리뷰를 요청하자 10건. 고치고 다시 요청하자 7건
- 병행하여 Claude Code의 확인자에게도 보여주었습니다. 5번을 거쳐도 새로운 허점이 발견되었습니다
- 도중에 구조를 크게 변경했습니다(나중에 쓰겠습니다). README에는, 그 후에도 '확인자를 6명 거쳤다'고 되어 있습니다(Claude Code의 서브 에이전트였는지, Codex였는지는 기록하지 않았습니다). README에 남아있는 이 단계의 리뷰는 6개이며, 번호가 붙은 지적은 순서대로 14건, 7건, 11건, 12건, 12건, 5건입니다.
줄어들지 않습니다. 발견된 것은 예를 들면 이런 것이었습니다(README 작성 방식 그대로. 락 파일은 의존 패키지의 버전을 고정하는 파일입니다).
- '형식 재조정된 락 파일(큰따옴표・들여쓰기 차이・공백 없음 yarn)'
- '태그 문자・異体字セレクター・한글의 임베딩 글자가 파일명 인코딩을 통과하는 경우'
- '일본어 인용 부호(〝〞)・오른쪽에서 쓰는 문자'
모두 실제로 일어날 수 있는 이야기입니다. 하지만, 평범한 사람이 AI로 만든 앱에서 얼마나 많이 발생할까요?
게다가, 고치는 것이 다음 문제를 만들었습니다.
- SQL의 읽는 방법을 수정하자, 그 수정으로 인해 새로운 놓친 부분이 생겼습니다.
- pnpm의 lock 파일 읽는 방법을 수정하자, 긴 줄에서 30초 제한에 걸리는 느림이 발생했습니다.
- 주석을 읽는 처리를 추가하자, 정규 표현식이나 JSX를 주석으로 오인했습니다. README에는 '이전 버전보다 나빠졌다'고 적혀 있습니다.
왜 끝나지 않는가
README에는 그때 Claude Code가 내린 추정이 이렇게 쓰여 있습니다.
이 앱은 어떤 형태일 수 있는 타인의 코드를 읽으며, 정해진 방식으로만 읽다 보니 예외는 반드시 남습니다. 범위가 정해진 변경을 위한 '고장 내기' 확인을 적용하면, 검증 역할은 매번 무언가를 발견할 수 있고 끝이 없습니다.
이는 추정일 뿐, 실험으로 확인된 것은 아닙니다. 단지 일어난 일은 이렇습니다. 입력 형태에 한계가 없다면, '고장 낼 시나리오'는 바닥을 보입니다. 지적이 0건인 날을 끝으로 하면, 끝이 오지 않습니다.
멈추는 방법을 정하다
그날 밤, 이 앱에 한해서 규칙을 변경했습니다(README 발췌).
검증 역할에게 지적을 '발생하기 쉬운 정도(일반적인 AI 제작 앱에서 발생하는지)'와 '영향의 크기(놓침 > 잘못된 지적 > 문구)'로 순서대로 나열하도록 했습니다. 정한 기준을 넘는 것만 수정하고, 나머지는 '알고 있는 한계'에 적습니다.
하나의 변경 사항당 검증 역할은 최대 2회까지 진행합니다.
또한, '내보내도 좋은 기준을 측정하기 전에 미리 정한다'라는 항목을 추가했습니다(나중에 작성하겠습니다).
점수표는 만들지 않았습니다. 나열하게 한 후 위에서부터 수정하는 방식으로 사용합니다. 한 번의 요청은 '발생하기 쉬운 순서대로 최대 5건. 실제로 점검하여 발생한 것만'이었습니다.
'고장 내기' 확인을 중단한 부분은 이용자의 코드를 읽는 부분뿐입니다. 범위가 정해진 서버 측 변경에서는 여전히 고장 내기를 진행하고 있습니다.
수정한 것, 남긴 것
수정한 것:
- 자체 로그인 확인이 되어 있지 않아 관리용 API 등 50개 이상의 곳을 '로그인 확인이 보이지 않음'으로 잘못 지적했습니다.
- JSON, YAML, TOML, Python에 작성된, 브라우저에 배포되는 이름의 환경 변수에 들어간 비밀 값을 놓쳤습니다(일부 문제를 넣은 시험에서 58건 중 4건).
- 외부 취약성 데이터베이스로 이름을 보내지 않는 의존 패키지를 내부 목록과 대조하지 않았습니다.
남기고, '알고 있는 한계'에 적은 것:
- bun.lock이나 yarn berry의 lock 파일 내 워크스페이스(여러 패키지를 묶어 관리하는 시스템) 기록을 사용하지 않습니다.
- 깊이가 수천에 달하는 폴더에서는 30초로 멈출 때가 있습니다.
- 사내 의존 패키지의 가져오는 출처가 이용자의 PC 전체 설정에만 적혀 있는 경우를 구별할 수 없습니다.
- 데이터베이스 테이블을 삭제한 후에도, 그 테이블의 읽기/쓰기 허가 설정(정책)을 남겨 잘못 지적하는 경우가 있습니다.
- pnpm의 lock 파일이 두 개의 문서를 연결한 특이한 형태일 경우, 의존 관계를 추적할 수 없는 경우가 있습니다.
남긴 것들은 일반적인 AI 제작 앱에서는 거의 발생하지 않거나, 발생하더라도 영향이 작다고 판단한 것입니다. 수정하지 않기로 결정했다면, 조용히 남기지 않고 적어둡니다.
단정하는 범위도 좁혔다
시간은 앞뒤로 흐릅니다. 멈추는 방법을 정하기 전, 같은 날 오후에 앱이 단정하는 범위를 좁혔습니다. '무엇이 일어났는지'를 가지고 '중간에 구조를 크게 변경했다'고 적은 것이 이것입니다.
모든 형태를 읽을 수 없다면, 앱이 단정하는 범위는 읽을 수 있는 범위에 맞추어야 합니다.
초기 버전에는 결과 화면에 '잘 되고 있는 점'이라는 항목이 있었습니다. 어떤 규칙으로 지적이 발견되지 않으면, 이용자의 앱에 대해 '여기는 잘 되어 있습니다'라고 전달하는 문구를 출력하는 칸입니다. 예를 들어 결제 규칙에서는 다음과 같이 나왔습니다.
Stripe의 결제 알림에서 서명을 확인하고 있습니다.
이는 '당신의 앱은 도착한 결제 알림(Webhook)이 진짜인지 아닌지 확인할 구조로 되어 있습니다'라는 의미입니다.
Claude Code의 검증 역할을 5번 반복해도, 판정에 새로운 허점이 발견되었습니다. 판정에 허점이 있으면, 실제로는 확인하지 않은 앱에도 이 '잘 되고 있습니다' 문구가 나옵니다. 이용자는 그것을 읽고 안심하여, 그 부분을 재검토하지 않게 됩니다. 잘못된 지적이 많은 것보다 이것이 더 위험하다고 생각하여, 오인할 수 있는 두 가지 항목(데이터베이스의 RLS〈행별 접근 제어〉와 결제 서명)에서는 '잘 되고 있습니다' 문구를 출력하는 것을 중단했습니다.
계속해서, 공개된 AI 제작 앱 6건의 코드로 시험해 보니, 역방향 오류도 발견되었습니다(코드를 읽기만 했을 뿐, 실행하지는 않았습니다).
- 「stripe」と「webhook」という語がファイルにあるだけで、決済の Webhook エンドポイントだとみなした
- 署名を自前のコードで検証しているエンドポイント(HMACで比較する書き方)を、「署名を確かめていない」と指摘した
言葉や形が合っているかどうかで、意味まで読むのは無理がある。そう考えて、受け持ちを分け直しました。
| 담당 영역 | 내용 |
|---|---|
| 프로그램이 확인하는 것 (형태로 말할 수 있는 것) | 키의 형태, 브라우저에 배포되는 환경 변수에 들어간 비밀 값, .env 처리, 의존 패키지의 버전과 알려진 취약점, 데이터베이스의 RLS 설정, 헤더 |
| 사용자의 AI (Claude Code・Codex 등)에게 확인시키는 것 (의미를 읽는 것) | 로그인 및 권한, 결제 알림과 금액, 입력 처리 등. 확인할 수 있는 프롬프트를 전달한다. |
화면에는、「지적하지 않아도 문제가 아닐 수 있습니다」라고 쓰여 있습니다.
좁혀가도 과한 표현은 남아있습니다. 이 글을 쓰는 주에도 Codex의 리뷰에서 한 가지를 발견했습니다. 「잘 되어 있는 점」에 적었던 「.env는 .gitignore로 Git 대상에서 제외하고 있습니다」라는 문구입니다. 프로그램이 본 것은, .gitignore에 .env라고 쓰여 있는지 여부뿐이었습니다. 이미 Git에 넣어버린 .env에는 .gitignore가 적용되지 않습니다. 문구를 「.env는 .gitignore에 작성되어 있습니다」로 수정하고, 이미 넣어버린 경우의 주의사항을 덧붙였습니다. 넣어버린 이후의 절차는, .env를 GitHub에 올려버렸을 때의 내용을 적었습니다.
그리고 여기까지 좁혀가도, 확인 역할의 지적은 멈추지 않았습니다. 「무슨 일이 일어났는지」에 쓴 6가지 리뷰는 이 재구축 이후의 것입니다. 주장할 수 있는 범위를 좁히는 것과, 리뷰를 멈추는 방법을 결정하는 것은 둘 다 필요했습니다.
내보내도 되는 기준과 시험 세트
「지적이 0건」 대신, 멈출 장소가 필요합니다. 그래서 기준을 숫자로 정했습니다.
- 조정에 사용한 6건의 성적은 근거로 삼지 않는다
- 조정에 사용하지 않은 공개 앱 20건을 모아 시험 세트로 만든다. 무엇을 지적해야 하는지(정답)는 점검하기 전에, 점검 도구와 별도로 코드를 읽고 결정한다.
- 기준은 3가지: 심각한 누락이 0회 / 잘못된 지적이 앱당 평균 1 이하 / 심각한 잘못된 지적이 0
매번 새로운 20건을 모아 5번 측정했습니다.
| 회차 | 심각한 누락 | 잘못된 지적의 평균 | 심각한 잘못된 지적 | 기준 |
|---|---|
| 1회 | 1 | 0.40 | 2 | 미달 |
| ... |
표의 「심각한 누락」은 아래 1번 항목에서 정한 범위 내에서의 수입니다. 기준은 측정하면서 3번 수정했습니다.
- 1차 후: 알려진 취약점의 누락은 도구가 대조하는 범위(당시에는 주요 의존 패키지)로 센다. 범위 밖은 화면에 「확인하지 못함」이라고 적는다(모든 의존 패키지로 셀 경우, 1회차는 20건이었습니다)
- 3차 후: 정답과 점검에서 같은 날짜의 취약점 데이터를 사용한다(3회차의 1건은, 점검이 전날에 가져온 목록보다 나중에 공표된 취약점이었습니다)
- 4차 후: 선을 「무엇을 확인할지 (범위)」로 긋는다. 범위 안(실제 키, .env, public 스키마 테이블의 RLS, 주요 의존 패키지의 심각한 취약점)은 엄격하게 지키고, 범위 밖은 화면의 「확인하지 않음」에 적는다. 5회차를 마지막 측정으로 한다.
4차 후 수정은 리뷰에서 일어난 것의 반복이었습니다. 새로운 20건을 모을 때마다 특이한 형태가 나옵니다. 「심각한 누락이 0」는 계속되지 않았습니다. 여기에서도, 0건을 기다리는 것을 그만두고, 범위로 선을 그었습니다.
5회차에는, 범위 안에서 심각한 누락이 0, 심각한 잘못된 지적이 0입니다. 여기서 공개했습니다. 범위 밖에는 누락이 1건 있어 「알려진 한계」에 적습니다.
3번의 수정은 모두 결과를 본 후에 이루어졌습니다. 나중에 읽어도 알 수 있도록, README에 「결과를 본 후 변경됨」이라고 적어두었습니다.
정해도 지키는 건 별개의 문제
정한 다음 날, Claude Code가 이 규칙을 잊었습니다. 다른 변경으로 인해, 다시 확인 역할에게 「고장 낼 수 있는 줄거리」를 찾게 했고, 발생 가능성이 낮은 것까지 고치고 있었습니다. 사람이 알아차리고 멈췄습니다.
규칙은 README에 적혀 있었습니다. 그래도 잊어버립니다. 그래서 Claude Code의 메모리에도 적었습니다. 메모리는 Claude Code가 세션 시작 시마다 읽는 기억 사항입니다. '이 앱 변경사항에서는 검토자에게 요청하는 글에, 순서와 2회까지를 반드시 작성한다'고 넣었습니다.
그 이후의 변경(취약점 데이터베이스 조회 기능을 추가한 변경)은 Codex가 구현했고, Claude Code가 확인했습니다. 검토자의 1차 검토에서 발생하기 쉽고 영향이 큰 것을 고치고, 2차 검토를 마지막으로 중단합니다.
같은 형태의 문제는 다른 곳에서도 나왔습니다. 피드백 기능은 서버에 데이터를 저장하는, 범위가 정해진 변경입니다. 여기서는 지금도 검토자에게 고장 내게 하고 있습니다. 다만, 그중 자유 기입란(사용자가 쓰는 한마디)에서 비밀이나 코드를 가리는 처리만은, 검토자가 볼 때마다 새로운 유출 방식이 발견되었습니다. 입력에 한계가 없기 때문입니다. 이곳에서도 누락 없이 가리는 것을 목표로 하는 것을 포기하고, '발견된 범위 내에서 가린다'로 위치를 정하여, 전송 전에 내용물을 그대로 보여주는 방식으로 했습니다.
도입한다면
- 자신의 앱에 입력 형태에 한계가 없는 부분이 있는지 확인합니다. 있다면, 그 부분에서는 '지적 0건'으로 끝내지 않습니다.
- 검토 요청 시, 순서(발생 용이성: 일반적인 사용에서 발생하는지 / 영향도: 놓침 > 잘못된 지적 > 문구)와 회수 상한선을 작성합니다.
- 고치지 않기로 결정한 것은 '알고 있는 한계'에 적습니다.
- 화면상으로 '여기는 되어 있습니다'라고 전달하는 것은, 형태만으로 단정할 수 있는 경우로 한정합니다. 코드의 의미를 읽어야 알 수 있는 곳은 단정하지 않습니다.
- 제출해도 되는 기준을 측정하기 전에 정합니다. 조정에 사용하지 않은 그룹으로 측정합니다. 나중에 기준을 변경했다면, 변경했다고 적습니다.
- 규칙은 Claude Code나 Codex가 매번 읽는 장소(AGENTS.md 또는 메모리)에 작성합니다. README에만 적으면 잊어버립니다.
2의 요청은 예를 들어 다음과 같이 작성할 수 있습니다 (실제로 사용한 문구가 아니라, README의 규칙을 재구성한 예시입니다).
지적 사항은 일반적인 사용에서 발생하는지 여부와 영향도(놓침 > 잘못된 지적 > 문구)로 순서지어 주세요. 상위 5건까지 고치겠습니다. 검토는 이 변경에 대해 2회까지 합니다. 고치지 않을 것은 '알고 있는 한계'에 적을 것이니, 그 후보도 제시해 주세요.
맺음말
여기에 작성한 구조는 점검하기 좋은 날(무료/등록 불필요)에서 작동하고 있습니다. AI로 만든 앱의 폴더나 ZIP 파일을 선택하면, 비밀 키 유출이나 의존 패키지의 알려진 취약점 등을 브라우저 내에서 점검합니다. 보지 않은 범위는 화면에 적혀 있습니다. 중단 방식이나 기준 설정 방법으로 더 좋은 방법을 아시는 분은 알려주시면 감사하겠습니다.
토론

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