AI 코드 리뷰를 '최대 N개'로 제한했을 때의 결과 분석
요약
Claude Code의 `/code-review` 호출 방식에 `--max-findings` 옵션을 적용하여 코드 리뷰 지적 사항의 상한선을 설정하는 테스트 결과를 분석했습니다. 이 옵션은 지정된 최대 개수(N)를 정확하게 준수하며, 'all'을 지정해도 실제 발견된 결함 건수를 초과하지 않았습니다. 또한, 한 번의 제한이 다음 호출에 영향을 주지 않는다는 것을 확인했습니다.
핵심 포인트
- `--max-findings` 옵션은 설정한 최대 개수를 매우 정확하게 준수한다.
- AI가 요청하는 것이 아니라 시스템 레벨에서 출력을 제한한다.
- 상한선 지정은 이전/다음 코드 리뷰 호출에 영향을 주지 않는다.
Claude Code에는 작성한 코드의 문제점을 지적하게 만드는 /code-review라는 호출 방식이 있습니다.
(슬래시(/)를 앞에 붙여 사용하는, 미리 준비된 명령어입니다.)
여기에 '지적 사항을 몇 개까지로 할지'라는 상한선을 설정할 수 있다는 것을 알고 3가지 경우를 실제로 테스트해 보았습니다.
결론부터 말씀드립니다.
- 상한선 숫자는 정확히 지켜졌다. 1개, 2개, 3개를 지정한 총 11회 동안 한 번도 초과하거나 부족한 적이 없었습니다. - '모두 보여줘(all)'는 늘리는 버튼이 아니었다.
all을 지정한 3번의 경우 모두 7건이었으며, 아무것도 지정하지 않은 경우(8건이 5회, 7건이 2회)를 넘어선 적은 0였습니다. - 상한선은 다음 호출에 영향을 주지 않았다. 1개로 제한한 직후 아무 설정 없이 다시 호출했을 때는 7건으로 돌아가 있었습니다.
테스트했던 내용을 먼저 보여드리겠습니다. 상한 1개를 지정했을 때 반환된 내용이 전부입니다.
(실제 출력 결과를 그대로 붙였습니다.)
timeout 600 claude -p "/code-review --max-findings 1" \
--output-format json --strict-mcp-config --permission-mode acceptEdits < /dev/null
[
{
"file": "cart.py",
...
반환되는 것은 목록 형태의 문장이 아니라, 1건이 하나의 덩어리로 된 표 같은 데이터(JSON)였습니다.
따라서 '몇 건이 반환되었는지'는 눈으로 읽기보다, 덩어리의 개수를 기계적으로 셀 수 있었습니다.
아래 건수는 모두 이 개수입니다.
테스트 소재: 일부러 6군데 오류를 넣은 회계 코드
세는 것에 앞서, 지적받을 대상을 제가 직접 준비했습니다. 장바구니의 총 금액을 계산하는 41줄짜리 작은 파일에,
결함을 6가지 종류로 의도적으로 심었습니다.
| # | 심은 결함 | 어떤 문제가 생기는가 |
|---|---|---|
| 1 | 반복문의 범위를 하나 짧게 함 | 마지막 품목이 총액에 포함되지 않음 |
| ... | 딱 100엔일 때만 전액 무료 처리됨 | |
| 5 | 열었던 파일을 닫지 않음 | 호출할수록 열린 채로 방치된 구멍이 늘어남 |
| 6 | 실패를 pass로 무마함 | 영수증을 작성하지 못해도 성공으로 간주됨 |
이 파일을 '직전 커밋부터의 변경(diff = 변경 전과 변경 후의 차이)'로 설정하고,
그 차이를 리뷰하게 했습니다. git diff --numstat은 34 2 cart.py였으며, 이는 34줄 추가 및 2줄 삭제의 1개 파일입니다.
너무 작은 소재는 '상한보다 먼저 지적이 소진'되어 상한이 작동했는지 알 수 없기 때문에, 6가지 종류로 설정했습니다. (최대 N개를 지정하는 경우를 모두 하회시키고 싶었기 때문입니다.)
Impact: 파일 1개, 41줄, 858바이트, 34줄 추가의 차이에 결함 6종을 심었습니다
'지적 1개까지'는 정말로 1개였나
--max-findings에 숫자를 넣어 1, 2, 3 세 가지 경우를 테스트했습니다.
| 지정 | 횟수 | 반환된 건수 |
|---|---|---|
--max-findings 1 | 4 | 1, 1, 1, 1 |
--max-findings 2 | 4 | 2, 2, 2, 2 |
--max-findings 3 | 3 | 3, 3, 3 |
'지적 1개까지'를 지정한 4번의 경우 모두 1건이었고, 2개 지정 4번의 경우 모두 2건이었으며, 3개 지정 3번의 경우 모두 3건이었습니다.
총 11회 동안 단 한 번도 틀린 적이 없었습니다. '대략 그 정도'가 아니라 '정확히'였습니다.
솔직히 말하자면, 여기는 절반 정도 틀릴 거라고 예상했습니다. AI에게 '3개 언급해줘'라고 부탁했는데 4개가 돌아오는 경험을 했었기 때문입니다. 하지만 이 상한선은 답변의 내용 자체를 AI가 요청하는 것이 아니라,
내보내는 단계에서 잘리는 것처럼 보였습니다.
흥미로웠던 점은 '잘리는 1건이 무엇인가' 하는 것입니다. 24번 모두 맨 앞에 온 지적 사항은 같은 결함 #1 (마지막 품목이 총액에 포함되지 않음)이었습니다. 즉, 상한 1개로 제한해도 임의의 1개가 아니라,
매번 동일한 것이 남았습니다.
다만 줄 번호는 너무 신뢰해서는 안 될 것 같습니다. 같은 결함 #1이라도, 줄 번호는 23회에 9행, 1회에 10행이었습니다.
두 번째 이후의 지적 사항은 동일한 결함이라도 회차마다 1~3줄씩 차이가 있었습니다.
가리키는 줄은 참고용으로 생각하는 것이 안전합니다.
영향(Impact): 상한선 1/2/3의 합계 11회 모두 지정된 건수와 일치했습니다. 24회 모두 첫 번째 지적은 동일한 결함이었고, 해당 라인 번호는 23회에 9행, 1회에 10행(1행 차이)이었습니다. 두 번째 이후의 라인 번호는 같은 결함이라도 1~3행 정도 차이가 났습니다.
'모두 보여줘'라고 해도 건수는 하나도 늘지 않았다
상한선에는 숫자 외에도 all (전체)과 default (기본값으로 되돌리기)를 지정할 수 있습니다.
'모두 보여줘'라고 요청하면 더 많이 나올 것이라 생각하고 시도해 봤는데 그와는 반대였습니다.
| 지정 | 횟수 | 반환된 건수 |
|---|---|---|
--max-findings all | 3 | 7, 7, 7 |
--max-findings default | 1 | 7 |
| 아무것도 지정하지 않음 | 7 | 8, 8, 8, 8, 8, 7, 7 |
--max-findings 20 | 2 | 8, 8 |
all은 세 번 모두 7건이었습니다. 아무것도 지정하지 않은 7회는 8건이 5회, 7건이 2회였으며, all이 아무것도 지정하지 않은 경우를 상회한 적은 없었습니다.
또한, 실제 지적보다 많은 숫자(20)를 주어도 반환되는 것은 최대 8건에 그쳤습니다.
상한선은 '여기까지만 보여준다'는 선일 뿐, '여기까지 반드시 보여줘야 한다'는 할당량(노르마)이 아닙니다.
여기는 실수하기 쉬운 부분이었습니다. 처음에 시도했던 1회가 7건이었고, all도 7건이었기 때문에 '같네'라고 넘어가려 했습니다. 아무것도 지정하지 않은 경우를 7회까지 늘려서야 비로소,
아무것도 지정하지 않은 경우가 7~8건으로 변동하는 반면, all은 세 번 모두 7건으로 고정되어 있다는 것을 알게 되었습니다.
3번의 관측만으로는 차이를 말하면 안 됩니다.
영향(Impact): all은 세 번 모두 7건, 아무것도 지정하지 않은 경우는 7회에 8건이 5회, 7건이 2회였습니다. all이 상회한 적은 0회였습니다. 상한선 20도 최대 8건에 그쳤습니다.
상한선을 낮추면 대기 시간이 확실히 짧아진다
상한선을 작게 설정하자, 응답을 기다리는 시간이 눈에 띄게 짧아졌습니다.
- 상한선 1/2/3 (합계 11회)의 소요 시간은 최장 8,208밀리초였고 - 아무것도 지정하지 않음・
all・20・default(합계 13회)의 소요 시간은 최단 11,769밀리초였습니다.
가장 느린 경우와 가장 빠른 경우가 겹치지 않습니다. 즉, 우연한 변동성으로는 설명하기 어려운 차이입니다.
나오는 글자 수도 상한선 1건은 366446자로, 아무것도 지정하지 않은 경우는 2,7463,245자로 한 자릿수 차이가 났습니다.
반면 비용(요금)에서는 차이를 주장하기 어려웠습니다. 상한선 1건의 최고가 0.0432141달러인 반면,
아무것도 지정하지 않은 경우의 최저가는 0.0356350달러로, 범위가 겹칩니다.
'지적을 좁히면 저렴해진다'고는 이 횟수로는 말할 수 없습니다.
영향(Impact): 소요 시간은 상한선 1/2/3 (11회)의 최장 8,208ms 대 아무것도 지정하지 않음・all・20・default (13회)의 최단 11,769ms로 범위가 중복되지 않았습니다. 출력은 상한선 1건 366446자 대 아무것도 지정하지 않은 경우 2,7463,245자입니다. 비용은 중첩 판별 불가
다음에 호출하면 상한선이 사라져 있었다
가장 의아했던 부분은 여기였습니다. 상한선의 지정이 다음 번에도 유지되는지 같은 폴더 안에서 순서대로 호출해 보았습니다.
| 순 | 호출 방식 | 반환된 건수 |
|---|---|---|
| 1 | /code-review --max-findings 1 | 1 |
| 2 | /code-review (지정 없음) | 7 |
| 3 | /code-review --max-findings 2 | 2 |
| 4 | /code-review (지정 없음) | 8 |
| 5 | /code-review --max-findings default | 7 |
| 6 | /code-review (지정 없음) | 8 |
두 쌍 모두, 다음 호출 시에는 상한선 지정이 풀려 있었습니다. default(기본값으로 되돌리기)를 지정하기 전부터 돌아가고 있었습니다.
설정 파일에 쓰여있는지 찾아봤습니다. 첫 번째와 전후의 ~/.claude.json 전체 항목을 비교해 본 결과,
바뀐 것은 3개뿐이었고, 모두 상한선과는 관계없는 사용 횟수 등이었습니다.
DIFF /cachedGrowthBookFeaturesAt | 1791602743067 -> 1791602805141
DIFF /skillUsage/code-review/lastUsedAt | 1791602743463 -> 1791602805440
DIFF /skillUsage/code-review/usageCount | 13 -> 14
작업 폴더 쪽에도 새로운 파일은 만들어지지 않았습니다(.git과 cart.py 그대로).
최소한 이번과 같이 화면을 사용하지 않는 호출 방식에서는 상한 선택이 어디에도 저장되어 있지 않습니다.
사용하려면 매번 플래그를 지정하는 것이 안전합니다.
참고로, 화면이 있는 대화 세션 내에서 동일한 설정이 유지되는지는 측정하지 않았습니다(검증 불가).
영향: 1건에 한정하여 바로 호출했을 때는 7건이었고, 2건 직후에는 8건이었습니다. ~/.claude.json의 차분 분석 결과 3건은 모두 상한과 무관합니다.
매번 발생했던 '거부'는 권한 문제가 아니었다
총 24회 동안 permission_denials (직역하면 '허가 거부')이 1~2건, 총 27건 기록되었습니다.
처음에는 권한 설정을 잘못한 것이라 생각했지만, 내용을 보니 27건 모두 도구의 이름이 Bash (명령어 실행)이었고,
명령어 문자열에는 예외 없이 @{upstream} 이라는 방식이 들어가 있었습니다.
git status --short | head; git diff @{upstream}...HEAD 2>&1 | head -500 || true; git diff HEAD | head -500
실행 과정을 한 번만 자세히 기록하여 추적해 보니, 첫 번째 명령어가
Brace expansion (unquoted { in concatenation with ,/..) 로 실패했고,
두 번째 시도(@{upstream}을 따옴표로 묶은 버전이나 git diff HEAD로 바꾼 버전)에서 차분 분석이 가능했습니다.
즉, 권한 때문에 막힌 것이 아니라, 중괄호({}) 표기 방식의 제한에 걸려 한 수 밀린 것일 뿐입니다.
이름만 보고 '권한 부족으로 리뷰가 불완전해졌다'고 읽으면 오해할 수 있습니다.
영향: 27건 모두 Bash이며, 전 건 @{upstream}을 포함합니다. 자세히 기록한 한 번의 경우 두 번째 명령어에서 리뷰가 성립되었습니다. 24회 모두 종료 코드 0 및 stderr 0바이트였습니다.
원활하지 않았던 점들
처음에는 완전히 다른 주제를 측정할 계획이었습니다. 같은 트렌드 수집 상위권에 있던, 실제 Claude Code용 플러그인(추가 도구함)을 넣고, 단순히 넣는 것만으로 매번 걸리는 부담을 측정하려 했습니다.
획득용 URL은 403으로 차단되었고, 다른 방법으로는 가져올 수 있었지만,
외부에서 가져온 코드를 claude에 읽히게 하는 실행이 환경 측의 가드에 막혔기 때문에,
재현할 수 없다고 판단하여 주제를 포기했습니다. 넣지 않고 검사만 할 경우 통과했고,
검사 결과는 '합격' + 조언 1건이었습니다.
또 하나. 첫 번째 호출 비용이 0.1127496 달러였고, 나머지 23회의 0.0266793~0.0558152 달러와 달랐습니다.
같은 질문을 반복할 경우 내부에서 재사용되는 메커니즘(프롬프트 캐시)이 활성화되지 않았을 가능성이 높지만, 그 부분은 분리하여 측정하지 못했습니다. 따라서 '두 번째 호출부터는 저렴하다'고 말할 수는 없습니다.
영향: 당초 주제는 획득 시 403 에러 발생 및 외부 코드 읽기 부분이 환경 가드에 막혀 재현 불가(검사만은 합격+조언 1건). 첫 호출 비용만 0.1127496 달러였고, 나머지 23회는 0.0266793~0.0558152 달러에서 벗어났습니다.
요약
| 조사 내용 | 실측 결과 |
|---|---|
--max-findings 1/2/3은 지켜지는가 | 총 11회 모두 지정대로(초과 0・부족 0) |
all은 미지정보다 많이 나오는가 | all 3회 모두 7건. 미지정을 상회한 횟수는 0 |
| 상한보다 큰 숫자(20)를 주면 | 2회 모두 8건(찾은 수에서 멈춤) |
| 상한을 낮추면 빨라지는가 | 최대 8,208ms 대 최소 11,769ms로 범위 비중복 |
| 상한을 낮추면 저렴해지는가 | 범위가 겹쳐 판정 불가 |
| ... | 매번 나오는 permission_denials |
27건 전 건이 Bash이며, 전 건 @{upstream}을 포함하는 명령어 |
사용법으로는, 간단히 보고 싶을 때는 상한 1~3건을 매번 지정하고, 시간을 들여 볼 때는 아무것도 지정하지 않는 것이 가장 자연스러웠습니다. all은 일부러 붙일 이유를 찾지 못했습니다.
검증 환경 및 측정 방법
claude --version
= 2.1.296 / Node v22.22.0 / Python 3.11.17 / Linux 6.18.44-fc-v114 / CPU 4 - 응답에 사용된 모델명은 실제 출력에서 가져왔으며, 24회 모두 claude-sonnet-5-5
단 하나만 - 한 번에 하나의 프로세스.
claude -p "<호출 방식>" --output-format json --strict-mcp-config --permission-mode acceptEdits
,
입력은 /dev/null
,
timeout 600가 붙습니다. 조건별로 주제를 새 폴더에 복사한 후 실행합니다.
(상한이 남아있는지 확인하는 것만은 같은 폴더를 재사용)
- 건수는 반환된 데이터 덩어리의 개수입니다.
지적 내용이 맞는지 여부는 판정에 사용하지 않았습니다. - 총 25회(건수를 센 24회 + 실행 상황을 기록한 1회)로 종료 코드 0, stderr 0바이트
(stderr는 에러 표시용 출구. 0바이트=에러 문자가 한 글자도 나오지 않음). 비용 합계는 1.1166038 달러
검증 불가로 남은 것: 출처 게시물이 말하는 '2.1.288에서 추가된' 버전의 귀속
(당사는 2.1.296에서만 실측), 지운 입력을 위 키로 되돌릴 수 있는지(화면을 통한 조작이 필요),
대화 세션 내에서 상한이 유지되는지, 지적 품질의 우열, 더 큰 차분이나 여러 파일/다른 언어에서의 건수,
줄 번호가 어긋나는 이유, all이 지정하지 않은 것보다 적어지는 이유, 지정하지 않았을 때 7건과 8건으로 흔들리는 이유,
다른 버전/다른 모델에서의 동작.
Claude Code를 실제로 구동하여 확인한 범위만 작성하고 있습니다.
Discussion

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