Claude Code를 실무에 붙이려면 모델이 아니라 하네스를 설계해야 한다

같은 Claude 모델을 두고도 어느 날은 멀쩡한 패치를 만들어내고, 어느 날은 엉뚱한 파일을 고친 채 “완료했다”고 내미는 경험이 있을 것이다. 이 차이를 모델 탓으로 돌리기 쉽지만, 실제로는 모델이 일하는 환경, 곧 하네스(Harness)가 결과를 가른다. 모델이 똑같아도 컨텍스트가 흐리면 엉뚱한 파일을 건드리고, permissions가 느슨하면 위험한 명령이 그대로 실행되며, 검증 구조가 없으면 틀린 수정안을 자신 있게 내놓는다

Claude Code의 코어는 의외로 단순하다. 모델을 호출하고, 모델이 도구 사용을 요청하면 그 도구를 실행하고, 결과를 다시 모델에 돌려주는 루프다. 시스템의 대부분은 이 루프 자체가 아니라 루프 주변의 운영 하네스에 존재한다. 권한 시스템, 도구 라우팅, 컨텍스트 압축, 훅, 스킬, MCP, 서브 에이전트 위임, 세션 영속화 같은 요소들이 Claude Code를 실무 도구로 만든다

이 글은 Claude Code를 실제 프로젝트에 붙일 때 반드시 설계해야 하는 6가지 하네스를 정리한다. Context, Permission, Verification, Tool, Sub-agent & Worktree, Debugging Harness다. 모델을 더 똑똑하게 만드는 이야기가 아니라, 모델이 안전하고 검증 가능한 경계 안에서 일하게 만드는 이야기다

하네스 엔지니어링이란 무엇인가

하네스 엔지니어링은 AI 모델이 실제 작업을 안전하게 수행할 수 있도록 주변 실행 구조를 설계하는 일이다. 핵심은 모델 자체가 아니다. 모델은 이미 충분히 많은 일을 할 수 있다. 문제는 모델이 실제 파일 시스템, 터미널, Git, Docker, 테스트 러너, 외부 API에 닿는 순간 시작된다.

채팅창의 AI가 틀리면 사용자가 읽고 무시하면 된다. CLI에서 파일을 수정하고 셸 명령을 실행하는 AI는 다르다. 이때는 답변 품질보다 실행 경계가 중요하다. 자율성이 커질수록 실패 비용도 같이 커진다. Claude가 잘못된 파일을 수정하고, 테스트 없이 완료를 선언하고, .env 같은 시크릿 파일을 읽으려 하고, 관련 없는 리팩터링을 섞고, Git 브랜치를 지저분하게 만들 수 있다

좋은 하네스 엔지니어링의 목적은 무한 자율성이 아니라 검증 가능한 경계 안의 적절한 자율성이다. 다음 질문에 답할 수 있어야 한다

  • Claude가 반드시 알아야 할 정보는 무엇인가
  • Claude가 절대 보면 안 되는 파일은 무엇인가
  • Claude가 자유롭게 실행해도 되는 명령은 무엇인가
  • 실행 전에 반드시 사람이 승인해야 하는 명령은 무엇인가
  • 어떤 테스트가 통과해야 “완료”라고 볼 수 있는가
  • 수정 범위가 예상보다 넓어졌을 때 어디서 멈춰야 하는가

Claude Code의 7개 구성과 에이전트 루프

하네스를 설계하기 전에 Claude Code의 구조를 알아야 한다. Claude Code는 다음 7개 구성으로 나눌 수 있다

User → Interface → Agent Loop → Permission → Tools → Execution → (다시 Agent Loop)
                                                          ↓
                                State & Persistence (Transcript, CLAUDE.md, Memory,
                                Compact Summary, Sub-agent Sidechain, Resume/Fork)

사용자가 인터페이스로 요청을 넣으면 Agent Loop가 컨텍스트를 조립하고 모델을 호출한다. 모델이 도구 사용을 요청하면 Permission 시스템을 통과한 뒤 Tool이 실행된다. 실행 결과는 다시 Agent Loop로 돌아가 다음 이터레이션이 이어진다. 동시에 State & Persistence 레이어가 Session Transcript, CLAUDE.md, 메모리, Compact 경계, 서브 에이전트 사이드체인 같은 기록을 유지한다

