
AI 리뷰의 상호 비평 ― 확신도 '높음'으로 틀린 지적을 어떻게 간파할 것인가 | Claude×Roblox 개인 개발
요약
Roblox 개발 과정에서 Gemini CLI와 Claude Code를 활용해 AI 모델 간의 상호 비평 실험을 진행했습니다. AI가 확신을 가지고 내놓은 지적이 실제 의도와 다를 수 있음을 확인하며, 비평의 질을 높이기 위한 프롬프트 설계의 중요성을 다룹니다.
핵심 포인트
- AI 모델 간의 상호 비평이 항상 객관적인 검증으로 이어지지는 않음
- 비평 모델의 비판적 태도에 따라 검증의 가치가 결정됨
- AI가 높은 확신도로 틀린 지적을 할 수 있으므로 사실 확인이 필수적임
- 효과적인 리뷰를 위해 프롬프트에 비판적 관점을 강제하는 설계가 필요함
1. 서론
Roblox 『Wobble Survival: Don't Fall!』을 개인 개발하고 있습니다. 워크플로우는 「Claude.ai로 설계·사양서 작성 → Claude Code로 구현」입니다.
지난번에는 여러 AI 모델에게 동일한 코드를 보여주고 리뷰를 시켰던 이야기를 썼습니다. Gemini CLI와 Claude Code, 두 모델입니다.
그때 「AI는 형식, 인간은 의도」라는 선을 긋고, 행 번호는 기계가 검증해야 한다는 배움을 적었습니다. 이번에는 그 다음 이야기입니다.
두 모델에게 서로의 지적을 비평하게 했습니다.
예상했던 것은 「의견이 갈렸을 때, 어느 쪽이 옳은지를 인간이 판정한다」는 전개였습니다. 실제로 일어난 일은 조금 달랐습니다.
2. 방법
1차 리뷰에서 두 모델이 각각 지적 사항 표를 내놓았습니다. Gemini CLI가 6건, Claude Code가 17건입니다.
이를 상대방에게 붙여넣어 판정하게 했습니다. 던진 프롬프트는 다음과 같습니다.
다음은 다른 AI 모델에 의한 동일 파일(@src/client/init.client.luau)의 리뷰 결과.
각 지적에 대해 「타당/과함/오류/보충 필요」를 판정하고, 이유를 1~2문장으로 작성할 것.
필요하다면 파일을 참조하여 사실 확인을 해도 좋다. 자신의 지적과 내용이 같다면 「일치」라고 기재할 것.
...
의도한 점은 세 가지입니다.
판정을 4가지 분류로 고정했다. 「타당/오류」라는 이진 분류로는 정도의 차이가 뭉개진다 -
사실 확인(裏取り)을 허용했다. 비평만 시키면 인상론이 되기 때문에, 실제 코드를 확인하게 한다 -
모델명을 숨겼다. 「Claude Code의 지적이니까」와 같은 판단이 섞이는 것을 피하고 싶었다
3. 비평은 비대칭적이었다
결과를 먼저 적습니다.
| 비평 방향 | 대상 | 「오류」 판정 | 「과함」 판정 |
|---|---|---|---|
| Claude Code → Gemini | 6건 | 2건 | 0건 |
| Gemini → Claude Code | 17건 | 0건 | 0건 |
Claude Code는 6건 중 2건을 「적용하면 퇴보(Degrade)한다」고 판정했습니다. 각각 근거가 있습니다.
반면, Gemini 측은 17건 모두를 「타당」하다고 판정했습니다. 반론은 제로. 총평은 극찬이었습니다.
이 부분이 예상과 달랐던 지점입니다. 제가 기대했던 것은 「의견이 갈려서 인간이 재판한다」는 전개였습니다. 실제로는 한쪽이 전적으로 긍정했기 때문에 의견 차이가 발생하지 않았습니다.
비평을 시킨다고 해서 자동으로 검증이 되는 것은 아닌 듯합니다. 비평의 가치는 비평하는 측이 얼마나 비판적으로 보느냐에 달려 있습니다. 전적인 긍정만 돌아온다면 대조해 볼 의미가 희박합니다.
이는 체크 관점의 템플릿에 반영해야 할 점이라고 생각합니다. 「최소 1건은 문제점이나 반대 의견을 제시할 것」과 같은 강제성을 부여하는 방향입니다.
4. 하이라이트 ― 확신도 「높음」으로 틀리다
Claude Code가 「오류」라고 판정한 2건 중 1건이 이번에 가장 중요했습니다.
Gemini의 지적 (확신도: 높음)
RenderStepped에서의 거리 판정은 중복이다. BillboardGui.MaxDistance로 대체할 수 있다.
표지판(입구 사인)의 표시·비표시를 매 프레임 거리를 측정하여 제어하고 있는 부분입니다. Roblox의 BillboardGui에는 MaxDistance라는 프로리티가 있어, 일정 거리를 넘으면 자동으로 비표시됩니다. 그것을 사용하면 자체적인 거리 판정은 필요 없을 것이다라는 지적입니다.
코드만 보면 논리적인 지적으로 보입니다.
Claude Code의 반증
BillboardGui.MaxDistance는 카메라로부터의 거리 기준이다. 이 코드는 주석에 명시된 대로 캐릭터(HumanoidRootPart) 거리를 의도하고 있다 (ProximityPrompt와 기준점을 맞추기 위해). 교체는 등가적이지 않고 동작 변경이며, 「중복」이라는 단정은 의도를 놓친 것이다.
이 구현은 의도적인 것이었습니다. 표지판의 표시 조건을 E키로 탑승할 수 있는 조건(ProximityPrompt)과 맞추고 싶었습니다. 카메라 거리로 판정하면, 「표지판은 보이는데 탈 수 없다」거나 「탈 수 있는데 표지판이 사라져 있다」는 불일치가 발생합니다.
그리고 MaxDistance는 카메라 거리 기준이므로, 교체하면 조건이 일치하지 않게 됩니다.
만약 이 지적을 그대로 적용했다면, 잘 작동하던 구현을 망가뜨렸을 것입니다. 게다가 **확신도는 「높음」**이었습니다.
제6탄에서 「AI는 형식을 보고, 인간은 의도를 본다」라고 썼습니다. 그 경계선이 그대로 실증된 형태입니다. AI는 「형식적으로 이상함」과 「의도적으로 그렇게 함」을 구분하지 못합니다. 그리고 구분하지 못한다는 사실을 스스로 인식하지 못합니다. 그렇기에 확신도가 높은 상태로 틀립니다.
5. 수정 방향이 반대로 결정되었다
또 다른 한 건의 「오류」 판정은 성질이 달랐습니다.
두 모델 모두 같은 문제를 검출하고 있었습니다. 시간 취득에 tick()과 os.clock()이 혼재되어 있어, 기준이 다른 값을 비교하고 있는 부분이 있다는 점입니다. 이것은 실제 버그입니다.
문제는 어느 쪽으로 통일할 것인가였습니다.
- Gemini 안:
os.clock()으로 통일 - Claude Code 안:
tick()으로 통일
Gemini의 제안에는 일반론적인 근거가 있습니다. tick()은 권장되지 않으며(deprecated), os.clock()이 권장됩니다. 단일적으로 보면 Gemini 안이 더 올바른 방향입니다.
이에 대해 Claude Code가 내놓은 반증은 다음과 같았습니다.
effectEnd는 ShopUI.setActiveEffect에도 전달되고 있으며, ShopUI 측은 모든 곳에서 tick()과 비교하고 있다 (ShopUI.luau:692 외). os.clock()으로 바꾸면, 다른 파일과의 사이에 새로운 혼재를 발생시킨다.
파일을 넘나드는 확인입니다. 리뷰 대상은 init.client.luau라는 1개의 파일이었지만, 거기서 다루는 값이 다른 파일에도 전달되고 있었습니다. 그쪽이 tick() 기준인 이상, 한쪽만 os.clock()으로 바꾸면 혼재가 이동할 뿐입니다.
결과적으로 tick() 통일을 채택했습니다. 일반론적으로는 퇴보하는 방향이지만, 지금 당장 혼재를 없애기 위해서는 이것이 옳습니다.
os.clock()으로의 전체 이행은 별도의 Issue로 분리하여 추후에 모아서 진행하기로 했습니다. 여기서 효과를 발휘한 것은 지적의 정확성이 아니라, 영향 범위의 확인이었습니다. 같은 문제를 발견하더라도 수정 방향은 반대가 될 수 있습니다. 그리고 방향을 결정하기 위해서는 대상 파일 외부를 볼 필요가 있었습니다.
6. 그럼에도 상호 비평에는 의미가 있다
여기까지 쓰면 「Gemini 측의 지적은 정밀도가 낮았다」는 이야기로 보일 수도 있겠지만, 반대의 기록도 있습니다.
Gemini가 꼽은 6건 중 1건을 Claude Code 스스로가 「자신의 리뷰에서는 놓쳤던 정당한 지적」이라고 인정했습니다. 주석 처리된 채 남아있던 코드 (-- resultUI:dismiss())를 검출한 것입니다.
Claude Code는 17건이나 제시했습니다. 그럼에도 누락이 있었습니다. 그리고 그 누락을, 6건밖에 제시하지 않은 쪽이 채워줍니다.
이것이 여러 모델로 리뷰를 수행하는 의미라고 생각합니다. 어느 쪽이 우수한가가 아니라, 한쪽이 놓친 것을 다른 쪽이 잡아내는 것입니다. 건수의 많고 적음과 망라성(coverage)은 별개의 문제입니다.
7. 결론 나지 않는 것은 실기(実機)에서 확인한다
비평을 시켰음에도 결론이 나지 않은 것도 있습니다.
BillboardGui.MaxDistance = 0이라는 설정이 무엇을 의미하는가라는 논점에서, 두 모델의 주장이 정면으로 엇갈렸습니다.
- Gemini:
0은 무한대를 의미한다 (확신도: 높음) - Claude Code:
0은 비표시(hidden) 쪽으로 작동할 가능성이 있다. 의도를 읽을 수 없다 (확신도: 낮음)
어느 쪽이 맞는지 저도 단정하지 않습니다. 이것은 실기에서 확인해야 할 문제이기 때문입니다.
그리고 한쪽은 확신도 「높음」으로 단정하고 있습니다. AI가 단정하는 것과 그것이 옳은 것은 별개입니다. 결론이 나지 않는 논점이 드러난 것만으로도, 비평을 시킨 가치는 있었습니다.
8. 성과와 Issue로의 퇴피
집약된 지적을 3개 계층으로 나누어 처리했습니다.
| 계층 | 내용 | 대응 |
|---|---|---|
| 확정 버그 | 확인 완료. 동작에 영향을 줌 | 즉시 수정 |
| ... | ... | ... |
결과는 다음과 같습니다.
| 커밋 | 내용 |
|---|---|
| 데드 코드 삭제 | init.client.luau에서 798행 삭제 (제6탄에서 작성한 분량) |
| 연출 구현 | 코인 선취 연출의 HUD 반영 (표시 전용 API를 신설하여, 연출 값과 구매 판정을 분리) |
| 확정 버그 3건 | 시간 기준을 tick()으로 통일 / 구매 토스트의 {item} 미치환 문제를 호출 측에서 해결하도록 수정 / 로그인 알림의 일본어 직접 기입을 L() 함수화 |
| 정리 계열 일괄 대응 | 디버그용 print 삭제, 주석 처리된 잔해 삭제, Shared 중복 취득 통합, 섀도잉 (Shadowing) 해소, 매직 넘버 (Magic Number)의 GameConfig 집약, 간판의 거리 판정에 설계 의도를 명시 (동작은 불변) |
지시서 1건 = 1 커밋으로 운영하고 있으므로, 확정 버그 3건은 1 커밋에 합쳐져 있습니다.
마지막 정리 계열 커밋에, §4에서 작성한 간판 건이 포함되어 있습니다. 잘못 지적된 코드 자체는 바꾸지 않고, 왜 이렇게 구현했는지에 대한 내용을 주석으로 추가했습니다.
의도를 읽어내지 못하면, AI는 형식만으로 판단합니다. 이번에는 운 좋게 한 줄의 주석이 있었기에 반증할 수 있었지만, 그마저도 "ProximityPrompt와 동일한 기점"이라는 짧은 기술이었습니다. MaxDistance로는 대체할 수 없는 이유까지는 적혀 있지 않았습니다. 지적을 받았다는 것은, 설명이 부족했다는 뜻이라고 생각하여 내용을 추가했습니다.
그리고 9건을 GitHub Issue로 격리했습니다. 격리한 것은 동작 변경을 수반하는 것과 사양 확인이 필요한 것들입니다. 릴리스 전에 해야 할지 말지에 따라 선을 그었습니다.
전전회에 썼듯이, 저는 PR(Pull Request) 없이 직접 push로 개발하고 있습니다. Issue는 "지금은 하지 않기로 결정한 것들의 보관소"로 사용하고 있습니다.
9. 요약 / 다음 예고
AI에게 서로의 지적을 비평하게 한 결과입니다.
비평은 비대칭적이었다. 한쪽은 6건 중 2건을 증거와 함께 반증했고, 다른 한쪽은 17건 모두를 긍정했다. 비평의 가치는 비평하는 측의 비판성에 의존한다 -
확신도 '높음'으로 틀린다. 의도적인 구현이 '冗長 (redundant, 불필요하게 길음)'하다고 판정되었다. AI는 "형식적으로 이상한 것"과 "의도적으로 그렇게 만든 것"을 구분하지 못한다 -
수정 방향이 반대로 결정된다. 같은 버그를 발견하더라도 영향 범위를 확인하면 방향이 달라진다 -
건수가 많은 쪽에도 누락이 있다. 17건을 제시한 쪽이 놓친 것을 6건을 제시한 쪽이 찾아냈다 -
결론 나지 않는 논점은 실기(実機)에서 확인한다. AI가 단정하는 것과 옳은 것은 별개 -
그리고 이번에 가장 효과적이었던 것은, 코드에 주석으로 의도가 적혀 있었다는 점이었습니다. 간판 건을 반증할 수 있었던 것은 구현 시점에 이유를 남겨두었기 때문입니다. 적어두지 않았다면 두 모델 모두 똑같은 오지적을 했을 것이고, 저 또한 깨닫지 못한 채 코드를 망가뜨렸을지도 모릅니다.
그렇다고는 해도, 그 주석은 "ProximityPrompt와 동일한 기점"이라는 한 줄이었습니다. MaxDistance로는 대체할 수 없는 이유까지는 적혀 있지 않았습니다. 그래서 지적을 받은 것입니다. 오지적을 받았다는 것은, 의도가 충분히 적혀 있지 않았다는 뜻이기도 합니다. 그렇게 생각하여 리뷰 후에 설계 의도를 추가했습니다.
다음 회는 이 내용의 이어지는 이야기입니다. 같은 코드를 다시 한번 리뷰하게 했을 때 무엇이 나왔는지를 쓰겠습니다. 한 번의 리뷰로 충분한가에 대한 이야기입니다.
Discussion

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