수행할 작업: 에이전트의 실수를 잡아내는 테스트를 하나 추가한 다음, Claude Code가 이를 스스로 실행하고 테스트가 실패하는 동안에는 턴을 종료할 수 없도록 연결합니다. 약 20분 정도 소요됩니다.
대상: 실제 코드에 Claude Code를 사용하고 있으며, 에이전트가 제대로 해결하지 않았음에도 "수정했습니다, 테스트 통과, 완료되었습니다"라고 말한 경험이 있는 분.
건너뛰어도 좋은 분: 에이전트가 이미 실패한 테스트 상태에서 종료하지 못하도록 설정되어 있다면, 이 단계는 이미 지나온 것입니다.
필요 사항: Claude Code (버전 2.1.143 이상), 예제 실습을 위한 Python 3 (내장 테스트 러너를 사용하므로 별도 설치 불필요), 그리고 본인의 프로젝트.
1. 원하는 바를 명시하는 테스트 작성하기
테스트는 코드를 실행하고 코드가 올바르게 작동하는지 확인하는 작은 프로그램입니다. 여기서 중요한 단어는 _당신의 것(yours)_입니다. 테스트는 당신이 코드가 수행하기를 원하는 바를 인코딩해야 합니다. 왜냐하면 에이전트가 코드와 테스트를 모두 작성하게 되면, 에이전트가 두 가지를 조용히 일치시켜 버릴 수 있기 때문입니다. 검증은 에이전트 외부에서 이루어져야 하며, 그렇지 않다면 그것은 검증이 아닙니다.
가장 간단한 예시를 소개합니다: 함수 하나와 그에 대한 테스트입니다. 에이전트는 add 함수를 수정하라는 요청을 받았고, 완료되었다고 보고했습니다.
calc.py
def add(a, b):
return a - b # 틀림
test_calc.py
from calc import add
import unittest
...
두 파일을 동일한 빈 폴더에 저장하고 해당 위치에서 터미널을 엽니다. 에이전트는 확신에 차 있었습니다. 하지만 테스트는 에이전트가 얼마나 확신했는지 신경 쓰지 않습니다. add(2, 3)의 결과값은 5가 아닌 -1로 나왔고, 이제 당신은 1초 만에 "완료"가 사실이 아니라는 것을 알게 되었습니다.
2. 에이전트가 무시할 수 없는 게이트(Gate) 설치하기
이 내용을 프로젝트의 check.sh로 저장하세요:
#!/bin/bash
cd "$(dirname "$0")"
tmpdir=$(mktemp -d)
...
여기서 2가 핵심적인 부분입니다. 일반적인 실패는 1을 반환하며 종료됩니다. 하지만 우리는 의도적으로 2를 반환하며 종료하는데, 이는 Claude Code가 다음에 이 값을 어떻게 처리하는지 때문입니다.
3. 게이트가 스스로 실행되도록 만들기
Claude Code에는 훅(Hooks)이 있습니다. 이는 요청받지 않아도 특정 이벤트 발생 시 Claude Code가 대신 실행하는 스크립트입니다. 우리가 원하는 것은 에이전트가 턴을 종료하려고 시도하는 즉시 실행되는 Stop 훅입니다.
check.sh를 여기에 연결하세요. 프로젝트에 .claude 폴더가 없다면 생성한 다음, .claude/settings.json 파일을 생성하거나 열어서 다음 내용을 추가하세요:
{
"hooks": {
"Stop": "bash \"${CLAUDE_PROJECT_DIR}/check.sh\""
...
Stop 훅이 종료 코드 2로 종료되면, Claude Code는 **중단을 차단(blocks the stop)**합니다. 즉, 에이전트가 작업을 마치는 것을 거부하고, 테스트 실패 내용을 이유로 에이전트에게 다시 전달하여 계속 작업하게 만듭니다. 테스트 스위트(suite)가 실패(red) 상태인 동안에는 에이전트가 완료되었다고 말할 수 없습니다. 이제 턴(turn)을 종료할지 여부는 에이전트가 아니라 테스트 스위트가 결정하기 때문입니다.
4. 여전히 문제가 발생하는 경우
- 항상 차단하는 게이트는 루프(loop)를 유발할 수 있습니다. Claude Code는 8회 연속으로 차단되면 스스로 턴을 종료합니다 (
CLAUDE_CODE_STOP_HOOK_BLOCK_CAP 환경 변수로 이 제한을 변경할 수 있습니다).
- 대규모 스위트는 모든 중단 과정을 느리게 만듭니다.
check.sh가 빠른 하위 집합(수정한 부분 근처의 테스트)을 가리키도록 설정하고, 전체 실행은 CI(지속적 통합)에 맡기세요.
- 통과(Green) 상태는 테스트의 품질만큼만 유효합니다. 게이트는 테스트가 통과되었음을 증명할 뿐, 테스트가 _충분하다_는 것을 증명하지는 않습니다.
- 테스트가 닿는 부분만 보호합니다. 테스트되지 않은 코드, 서술된 주장, "문서를 확인했습니다"와 같은 말들은 게이트가 감지할 수 없습니다.
5. 게이트가 작동하는지 증명하기
게이트가 작동한다는 것을 제 말이나 에이전트의 말만 믿지 마세요. 의도적으로 무언가를 망가뜨려 보세요. 테스트가 실패하도록 코드를 한 줄 변경한 다음, Claude Code에게 작업을 마무리하라고 요청하세요. 에이전트가 멈추는 대신, 다시 끌려 들어와 실패 내용을 전달받는 모습을 지켜보세요. 한 번이라도 차단되는 것을 직접 확인하고 나면, 에이전트가 깔끔하게 작업을 마치는 것이 비로소 의미를 갖게 됩니다.
이것이 바로 기초(floor)이며, 매우 실질적인 기초입니다. 에이전트는 더 이상 작업이 끝났다고 스스로 결정할 수 없습니다. 에이전트가 반박할 수 없는 무언가가 대신 결정합니다. 다음 단계는 게이트의 범위를 "테스트가 통과됨"에서 "테스트가 통과할 가치가 있음"으로 넓히는 것입니다.