이 구조에서 핵심은 모델과 실행 환경 사이에 하네스가 있다는 점이다. 모델은 “이 파일을 읽고 싶다”, “이 명령을 실행하고 싶다”고 요청만 한다. 실제 실행은 Claude Code의 하네스가 맡는다. 하네스는 도구 요청을 받아 권한을 확인하고, 훅을 실행하고, 샌드박스나 Deny 규칙을 적용한 뒤 결과를 모델에 돌려준다

이 분리가 중요한 이유는 모델이 아무리 강력해져도 직접 모든 행동을 마음대로 실행하면 위험하기 때문이다. 하네스는 모델의 자율성을 실행 가능한 형태로 바꾸면서 동시에 통제 가능한 경계 안에 둔다. 즉 Claude Code의 실무 품질은 모델 성능과 하네스 품질의 곱에 가깝다. 모델이 좋아도 하네스가 약하면 결과는 불안정하다

Claude Code가 계층적 설정을 제공하는 이유도 여기에 있다. 사용자 설정, 프로젝트 설정, 로컬 프로젝트 설정이 있고 settings.json을 통해 권한, 환경 변수, 훅, 모델을 구성한다. CLAUDE.md는 시작 시 컨텍스트를 제공한다. 프롬프트 하나로 끝나는 시스템이 아니라, 프로젝트와 팀 단위로 동작을 조정하는 정책 기반 시스템이다

문제가 생겼을 때 “모델이 왜 이러지”가 아니라 “어느 하네스가 약한지”를 먼저 묻는 것이 정석이다

증상점검할 하네스
관련 없는 파일을 건드린다Context
위험한 명령을 실행하려 한다Permission
수정은 했는데 검증이 없다Verification
MCP나 훅이 꼬였다Tool
병렬 작업이 충돌한다Sub-agent & Worktree
로그를 줘도 원인을 못 좁힌다Debugging

Context Harness – Claude가 보는 작업면을 설계한다

Context Harness의 목적은 Claude가 작업에 필요한 정보는 충분히 보게 하고, 필요 없는 정보는 보지 않게 만드는 것이다. 큰 코드베이스에서는 컨텍스트 부족보다 컨텍스트 과잉이 더 자주 문제를 만든다. 관련 없는 파일, 오래된 대화, 이전에 틀렸던 가설, 너무 긴 로그, 애매한 요구사항이 섞이면 Claude는 그럴듯하지만 잘못된 방향으로 움직인다

Claude Code의 메모리 문서에 따르면 각 세션은 fresh context window로 시작한다. 세션을 넘어 지식을 가져가는 메커니즘은 사용자가 작성하는 CLAUDE.md와 Claude가 작성하는 auto-memory다. 두 파일은 강제 설정이 아니라 컨텍스트로 제공된다. 즉 CLAUDE.md에 적었다고 해서 강제되는 것이 아니라, Claude가 더 잘 따를 수 있도록 정보가 주입될 뿐이다. 반드시 막아야 하는 행동은 Permission이나 Hooks 같은 실행 단의 하네스로 옮겨야 한다

Context Harness를 설계할 때 가장 먼저 구분해야 하는 4가지 종류는 다음과 같다

  • Instruction: Claude가 어떻게 행동해야 하는지에 대한 규칙. “테스트 없이 완료를 선언하지 말 것”, “API 응답 스키마를 임의로 바꾸지 말 것”
  • Context: 작업을 이해하는 데 필요한 배경. 프로젝트 구조, 관련 파일, 현재 브랜치, 프레임워크, 런타임
  • Evidence: 지금 문제를 보여주는 관찰 자료. 실패한 테스트 출력, 스택 트레이스, 빌드 에러, CI 로그
  • State: 지금까지 진행된 작업 기록. 이미 시도한 해결책, 실패한 가설, 변경된 파일, 압축된 요약, 서브 에이전트 결과

