본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 06. 15. 11:55

자신의 코드를 스스로 리뷰할 수 없다 — Evaluator가 실제로 잡아낸 것들

요약

LLM이 생성한 코드의 확인 편향을 해결하기 위해 독립된 컨텍스트를 가진 Evaluator 에이전트의 필요성을 설명합니다. Evaluator는 구현자의 선입견 없이 API 사양 정합성 및 코드 완성도를 검증하여 실행 전 오류를 방지합니다.

핵심 포인트

  • LLM은 생성 시의 선입견 때문에 자신의 코드 버그를 스스로 찾기 어려움
  • Evaluator는 새로운 컨텍스트로 기동하여 구현자의 편향을 배제함
  • API 사양 정합성 검증을 통해 실행 전 오류(Runtime Error) 방지 가능
  • 단순 동작을 넘어 UI 설명 누락 등 코드의 완성도(Correctness)를 높임

자신의 코드를 스스로 리뷰할 수 없다 — Evaluator가 실제로 잡아낸 것들

LLM에는 「확인 편향 (Confirmation Bias)」이 있다

인간에게도 "자신이 작성한 코드의 버그는 스스로 찾아내기 어렵다"는 문제가 있다. 작성 직후에는 "이대로 동작할 것이다"라는 확신이 강해 눈이 미끄러지기 때문이다.

Claude Code는 이 문제가 더욱 심각하다.

코드를 생성한 Claude Code에게 "이 코드를 리뷰해줘"라고 요청하면, 생성할 때와 동일한 컨텍스트와 동일한 선입견을 가진 채 리뷰가 진행된다. 생성 시에 "pytesseract의 image_to_string은 인스턴스 메서드(Instance Method)로 호출할 수 있다"라고 오해했다면, 리뷰 시에도 동일한 오해를 가진 채 "문제없습니다"라고 답변한다.

이를 해결하는 것이 Evaluator 에이전트다.

3 에이전트 구성의 메커니즘

Evaluator는 **새로운 컨텍스트로 기동하는 서브 에이전트 (Sub-agent)**다. 구현자(Claude Code 본체)의 선입견을 이어받지 않는다. "왜 이 코드가 이렇게 작성되었는지" 모르는 상태에서 읽는다. 그렇기에 발견할 수 있다.

Evaluator가 실제로 잡아낸 3가지 사례

사례 1: 「동작함」과 「올바르게 동작함」의 차이 — Discord Bot의 @app_commands.describe 누락

웹사이트에서 데이터를 크롤링하여 Discord에서 확인할 수 있는 Bot을 개발했다 (이 Bot의 구축 기사). 슬래시 커맨드(Slash Command)에 count 파라미터를 추가한 후, 동작 확인 시에는 문제가 없었다.

하지만 Evaluator의 체크에서 지적이 들어왔다.

# ❌ 동작하지만 불완전함
@tree.command(name="report")
async def report_command(interaction: discord.Interaction, count: int = 3):
...

@app_commands.describe는 Discord UI에 파라미터 설명을 표시하는 데코레이터(Decorator)다. 생략해도 커맨드는 동작한다. 하지만 사용자에게는 "count에 무엇을 입력해야 하지?"라는 의문이 전달되지 않는다.

「동작함」과 「올바르게 동작함」은 별개의 것이다. 스스로 리뷰했다면 "동작 확인 완료"로 통과시켰을 것이다. 이 문제를 깨닫기까지 Bot을 며칠간 운용하다가 수정하게 되었다 (30~60분의 손실). Evaluator의 체크리스트에 "discord.py의 슬래시 커맨드 API 사양 정합성"이 포함되어 있었기에 다음부터는 방지할 수 있다.

사례 2: 실행 전에 방지한 API 실수 — pytesseract의 인스턴스 메서드 오용

데스크톱 게임 도구에 OCR 기능을 구현할 때, 다음과 같은 코드를 작성했다.

# ❌ 인스턴스 메서드로 호출함 (존재하지 않음)
import pytesseract
ocr = pytesseract.Pytesseract()
...

실제로는 Pytesseract에 생성자(Constructor)가 존재하지 않는다. 모듈 함수로서 직접 호출하는 것이 올바르다.

