~/khan

Claude × Codex 핑퐁 리뷰 루프, 몇 번이 적정선인가

· 16 min read · by Khan
#Claude Code#Codex#AI 협업#멀티에이전트#코드리뷰#설계 패턴

가설 — CLAUDE.md 상단에 "과설계 금지"만 단단히 박아두면, Claude ↔ Codex 핑퐁 루프가 아무리 길어져도 코드 비대화는 막을 수 있다.

결론 — 못 막습니다. 루프 횟수 자체가 과설계의 1차 변수이고, 프롬프트는 2차 변수에 불과합니다.

이 글은 그 결론에 도달한 과정의 회고입니다. 현장에서 실제로 돌아가는 멀티에이전트 구현체들이 디폴트 라운드 수를 어떻게 잡고 있는지를 먼저 살펴보고, 핑퐁이 길어질수록 왜 코드가 안전해지는 게 아니라 부풀어 오르는지를 메커니즘으로 풀어보겠습니다. 그리고 마지막에 직접 게이트 스킬을 설계할 때 반드시 들어가야 할 체크리스트를 정리합니다.

1. 시작 — 왜 "라운드 수"가 신경 쓰였나

최근에 Claude Code와 Codex를 짝지어 돌리는 사례를 자주 봅니다. 패턴은 단순합니다.

[요구사항]

Claude: 설계 + 구현

Codex: 리뷰 (corrections, edge cases, ...)

Claude: 수정

Codex: 재리뷰

... (APPROVED 또는 N라운드 도달 시 종료)

이걸 스킬 한 개로 묶어두면, 사람 손을 거의 안 거치고 한 번 트리거로 끝납니다. 매력적입니다. 그런데 직접 몇 번 돌려보다 보니 한 가지가 계속 걸렸습니다.

핑퐁이 길어질수록 코드가 더 안전해지는 게 아니라, 더 비대해지는 것 같다.

CLAUDE.md 맨 위에 "과설계 금지", "방어적 코드 금지", "YAGNI"를 줄 맞춰 박아둬도 그랬습니다. 이게 단순한 체감인지, 아니면 측정 가능한 현상인지 확인하려고 현장 구현체와 논문을 훑었습니다.

2. 현장 구현체가 잡은 상한선

먼저 실제로 Claude × Codex 루프를 코드로 구현한 프로젝트들이 어떤 숫자를 디폴트로 박아뒀는지 봅니다.

구현체디폴트 상한종료 조건비고
SmartScope SKILL.md5 라운드VERDICT: APPROVED (리뷰어가 "통과" 판정) 또는 5회 도달5회 후 새 세션으로 한 번만 최종 검수 (루프 X)
The Pair (timwuhaotian)20 라운드상한 도달 시 자동 종료가 아닌 "사람 검토 대기" 상태로 멈춤20은 "정상 종료값"이 아니라 "이제 사람이 들어와야 한다는 알람"
Adversarial Review (alecnielsen)2 라운드코드 레벨에서 못박은 상한무한 루프 방지가 1순위 목적
Self-Refine 논문 (Madaan 2023)평균 ~3회에서 수렴품질 정체 구간 (더 돌려도 안 좋아지지 않음)과반 태스크에서 3회 후 성능 정체

숫자는 2부터 20까지 폭이 넓어 보입니다. 그런데 한 가지 공통점이 있습니다.

20을 잡은 The Pair조차도, 20을 "끝나는 지점"이 아니라 "사람이 들어와야 하는 지점"으로 정의합니다.

즉 "AI끼리 알아서 수렴하는 정상 횟수"의 상한은 사실상 5 안팎입니다. 그 이상은 자동 종료가 아니라 에스컬레이션 신호로 다뤄야 한다는 합의에 가깝습니다.

3. 왜 핑퐁이 길어지면 과설계로 가는가

직관이 아니라 분명한 메커니즘이 있습니다. 세 가지로 정리해 봤습니다.

3-1. 비평가 편향 — 리뷰어는 "지적 없음"을 못 견딘다

LLM 리뷰어(Codex)는 "지적할 게 없습니다"라고 말하는 걸 잘 못 합니다. 매 라운드마다 뭔가 코멘트할 거리를 찾으려는 본능이 강합니다. 라운드가 거듭될수록 진짜 큰 이슈는 이미 해결되어 있기 때문에, 남는 건 점점 사소한 트집(nitpick)과 "혹시 모르니까 더 막아두자"식 방어 제안뿐이게 됩니다.