이 네 가지가 구분되지 않으면 정보를 많이 줘도 흐린 정보를 준 셈이 된다. 좋은 Context Pack은 전체 프로젝트를 던지는 것이 아니라 작업면을 잘라서 주는 것이다

다음은 같은 요청의 두 가지 표현이다. 위쪽이 컨텍스트 흐린 요청, 아래쪽이 Context Pack을 갖춘 요청이다

이 로그인 버그를 고쳐줘.
[목표] /api/login에서 발생하는 500 에러를 해결한다.

[증거]
- 실패 로그: <stack trace 첨부>
- 재현 명령: curl -X POST .../api/login

[관련 파일 후보]
- src/auth/AuthService.ts
- src/auth/AuthController.ts
- src/auth/strategies/jwt.strategy.ts
- src/auth/__tests__/auth.service.spec.ts

[수정 금지]
- DB 스키마
- Public API response shape
- .env 및 시크릿 관련 파일

[지시]
1. 먼저 원인 후보를 3개 이하로 좁힌다
2. 수정 전에 어떤 파일을 볼지 제한한다
3. 검증 명령은 npm test -- auth 만 사용한다

컨텍스트는 희소 자원이다. Claude Code는 시스템 프롬프트, 환경 정보, CLAUDE.md 계층, auto-memory, 도구 메타데이터, 대화 히스토리, 도구 결과, 서브 에이전트 요약, 압축 요약을 조합해 컨텍스트 윈도우를 만든다. 컨텍스트가 커지면 압축이 일어나지만 압축은 항상 손실을 동반한다. 반복되는 팀 규칙이나 핵심 아키텍처는 CLAUDE.md에 두고, 강제해야 하는 규칙은 Permission이나 Hooks로 옮겨야 한다

Claude가 헛돌면 설명을 더 붙이는 것이 아니라 컨텍스트를 다시 설계해야 한다. 관련 파일과 무관한 파일을 분리하고, 수정 금지 범위를 명확히 하고, 잘못된 가설을 폐기하고, 필요하면 새 세션을 연다. 컨텍스트가 오염된 상태에서 대화를 이어가면 Claude는 잘못된 가설을 흐름으로 받아들여 더 정교한 오답을 만든다

Context Harness의 목적은 정보를 많이 주는 것이 아니라 Claude가 올바른 첫 번째 행동을 하게 만드는 것이다. 큰 코드베이스에서 첫 번째 행동은 보통 수정이 아니라 탐색이다

Permission Harness – AI가 할 수 있는 행동 반경을 설계한다

Claude Code는 파일을 읽고, 코드를 수정하고, 셸 명령을 실행할 수 있다. 이 능력이 강한 이유는 실제 프로젝트 작업에 붙을 수 있기 때문이다. 동시에 위험한 이유도 같은 곳에 있다. AI가 파일 시스템과 터미널에 닿는 순간 실수는 잘못된 답변이 아니라 실제 변경, 실제 삭제, 실제 배포, 시크릿 노출이 된다

Claude Code 공식 권한 문서는 다음을 분명히 한다. Read 작업은 승인 없이 가능할 수 있지만 Shell 실행과 파일 수정은 승인 흐름이 필요하다. 권한 규칙은 settings 계층을 따르며, 어떤 레벨에서 Deny된 도구는 다른 레벨에서 Allow할 수 없다. Deny는 단순 옵션이 아니라 상위 정책이다

Permission Harness를 설계할 때 잘못된 출발점은 “Claude를 믿을 수 있는가”다. 옳은 질문은 “이 행동이 실패했을 때 비용이 얼마인가”다. grep은 실패 비용이 낮다. npm test는 시간이 걸려도 파일을 수정하지 않는다. 반면 rm, git push, DB 마이그레이션, 프로덕션 배포, 시크릿 파일 읽기는 실패 비용이 크다. 권한은 신뢰의 문제가 아니라 실패 비용 기반의 행동 반경 설계다

Permission Harness는 보통 3단계로 설계한다