이 코드를 실행하면 당연히 에러가 발생한다. 하지만 Evaluator가 실행 전에 지적했다.

Evaluator의 체크 항목 중 하나가 "외부 라이브러리의 API 사양 정합성 (메서드명·속성명이 실제로 존재하는지)"이다. Claude Code 본체는 라이브러리의 API를 "알고 있다고 가정"하고 작성하지만, 그 지식이 오래되었거나 틀릴 수 있다. 독립된 Evaluator가 "이 API가 정말 존재하는가"를 확인한다.

실행해서 실패한 뒤 수정하는 것(수십 분의 손실)이 아니라, 코드 완성 시점에 방지한다.

사례 3: Electron 앱의 IPC 비동기 API 불일치

Electron 앱에서 메인 프로세스(Main Process)와 렌더러 프로세스(Renderer Process) 간의 통신을 구현했다.

// ❌ 송신 측과 수신 측의 API가 맞지 않음
// renderer.js (송신)
ipcRenderer.send('search', query) // 일방향 송신
...

ipcRenderer.send는 일방향 통지다. ipcMain.handle은 Promise를 반환하는 양방향 통신이다. 이 두 가지를 조합하면 송신은 성공하지만 반환값(Return Value)을 획득할 수 없다.

개발 환경에서는 콘솔을 보고 있었기에 에러를 인지하지 못하고 "동작했다"라고 생각했다. 이 상태로 출시했다면 검색 기능이 소리 없이 작동하지 않는 앱이 되었을 것이다 (원인 조사에 1~2시간이 걸리는 종류의 버그). Evaluator가 "IPC 통신의 API 사양 정합성 (invoke/handle 대응)"을 확인하여 발견했다.

설정 방법

Evaluator 에이전트 (agents/evaluator.md)

다음은 Evaluator에게 전달하는 실제 시스템 프롬프트 (system prompt) 내용이다. 체크 관점을 여기에 명시함으로써, 다음번부터도 동일한 종류의 실수를 자동으로 검출할 수 있다.

---
name: evaluator
description: |
...

실제 호출 방법

# Claude Code의 규칙으로서 rules/에 기재해 둔다
## 코딩 태스크의 필수 하네스 (Harness)
신규 파일을 2개 이상 생성하거나, 기능 추가·리팩터링 (Refactoring) 시에는
...

「동작함」에서 멈추지 않기 위해

세 가지 사례의 공통점은 「동작 확인 단계에서는 통과해 버리는 문제」라는 점이다.

  • @app_commands.describe가 없어도 커맨드는 동작한다 - pytesseract의 오용은 실행하기 전까지는 알 수 없다
  • Electron의 IPC 불일치는 표면상으로는 송신이 성공한다

「동작한다」는 사실이 「올바르게 동작한다」는 보증은 아니다. Evaluator는 이 간극을 메운다.

모듈 완성 시점에 Evaluator를 한 번 거친다. 그것만으로도 「나중에 발견되어 수정하는」 비용이 대폭 줄어든다.

요약

  • Claude Code는 생성 시와 동일한 컨텍스트 (Context)에서 리뷰하기 때문에 확인 편향 (Confirmation Bias)이 발생한다
  • Evaluator 에이전트는 독립된 새로운 컨텍스트에서 동작하기 때문에 선입견을 이어받지 않는다
  • 「동작함」과 「올바르게 동작함」의 차이 (@app_commands.describe, pytesseract API, Electron IPC)를 Evaluator가 발견했다
  • 체크 관점을 Evaluator의 시스템 프롬프트 (System Prompt)에 명시함으로써 다음부터 동일한 종류의 실수를 자동으로 검출할 수 있다
  • 모듈 완성 시점에 한 번만 끼워 넣으면 된다

3 에이전트 구성의 설계 사상과 실천에 대해서는 유료 서적 「Claude Code 하네스 엔지니어링 실천 플레이북 (Playbook)」에서 체계화되어 있다.

감상이나 "이 케이스는 어떻게 하나요?"와 같은 질문은 댓글로 편하게 남겨주시면 감사하겠습니다. '좋아요'도 큰 힘이 됩니다.

저자 코멘트

Discussion

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0