라운드별로 어떻게 변질되는지 예를 들면 이렇습니다.

  • 1라운드 — "이 함수에 null 체크가 없습니다" → 정당한 지적
  • 2라운드 — "이 입력값도 검증(validation)하는 게 좋을 것 같습니다" → 애매함
  • 3라운드 — "이 예외 케이스도 처리하면 더 안전합니다" → 대부분 YAGNI
    (You Aren't Gonna Need It — "어차피 안 쓸 기능 미리 만들지 마라"는 원칙)
  • 4라운드 — "로깅을 추가하면 디버깅에 도움될 것 같습니다" → 요구사항 밖으로 스코프 이탈

3-2. 개별 합리성의 함정 — 누적은 보이지 않는다

각 제안을 개별적으로 보면 다 합리적입니다. 그래서 Claude도 그대로 받아들입니다. 문제는 여기서 일어납니다.

CLAUDE.md의 "과설계 금지"는 전체 누적 효과에 대한 규칙입니다. 그런데 매 라운드의 판단은 개별 제안 단위에서 일어납니다. 규칙과 판단의 단위가 어긋나 있는 셈입니다.

다시 말하면, CLAUDE.md는 "비대해진 코드 전체"를 보고 작동해야 하는데, 핑퐁 루프 안에서는 "비대해지기 직전의 한 줄"을 보고 작동해야 합니다. 후자는 거의 항상 통과됩니다 — 한 줄짜리 null 체크 하나가 과설계로 보이지는 않으니까요. 그 한 줄이 라운드마다 쌓여서 결국 비대해집니다.

3-3. 모호한 종료 조건

VERDICT: APPROVED(리뷰어가 "통과"라고 선언하는 신호)를 리뷰어의 주관에 맡겨두면, 리뷰어가 "이만하면 됐다"고 판단할 때까지 루프가 돕니다. 그런데 위 3-1번에서 본 비평가 편향 때문에, 그 판단이 자꾸 늦어집니다.

모호한 종료 조건 + 지적하려는 본능 = 자연 발산.

이 조합은 거의 결정론적으로 루프를 늘립니다.

4. ASCII로 본 발산 vs 수렴 핑퐁

라운드별 변경 줄 수를 그려보면 차이가 명확합니다.

수렴하는 핑퐁 (정상)
변경 줄 수 (LOC = Lines Of Code)

40├ ●
  │  \
20├   ●
  │    \
 5├     ●____●  ← 3라운드부터 변경량 미미, 종료해도 OK
  └─────────────→ 라운드
    1   2   3   4
 
발산하는 핑퐁 (병리)
변경 줄 수

40├ ●        ●  ← 4라운드 변경량이 1라운드와 맞먹음
  │  \    ╱
30├   ●  ╱       방어 코드 + 트집(nitpick) 제안이 계속 누적
  │    ╲╱
20├    ●         ← 2라운드에 바닥 찍고 다시 상승
  └─────────────→ 라운드
    1   2   3   4

규칙 하나로 요약하면 이렇습니다.

N+1 라운드의 변경량 > N 라운드면, 그 루프는 수렴이 아니라 발산입니다. 즉시 종료시켜야 합니다.

이 신호 하나만 코드 레벨에서 모니터링해도, 위 3-3번의 "모호한 종료 조건" 문제를 상당 부분 자동으로 잡을 수 있습니다.

5. 작업 유형별 권장 상한

지금까지의 근거를 종합해 작업 유형별로 라운드 수를 정리해 봤습니다.

작업 유형권장 상한
버그 수정 · 소규모 변경 · 리네이밍1~2회 또는 루프 생략
일반 기능 구현2~3회
대규모 설계 (auth, 동시성, 데이터 모델)최대 5회
6회 이상금지 — 사람이 들어와야 합니다

SmartScope 본인도 "작은 변경에는 SKILL.md 패턴 자체가 overkill"이라고 명시하고 있습니다. 핑퐁 루프 자체가 비용(토큰, 시간, 그리고 누적되는 과설계 위험)이라는 점을 잊으면 안 됩니다.

6. 핑퐁 게이트를 설계한다면 — 체크리스트 6가지

직접 핑퐁 게이트 스킬을 만든다면 아래는 반드시 들어가야 한다고 봅니다.

6-1. 절대 상한을 코드 레벨에서 못박는다

CLAUDE.md에 "3라운드 이내로 끝내라" 같은 자연어 가이드만 두면 거의 100% 지켜지지 않습니다. 스킬 코드 안에서 카운터 변수로 강제해야 합니다.

디폴트 3, 절대 상한 5.

6-2. 리뷰어 프롬프트에 "지적의 범위"를 못박는다

이게 빠지면 과설계는 거의 확정입니다. 아래처럼 명시적으로 화이트리스트와 블랙리스트를 줍니다.

다음만 지적할 것:
- 동작 오류 (correctness bug)
- 보안 취약점 (security vulnerability)
- 요구사항 미충족
 
다음은 지적 금지:
- 코드 스타일 / 포맷팅
- 리팩토링 제안
- 방어적 코드 추가 (defensive coding)
- 추가 입력 검증 / null 체크 (요구사항에 없는 경우)
- 로깅 / 관측성(observability) 추가 (요구사항에 없는 경우)

화이트리스트만 두지 말고 블랙리스트도 같이 두는 게 핵심입니다. LLM은 "허용된 것 외엔 금지"보다 "이건 명시적으로 금지"라는 신호에 더 강하게 반응합니다.

6-3. 라운드별 변경량 모니터링

N+1 라운드의 변경 크기(diff)가 N 라운드보다 크면 발산으로 간주하고 즉시 종료합니다. 4번 절의 두 번째 패턴이 감지되는 순간 루프를 깹니다. 코드로 강제하기 쉬운 객관적 신호라서, 6-1의 절대 상한과 함께 두 겹의 안전장치가 됩니다.

6-4. 같은 지적이 2라운드 연속 살아남으면 사용자에게 위임

The Pair는 3라운드까지 권하지만, 실제로 해보면 2라운드가 더 안전합니다. 3라운드까지 가면 이미 본래 지적과 무관한 변경이 같이 섞여 들어와 있는 경우가 많습니다. "이 부분이 두 번 연속 지적됐다"는 신호는 AI가 자체 판단으로 닫지 말고 사람에게 넘기는 게 맞습니다.

6-5. 최종 검수는 새 세션에서 단발성으로

루프 내부에서 Codex의 컨텍스트(이전 대화 기억)가 계속 쌓이면, "이 정도면 됐다"는 편향이 자연스럽게 생깁니다. 종료 직전 한 번은 이전 대화 기록이 전혀 없는 새 세션으로 다시 검수합니다. SmartScope가 Level 1.5라고 부르는 패턴입니다.

요점은 이렇습니다 — 핑퐁에 참여한 리뷰어는 이미 그 코드와 정서적으로 얽혀 있습니다. 마지막 한 번은 그 정서가 없는 리뷰어에게 맡기는 게 안전합니다.

6-6. 버그픽스 모드와 설계 모드를 분리한다

같은 게이트로 모든 작업을 다루지 마세요. 버그픽스 모드는 디폴트 1라운드, 설계 모드는 3라운드로 분리해서 운영합니다. 한 가지 라운드 수로 모든 작업을 통과시키려고 하면, 반드시 한쪽(보통 작은 변경 쪽)에서 과설계가 일어납니다.

7. 후일의 나에게 남기는 메모

핑퐁 게이트 스킬을 만들기 전에 스스로에게 던질 질문 셋입니다.

  1. 종료 조건이 객관적인가?APPROVED가 리뷰어의 주관이면 발산합니다. "요구사항 X, Y, Z 충족됨"처럼 체크리스트로 바꿀 수 있는가.
  2. 리뷰어의 지적 범위를 좁혔는가? — 좁히지 않은 리뷰어는 매 라운드마다 새 일을 만들어냅니다. "correctness 외엔 지적 금지"를 명시적으로 박았는가.
  3. 상한을 코드로 강제했는가? — 자연어 가이드는 안 지켜집니다. 카운터를 박았는가. 그리고 그 상한이 작업 유형에 맞게 1~5 범위에 있는가.

이 세 가지가 다 "예"가 아니면, 핑퐁 게이트는 덜 안전하지만 더 비대한 코드를 만들어내는 기계가 됩니다. CLAUDE.md에 무엇을 쓰든 그렇습니다.


참고