등급행동 예시처리
SafeRead, Grep, npm test, type check자동 허용
Needs ApprovalEdit, Write, git commit, npm install, docker compose사람 승인
Denyrm -rf, .env 읽기, git push --force, kubectl delete, DB drop항상 차단

핵심은 안전한 명령과 되돌릴 수 있는 명령을 구분하는 것이다. git commit은 되돌릴 수 있지만 팀 워크플로우에 영향을 주므로 Ask가 맞다. npm install은 lock 파일을 변경하고 의존성 트리를 바꾸므로 프로젝트에 따라 Ask가 맞다. docker compose는 로컬에서 안전해 보이지만 포트 충돌과 볼륨 변경 가능성이 있다. 반대로 .env 읽기는 변경이 없어도 시크릿 노출 위험이 있으므로 Deny여야 한다

Claude Code 설정 문서는 민감한 파일 접근을 막기 위해 다음 같은 패턴을 제시한다

{
  "permissions": {
    "deny": [
      "Read(./.env*)",
      "Read(./**/secrets/**)",
      "Bash(rm -rf:*)",
      "Bash(git push --force:*)"
    ],
    "ask": [
      "Bash(git commit:*)",
      "Bash(npm install:*)",
      "Bash(docker compose:*)",
      "Edit",
      "Write"
    ]
  }
}

권한 시스템은 Deny First다. 권한은 단일 Yes/No 프롬프트가 아니라 여러 층으로 적용된다. 모델이 어떤 도구를 요청하든 하네스는 먼저 Deny 규칙을 확인한 뒤 모드, 분류기, 샌드박스 같은 다른 레이어를 통해 실행 가능 여부를 판단한다. 이 구조가 중요한 이유는 사람도 반복되는 권한 프롬프트에 익숙해지면 그냥 승인을 누르기 때문이다. 좋은 Permission Harness는 사람의 집중력 하나에 의존하지 않는다

Permission Harness는 Claude가 아무것도 못하게 막자는 것이 아니다. 반복적이고 안전한 검증 명령은 열어주고, 실패 비용이 큰 행동만 사람이 잡는 구조를 만드는 것이다. 이 기준이 생기면 Claude Code는 팀의 반복 작업을 줄이면서도 위험한 행동은 사람이 검토하는 안전한 작업 시스템이 된다

Verification Harness – 결과를 3개 레이어로 검증한다

가장 위험한 순간은 Claude가 틀렸는데도 그럴듯하게 “완료됐다”고 보고하는 순간이다. AI 에이전트는 로컬 컨텍스트 안에서는 설득력 있는 수정안을 만들지만, 그 수정안이 전체 코드베이스의 컨벤션을 깨거나 기존 테스트를 우회하거나 관련 없는 리팩터링을 섞거나 엣지 케이스를 놓칠 수 있다

Claude Code 논문은 에이전트 시스템의 핵심 충돌로 capability와 reliability를 든다. 모델이 빠르게 코드를 만들수록 작업 속도는 올라가지만 제한된 컨텍스트 때문에 전체 일관성을 놓치기 쉽다. 좋은 local decision이 나쁜 global outcome을 만들 수 있다는 것이다. Claude가 함수 안에서는 그럴듯한 코드를 만들어도 프로젝트 전체의 아키텍처 경계, 테스트 전략, API 계약, 마이그레이션 정책을 자동으로 보존한다고 가정해서는 안 된다

Verification Harness는 3개 레이어로 설계한다

Claude Patch
    │
    ├─ Layer 1: Automated Checks
    │   (unit, integration, lint, type check, build, smoke)
    │
    ├─ Layer 2: Diff Review
    │   (수정 범위, Public API 변경, 시크릿 파일 변경, 불필요한 리팩터)
    │
    └─ Layer 3: Counter-example
        (엣지 케이스, invalid input, race condition, regression)
            │
            └─→ PR / Branch / Rollback

Layer 1은 자동 검증이다. 테스트, lint, type check, 빌드, smoke 테스트처럼 반복 가능한 검증이다. Layer 2는 구조 검증이다. diff를 보고 수정 범위가 예상과 맞는지, 공개 API를 바꾸지 않았는지, 스키마를 건드리지 않았는지, 불필요한 리팩터가 섞이지 않았는지 확인한다. Layer 3은 반례 검증이다. 제안된 수정안이 어떤 입력에서 틀릴 수 있는지, 동시성이나 설정 상황에서 깨질 수 있는지 확인한다

흔한 오해는 “테스트가 통과하면 검증 끝”이다. 테스트 통과는 시그널이지 충분조건이 아니다. 레거시 코드베이스는 커버리지가 낮을 수 있다. Claude가 수정한 영역에 테스트가 없을 수도 있다. 반대로 테스트 자체가 outdated여서 실패하는 경우도 있다. Verification Harness는 테스트 실행이 아니라 “테스트가 무엇을 보장하고 무엇을 보장하지 않는지”까지 다뤄야 한다

다음은 검증을 강제하는 프롬프트 템플릿이다

방금 수정한 패치를 다음 기준으로 검토한다.

1. 원래 요청 범위를 벗어난 변경이 있는지
2. Public API 또는 response schema가 바뀌었는지
3. 기존 테스트 없이 비즈니스 로직이 변경되었는지
4. 이 수정이 실패할 수 있는 반례 입력은 무엇인지
5. 추가해야 할 regression 테스트가 있는지 (있다면 먼저 제안)
6. 위험도가 높은 변경 파일을 우선순위로 정리

위 6개 항목을 항목별로 답한 뒤, 자가 진단 결과를 한 줄로 요약한다.

이 프롬프트는 Claude에게 “코드가 좋아 보여?”라고 묻지 않는다. 검토 기준을 제공한다. 검증을 AI에 맡길 때도 검증 기준은 사람이 설계해야 한다

같은 컨텍스트의 Claude에게 자기 패치를 다시 검토시키면 같은 가설을 공유하므로 블라인드 스팟이 반복된다. 이때는 서브 에이전트를 분리하거나, 새 세션에 변경분만 주고 리뷰시키거나, worktree에서 별도로 검증을 돌리는 것이 좋다

검증 하네스의 마지막 단계는 롤백 가능성이다. 좋은 자동화는 실패하지 않는 자동화가 아니라 실패해도 되돌릴 수 있는 자동화다. 브랜치를 나누고, diff를 작게 유지하고, 검증 명령을 PR description에 기록하고, 롤백 방법을 함께 적는다. “Claude가 고쳤고, 이 테스트를 통과했고, 이 변경 범위 안에 있고, 이 반례를 확인했고, 문제가 생기면 이렇게 되돌릴 수 있다”까지 가야 AI 패치를 안전하게 다루는 개발자다

Tool Harness – Built-in, Skills, MCP, Hooks를 어디에 배치할지 결정한다

많은 자료는 Built-in Tools, Skills, MCP, Hooks를 기능 단위로 나열한다. 하네스 엔지니어링 관점에서는 이 기능들을 단순 나열하면 안 된다. “무엇을 더 붙일 수 있는가”가 아니라 “에이전트 루프의 어느 지점에 개입할 것인가”가 중요하다

Claude Code 논문은 확장 구조를 MCP, 플러그인, Skills, Hooks로 분석한다. 같은 역할이 아니다

  • MCP: 외부 시스템을 도구 인터페이스로 연결한다
  • Skills: 반복 절차나 도메인 특화 지침을 낮은 컨텍스트 비용으로 불러온다
  • Hooks: 도구 실행 전후나 세션 라이프사이클에 결정론적으로 개입한다
  • Plugins: 위 요소들을 패키징해 배포한다

이 구분이 잡히면 “Skills와 MCP 중 무엇을 쓸지”가 더 이상 헷갈리지 않는다. 에이전트 루프를 3개 지점으로 나누면 명확해진다

assemble()         →     model()           →   execute()
모델이 무엇을 보는가      모델이 무엇을 부르는가         실행 전후에 무엇을 강제하는가
─────────────────    ───────────────────      ──────────────────────
CLAUDE.md, Skills    MCP, Built-in tools        Hooks, Permissions

Skills는 모델에게 반복 절차를 알려주는 것에 가깝다. “백엔드 디버깅” 스킬을 만들어두면 Claude는 로그 정리, 원인 후보 좁히기, 관련 파일 탐색, 검증 명령 제한이라는 절차를 일관되게 따른다. Skills는 외부 시스템을 연결하지 않는다. 현재 컨텍스트 안에서 Claude가 더 일관된 절차로 생각하게 만든다. 팀의 PR 작성 규칙, 커밋 컨벤션, 디버깅 절차는 Skills로 만들기 좋다

MCP는 외부 시스템을 Claude의 도구 인터페이스로 여는 메커니즘이다. GitHub, 데이터베이스, 모니터링, 내부 API, Jira 같은 티켓 시스템이 대상이다. MCP를 붙이면 Claude가 더 강력해지지만 동시에 외부 시스템과 신뢰 경계를 공유하게 된다. 프롬프트 인젝션, 도구 포이즈닝, 오버쉐어링, 잘못된 액션 같은 위험이 커진다. MCP를 다룰 때는 스코프, 권한, 서버 신뢰도, 출력 크기, 시크릿 처리까지 함께 설계해야 한다

Hooks는 실행 전후의 결정론적 강제 장치다. Claude Code 공식 Hooks 문서가 정의하는 이벤트는 PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubAgentStop, PreCompact, SessionStart 등이다

  • PreToolUse: 특정 경로 쓰기 금지
  • PostToolUse: Python 파일 수정 후 포매터 자동 실행
  • Stop: 테스트 없이 종료하려는 흐름 차단

Hooks는 셸 명령을 자동 실행하므로 충분히 테스트한 안전한 환경에서 설계해야 한다

의도도구
반복 절차를 재사용하고 싶다Skills
외부 시스템(API, DB, Jira 등)을 연결하고 싶다MCP
실행 전후에 규칙을 강제하고 싶다Hooks
Claude의 행동 범위를 차단하고 싶다Permissions
위 요소들을 묶어 팀에 배포하고 싶다Plugin

흔한 함정은 “MCP가 더 고급이니까 반복 작업도 MCP로 만들자”는 발상이다. 모든 반복 작업에 MCP를 붙이면 시스템이 불필요하게 무거워진다. 외부 도구 호출이 없는 절차는 Skills로 충분하다. 반대로 외부 시스템 접근이 필요한데 Skills만으로 해결하려 하면 한계가 생긴다. 강제 규칙을 Skills에 적어두면 Claude가 잊거나 무시할 수 있으므로 Hooks나 Permissions로 옮긴다

Tool Harness는 더 많은 도구를 붙이는 일이 아니라 각 도구의 메커니즘을 정확한 위치에 배치하는 일이다

Sub-agent & Worktree Harness – 병렬 처리보다 격리가 우선이다

여러 Claude 세션을 동시에 돌리면 처리량이 늘어난다. 동시에 격리 없이 병렬로 돌리면 같은 파일을 동시에 수정하고, 같은 테스트 데이터베이스를 공유하고, 같은 브랜치에서 충돌을 만든다. 컨텍스트가 서로 오염되고 결국 병합할 수 없는 변경 사항만 남는다. 병렬의 핵심은 “많이 돌리기”가 아니라 “잘 정리해서 격리하기”다

Claude 공식 서브 에이전트 문서는 서브 에이전트를 다음과 같이 정의한다. 특정 작업을 처리하는 전문화된 AI 보조이며, 메인 대화에서 분리된 별도 컨텍스트를 사용하고, 특정 도구 접근 권한만 가질 수 있다. 즉 서브 에이전트는 단순 보조 AI가 아니라 메인 컨텍스트를 보완하는 격리 경계다

부모 세션이 서브 에이전트에게 작업을 위임하면 서브 에이전트는 별도의 제한된 컨텍스트 윈도우에서 일하고, 부모 세션에는 전체 대화 기록이 아니라 요약 또는 최종 보고만 돌아온다. 인증 모듈을 탐색하면서 여러 파일을 읽고 grep을 돌리고 가설을 세우는 작업을 메인 세션에서 계속하면 컨텍스트가 금방 지저분해진다. 탐색용 서브 에이전트에게 “인증 흐름 구조만 조사하고 관련 파일과 위험 지점을 요약해 달라”고 맡기면 메인 세션은 깔끔한 요약만 받는다

Sub-agent Harness는 역할 분리에서 시작한다

Main Session
    ├─ Explorer Sub-agent     (read-only 도구만)
    ├─ Implement Session      (Edit/Write 권한)
    └─ Verify Sub-agent       (test runner, diff 도구)

모든 서브 에이전트에게 모든 도구를 주지 않는다. 조사 에이전트는 read-only 도구만, 테스트 에이전트는 테스트 실행만, 코드 리뷰 에이전트는 변경분 읽기와 위험 탐색만 가진다. 좋은 사람 팀이 그렇듯 한 에이전트가 모든 일을 동시에 하지 않는다

Worktree Harness는 파일 시스템 수준의 격리를 제공한다. git worktree는 하나의 저장소에서 여러 워킹 디렉터리를 만들고 서로 다른 브랜치를 동시에 체크아웃하게 해준다

Repository
    ├─ main worktree                     (stable baseline)
    ├─ worktrees/bugfix-auth/            (Claude 세션 A)
    ├─ worktrees/refactor-payment/       (Claude 세션 B)
    └─ worktrees/spike-cache/            (Claude 세션 C)

세션 A는 인증 버그 픽스 브랜치 worktree에서 테스트 실패를 고친다. 세션 B는 리팩터 방향을 실험한다. 세션 C는 캐시 도입을 시험한다. 메인 worktree는 오염되지 않고, 각 작업의 변경분과 테스트 결과를 따로 비교할 수 있다

“Claude 터미널을 여러 개 열면 된다”는 발상은 병렬이 아니라 혼란을 만든다. 같은 브랜치에서 여러 세션이 동시에 편집하면 변경이 섞이고, 어느 세션이 어느 변경을 만들었는지 추적이 어렵고, 테스트 결과가 어느 변경에 대한 것인지 불명확해진다. worktree를 쓰면 작업별로 브랜치, 디렉터리, 테스트 결과, 변경분이 분리된다. 병렬 실행이 아니라 병렬 격리가 만들어진다

서브 에이전트와 worktree는 서로 다른 차원을 격리한다

메커니즘격리 대상단독 사용 시 한계
Sub-agent컨텍스트파일 변경 충돌 가능
Worktree파일 시스템역할이 섞일 수 있음
둘을 함께 사용컨텍스트 + 파일 시스템안전한 병렬 처리

조사 에이전트는 read-only로 관련 파일을 찾고, 구현 세션은 별도 worktree에서 작은 patch를 만들고, 검증 에이전트는 변경분과 테스트를 검토한다. 이 구조가 하네스 엔지니어링이 말하는 안전한 병렬 처리다

Debugging Harness – 로그가 아니라 풀 수 있는 형태로 바꿔준다

디버깅에서 흔한 실수는 에러 로그를 통째로 붙여놓고 “왜 이렇지”라고 묻는 것이다. 짧은 문제는 그것만으로 풀린다. 실무에서는 로그가 길고 노이즈가 많고, 환경 차이가 있고, 재현이 불안정하고, 이전 시도한 해결책이 섞여 있다. 이 상태에서는 Claude가 그럴듯한 일반론을 말하거나, 스택 트레이스의 마지막 줄만 보고 잘못된 원인으로 가설을 정한다

Debugging Harness의 목적은 Claude에게 더 많은 로그를 주는 것이 아니라 풀 수 있는 문제 형태로 바꿔주는 것이다. 좋은 디버깅 하네스는 3가지를 분리한다

  • Symptom: 무엇이 언제 실패하는가 (관찰된 현상)
  • Evidence: 스택 트레이스, 실패 테스트 출력, Docker 로그, CI 로그, 환경 변수 차이
  • Verification Loop: 어떤 명령으로 재현하는지, 어떤 테스트가 실패하는지, 어떤 로그를 추가하면 가설을 확인할 수 있는지

가장 중요한 원칙은 “먼저 수정하지 말고 원인 후보를 좁힌다”이다

다음은 디버깅 프롬프트의 표준 골격이다

[문제]
회원가입 API에서 간헐적으로 500이 발생한다. 동일 입력을 반복해도
재현되지 않을 때가 있다.

[환경]
- 로컬에서는 재현되지 않음
- CI에서는 30% 확률로 재현
- Docker 컨테이너 안에서 실행
- Node.js 20, PostgreSQL 15

[증거]
- stack trace: <첨부>
- 실패 테스트: signup.spec.ts > "should create user"
- 환경 변수 매트릭스: <아래 표 참고>

[제약]
- DB 스키마 변경 금지
- API response shape 변경 금지
- .env 또는 시크릿 파일 읽기 금지
- 먼저 수정하지 말고 원인 후보를 좁힐 것

[지시]
1. 가능성 높은 원인 후보를 순서대로 제시
2. 각 후보를 확인할 최소 명령 또는 파일 확인 방법 제시
3. 수정 전에 봐야 할 관련 파일 목록 제시
4. regression 테스트가 필요한지 판단

환경 변수 문제는 디버깅 하네스에서 특히 중요하다. “로컬에서는 되는데 Docker에서는 안 된다”, “Docker는 되는데 CI에서는 안 된다”, “스테이징은 되는데 프로덕션에서만 깨진다” 같은 상황은 코드 문제가 아니라 설정 스코프 문제일 때가 많다

Claude에게 시크릿 값을 보여주지 않고도 문제를 풀게 하려면 환경 변수 점검 매트릭스를 만들어 주면 된다

VariableLocalDockerCIProd비고
JWT_SECRET존재컨테이너에 미전달존재존재추정 원인
DATABASE_URL존재존재존재존재정상
REDIS_HOSTlocalhostredisredisprod-redis정상
LOG_LEVELdebuginfoinfowarn정상

이 표는 값을 노출하지 않고도 “Docker compose에서 JWT_SECRET이 컨테이너에 전달되지 않는다”는 문제를 Claude가 짚어내게 만든다. 중요한 것은 값 자체가 아니라 범위와 가시성이다. 이것이 프라이버시 보존형 디버깅 하네스다

Debugging Harness의 마지막 단계는 반례 루프다. Claude가 원인을 확신할 때 다음 질문을 강제해야 한다

  • 이 가설이 틀릴 수 있는 조건은 무엇인가
  • 이 수정이 실패할 수 있는 입력은 무엇인가
  • 이 로그와 모순되는 다른 원인은 무엇인가

AI를 의심하기 위해서가 아니다. 디버깅을 더 논리적으로 만들기 위해서다. 실무 디버깅은 가설을 세우고, 증거로 줄이고, 원인을 좁히고, 반례를 검증하는 일이다. Debugging Harness는 이 과정을 Claude와 함께 도는 구조다

정리

한 줄로 요약하면, Claude Code를 잘 쓴다는 것은 모델을 잘 다룬다는 의미가 아니라 모델 주변에 6개의 하네스를 설계한다는 의미다. Context로 Claude가 보는 작업면을 만들고, Permission으로 행동 반경을 제한하고, Verification으로 결과를 검증하고, Tool로 확장 메커니즘을 정확한 지점에 배치하고, Sub-agent와 Worktree로 컨텍스트와 파일 시스템을 격리하고, Debugging으로 문제를 풀 수 있는 형태로 바꿔준다. 문제가 생겼을 때 모델을 의심하기 전에 어느 하네스가 약한지부터 묻는 습관이 Claude Code를 데모 도구에서 실무 도구로 바꾼다

출처 – 인프런 [Claude Code Harness Engineering 클로드코드 심화 CLI 하네스 엔지니어링 실무]