루프와 하네스 엔지니어링: 파일 7개, 5단계, 설정까지 전부

TMT

https://x.com/ArchiveExplorer/status/2071192832455430283

많은 개발자가 루프와 씨름합니다. 루프는 멀쩡합니다. 그 밑에 깔린 폴더가 제대로 갖춰지지 않았을 뿐입니다.

제대로 굴러가는 Claude Code 프로젝트의 .claude/ 폴더를 열어 보면, 실제 일을 하는 것은 대략 일곱 가지입니다. CLAUDE.md, settings.json, hooks/, agents/, skills/, .mcp.json, 그리고 MEMORY.md 같은 상태 파일입니다.

대부분은 이 중 파일 하나, 많아야 두 개쯤 열어 봤을 겁니다. 루프가 세 번째 반복에서 멈춰버리는 이유가 바로 여기에 있습니다.

이 글을 다 읽고 나면 각 파일이 무슨 일을 하는지, 그 위에서 도는 다섯 가지 루프 단계가 무엇인지, 첫 시도를 대부분 망치는 세 가지 실패 유형이 무엇인지, 그리고 오늘 밤 바로 추가해야 할 파일 하나가 무엇인지 알게 됩니다.

프레임워크도, 구독도 필요 없습니다. 정확한 경로와 정확한 내용이 담긴 안내 하나면 충분합니다.

하네스는 바닥입니다. 바닥부터 먼저 다지세요.

두 개의 층, 하나의 설정

하네스(harness)는 .claude/ 폴더입니다. 실행이 거듭돼도 바뀌지 않습니다.

루프는 그 안에서 도는 것입니다. 목표, 실행, 검증 단계, 메모리 기록, 그리고 계속할지 멈출지에 대한 결정으로 이루어집니다.

하네스가 주방이라면, 루프는 레시피입니다.

둘은 서로가 없으면 무너집니다. 레시피 없는 주방은 놀리는 공간이고, 주방 없는 레시피는 그림의 떡입니다.

대부분의 개발자는 이 전체를 한 덩어리("내 에이전트 설정")로 취급하다가, 실패가 서로 다른 층에서 일어난다는 사실을 놓칩니다.

토큰 폭증, 프롬프트 피로, 빠뜨린 권한. 이런 건 하네스 문제입니다. 수렴할 줄 모르는 루프, 엉터리를 통과시키는 검증, 궤도를 벗어나는 예약 실행. 이런 건 루프 문제입니다.

어느 층의 문제인지 짚어내면 진단이 바로잡힙니다. 진짜 버그는 빠진 권한인데 프롬프트만 계속 고쳐 쓰는 일이 없어집니다.

저는 루프를 먼저 만들어 보면 어떤 하네스 파일이 필요한지 알게 될 거라 생각했습니다. 순서는 그 반대였습니다.

하네스는 각 반복이 무엇을 해도 되는지를 정합니다. 권한은 루프가 디스크에 쓸 수 있는지를 정하고, 서브에이전트는 검증이 깨끗한 컨텍스트에서 도는지를 정합니다.

스킬은 루프가 전문화할 수 있는지를 정하고, 훅은 애초에 루프가 원하는 트리거에서 발동되는지를 정합니다.

이런 결정들이 못 박혀 있지 않으면 루프는 추측합니다. 추측하기 시작하면 지어냅니다. 없는 파일, 없는 명령어, 아무것도 검증하지 않으면서 통과하는 테스트.

하네스가 이 추측을 막아줍니다. 그래서 순서는 언제나 하네스가 먼저, 루프가 그다음입니다.

하네스, 파일 하나씩 뜯어보기

CLAUDE.md

Claude Code가 실행될 때마다 가장 먼저 읽는 파일입니다. 이 내용은 세션 전체에 걸쳐 상시 컨텍스트가 됩니다.

여기에 프로젝트의 윤곽을 담으세요. 디렉터리 구조, 언어와 프레임워크, 실제로 동작하는 명령어, 에이전트가 지켜야 할 컨벤션, 그리고 절대 하면 안 되는 일의 명시적인 목록.

docs 어딘가에 묻어두지 말고 저장소 루트에 두세요. 동작하는 최소한의 형태는 이렇습니다:

# Project: my-app
Stack: Next.js 14, TypeScript, Postgres, Tailwind.
Layout: `app/` (routes), `lib/` (helpers), `db/migrations/`.

## Commands
- `pnpm dev` - local
- `pnpm test` - vitest
- `pnpm db:migrate` - apply migrations

## Never
- Edit `db/migrations/*` after merge.
- Add deps without justification in the PR body.
- Bypass `lib/auth/` to access user data.

함정은 비대해지는 것입니다. Less Context, Better Agents (arXiv 2606.10209) 논문의 측정에 따르면, 상시 컨텍스트가 너무 크다는 이유만으로 작업 완료율이 **91.6%**에서 **71%**로 떨어졌습니다.

300줄 아래로 유지하고, 매주 쳐내세요. 문단 하나를 더할 때마다 이후의 모든 턴이 그 비용을 치릅니다.

기준으로 삼을 만한 참고 자료는 centminmod/my-claude-code-setup입니다. 실제로 동작하는 CLAUDE.md 형태 세 가지를 나란히 담고 있습니다.

Image

settings.json

도구 허용 목록, 환경 변수, 훅 등록이 들어가는 파일입니다.

일상 작업에서 중요한 위치는 두 곳입니다. 저장소 범위 규칙은 저장소 루트의 .claude/settings.json에, 개인 기본값은 ~/.claude/settings.json에 둡니다.

적용 우선순위는 managed > project > local > user 순이므로, 프로젝트 설정이 항상 개인 설정보다 우선합니다.

한나절 만에 효과를 보는 첫 조치는 읽기 전용 Bash와 MCP 호출을 담은 allow 배열입니다:

{
  "permissions": {
    "allow": [
      "Bash(ls:*)",
      "Bash(git status:*)",
      "Bash(git diff:*)",
      "Bash(cat:*)",
      "Read(*)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(git push --force:*)"
    ]
  }
}

이제 에이전트가 ls, git status, cat 하나하나에 권한 승인을 기다리며 멈추는 일이 없어집니다. 파괴적인 작업은 여전히 승인을 거칩니다.

전체 키 목록은 Claude Code 문서 - Settings를 참고하세요. 비밀 값은 .claude/settings.local.json에 넣고 gitignore 처리하세요.

hooks

도구 이벤트에 맞춰 실행되는 결정적(deterministic) 스크립트입니다. 도구 실행 전에는 PreToolUse, 실행 후에는 PostToolUse, 에이전트가 턴을 마치면 Stop이 발동됩니다.

settings.json 안에 matcher 패턴과 셸 명령으로 등록합니다. 첫 훅의 정석은 Edit|Write에 매칭해서 파일을 prettier에 통과시키는 PostToolUse입니다.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {"type": "command", "command": "npx prettier --write \"$CLAUDE_FILE_PATH\""}
        ]
      }
    ]
  }
}

이제 모든 편집이 예측 가능한 상태로 끝납니다. 이것이 정책의 기본선입니다.

훅이 없으면 매 실행이 복불복입니다. 훅은 성공하면 조용히, 실패할 때만 요란하게 만드세요. 참고: Claude Code 문서 - Hooks.

subagents

.claude/agents/ 아래에 YAML frontmatter를 단 마크다운 파일로 둡니다. 메인 에이전트가 Task 도구로 호출하며, 새 컨텍스트 창에서 실행됩니다.

최소한의 검증자(verifier) 서브에이전트:

---
name: verifier
description: Reviews a diff against the goal spec. Invoke after every code change.
model: haiku
tools: [Read, Grep, Bash]
---

You are a verifier. Read the goal spec in `PROMPT.md`. Read the diff.
Return a JSON verdict: {passes: bool, failures: [{line, reason}]}.
Do not propose fixes. Do not run code. Do not be polite.

코드를 만든 컨텍스트 안에 함께 있는 리뷰어는 언제나 자기 자신에게 동의합니다. 리뷰를 새 컨텍스트로 떼어내는 것만으로 가장 크게 터지는 실패 유형 하나가 사라집니다.

참고: 바로 쓸 수 있는 예시 194개는 wshobson/agents(37K stars)에 있습니다. 느슨해진 테스트, 삼켜진 에러, 무늬만 이름 바꾸기 같은 11가지 꼼수를 이름 붙여 잡아내는 적대적 검증자가 필요하면 moonrunnerkc/swarm-orchestrator를 받으세요.

Image

skills

.claude/skills/ 아래에, YAML frontmatter를 단 SKILL.md를 담은 폴더로 둡니다.

점진적으로 로드됩니다. 세션 시작 시에는 name과 description만 컨텍스트에 들어가고, 본문 전체는 에이전트가 트리거에 맞는다고 판단할 때만 로드됩니다.

---
name: db-migration-writer
description: Writes Postgres migration files for this repo. Use when the user
  asks to add/alter a table, column, index, or constraint.
when_to_use: schema change requested, new feature requires a new column,
  index missing on a hot query path
---

# Steps
1. Read `db/schema.sql` to confirm current state.
2. Write the migration to `db/migrations/NNN_<verb>_<noun>.sql`.
3. Include both up and down. Test with `pnpm db:migrate --dry`.
4. Never touch existing migration files.

이 원칙 덕분에 스킬 50개를 쌓아 두어도 매 프롬프트마다 50개분의 토큰을 내지 않습니다.

정석 패턴: anthropics/skills(155K stars). 미리 만들어진 것 중 가장 방대한 키트: affaan-m/ECC(222K stars).

Image

같은 작업을 세 번째 마주쳤을 때 만든 스킬 3개가, 튜토리얼만 보고 미리 만들어 둔 스킬 50개보다 낫습니다.

MCP

서버는 저장소 루트의 .mcp.json에 선언합니다. Model Context Protocol은 루프가 살아 있는 외부 도구를 호출할 수 있게 해주는 규격입니다.

규칙은 세 가지입니다. 지금 하는 일에 쓰는 서버만 설치할 것, 자격 증명이 필요한 도구는 공식 서버를 쓸 것, "혹시 몰라서" 다섯 개씩 깔지 말 것.

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {"GITHUB_TOKEN": "${GITHUB_TOKEN}"}
    },
    "context7": {
      "command": "npx",
      "args": ["-y", "@upstash/context7-mcp"]
    }
  }
}

Anthropic이 관리하는 서버 모음: modelcontextprotocol/servers(87K stars). 코드 호스팅 연동: github/github-mcp-server(31K stars).

라이브러리 문서를 실시간으로 가져와 낡은 API 문제를 없애주는 upstash/context7(58K stars). 탐색용 인덱스: punkpeye/awesome-mcp-servers(89K stars).

가장 흔한 첫 실수는, 모든 호출을 기록하는 훅도 없이 쓰기 권한이 있는 서버부터 켜는 것입니다.

상태와 메모리

일곱 번째 조각입니다. 대부분은 세 번째 프로젝트가 산으로 갈 때까지 이걸 건너뜁니다.

형태는 이렇습니다. 정해진 경로에 MEMORY.md 인덱스 파일을 두고, 프로젝트의 기준 문서를 담는 vault 디렉터리를 따로 둡니다.

~/.claude/memory/
  MEMORY.md            # index, links to topic files below
  user-prefs.md        # preferences, terse-vs-verbose, voice
  project-decisions.md # "we picked Postgres over Mongo on 2026-03-12, here is why"
  feedback-recent.md   # corrections you keep applying

~/vault/               # project canon (does not change session to session)
  architecture.md
  api-spec.md
  post-mortems/

메모리에는 세션마다 달라지는 것을 담고, vault에는 달라지지 않는 것을 담습니다.

프로덕션급 세션 압축(핵심 사실을 잃지 않으면서 20만 토큰짜리 기록을 4천 토큰 요약으로)이 필요하면 thedotmack/claude-mem(84K stars).

이게 왜 중요한지에 대한 이론은 Anthropic의 컨텍스트 엔지니어링 글에 있습니다. 이 실패 유형에는 컨텍스트 부패(context rot)라는 이름이 붙어 있습니다.

가장 흔한 첫 실수는 메모리를 쌓기만 하고 지우지 않는 것입니다. 세션마다 쳐내세요. 그러지 않으면 메모리 자체가 부패가 됩니다.

하네스 위에서 도는 루프

1. 목표 스펙(Goal spec)

"완료"가 어떤 모습인지 정의하는 외부 계약입니다. 에이전트의 머릿속이 아니라 디스크에 둡니다. 루프는 반복할 때마다 이 파일을 다시 읽습니다.

이름은 PROMPT.mdAGENTS.mdAGENT_SPEC.md든 상관없습니다. 중요한 건 매번 다시 읽는다는 점입니다.

# Goal
Migrate `users.password` from bcrypt to argon2id across the codebase.

# Done when
- All new password writes use argon2id (`lib/auth/hash.ts`).
- Existing bcrypt hashes are rehashed on next successful login.
- Test suite green: `pnpm test auth`.

# Never touch
- `db/migrations/*` already merged.
- Anything under `legacy/`.
- The session cookie format.

# Stop if
- More than 3 files outside `lib/auth/` need edits.
- A test that already passes starts failing.

이 파일이 없으면 에이전트는 세 번쯤 반복하고 나서 길을 잃습니다. 가장 작은 참고 예시는 ghuntley/how-to-ralph-wiggum(1.7K stars)입니다. PROMPT.md에, 루프가 제자리에서 갱신해 나가는 IMPLEMENTATION_PLAN.md 상태 파일을 더한 구성입니다.

스펙이 없으면 실패가 진척처럼 보입니다. 코드는 작성되고 테스트는 통과하는데, 정작 풀린 문제는 내 문제가 아닙니다.

2. 계획 → 실행 → 검증

굴러가는 최소한의 루프는 세 단계입니다. 에이전트가 목표 스펙에 맞춰 계획하고, 실행하고, 별도의 검증 단계가 결과를 확인한 뒤에야 다음 반복이 시작될 수 있습니다.

반복마다 컨텍스트를 새로 여는 것이 Ralph 패턴입니다. 상태는 스펙 파일과 누적 로그의 형태로 디스크에 남습니다.

#!/usr/bin/env bash
# minimal loop runner: fresh context each turn, state on disk
set -euo pipefail

while true; do
  # plan + act in fresh context
  claude -p "Read PROMPT.md, IMPLEMENTATION_PLAN.md. Do the next step. Commit on green."

  # verify in fresh context (different subagent)
  if claude -p "/verify"; then
    echo "iter ok"
  else
    echo "verify failed, will retry"
  fi

  # exit when spec says done
  grep -q "^STATUS: done$" IMPLEMENTATION_PLAN.md && break
  sleep 5
done

정석 패턴과 CLI 스타터 모음: cobusgreyling/loop-engineering(3K stars).

verifyCompletion을 갖춘 프로덕션 TypeScript 참고 구현: vercel-labs/ralph-loop-agent(805 stars).

계획-작업-리뷰-릴리스 사이클 전체를 설치해서 쓸 수 있는 구성: Chachamaru127/claude-code-harness(2.9K stars).

검증 단계를 빼면 자신만만한 엉터리가 눈덩이처럼 불어납니다. 잘못된 출력 하나하나가 다음 반복의 입력이 되기 때문입니다.

3. 서브에이전트 팬아웃(fan-out)

하나의 목표가 독립적인 하위 작업 여러 개로 갈라질 때(기사 10개 분석, 파일 5개 수정, 소스 8곳 검색), 루프는 서브에이전트를 병렬로 띄웁니다. 종합은 오케스트레이터가 맡습니다.

비대해진 컨텍스트 하나로는 못 하는 일을, 작은 컨텍스트 열 개는 해냅니다.

# claude-agent-sdk-python style fan-out
from claude_agent_sdk import Agent, run_parallel

orchestrator = Agent.load(".claude/agents/orchestrator.md")
workers = [Agent.load(".claude/agents/researcher.md") for _ in range(8)]

results = run_parallel([
    w.run(source=src) for w, src in zip(workers, sources)
])

synthesis = orchestrator.run(inputs=results)

Anthropic의 멀티 에이전트 리서치 글은 내부 평가에서 단일 에이전트 기준선 대비 **+90.2%**를 기록했습니다.

공식 SDK: anthropics/claude-agent-sdk-python(7.4K stars). 공개된 것 중 가장 무거운 팬아웃 키트(에이전트 유형 60개 이상, MCP 도구 314개): ruvnet/ruflo(61K stars).

팬아웃을 건너뛰면 오케스트레이터가 허우적댑니다. 작업 열 개 분량의 원본 자료를 컨텍스트 하나에 몰아넣은 모양이야말로 컨텍스트 부패를 부르는 바로 그 형태입니다.

4. 스케줄러와 상태 저장

자리를 비운 사이 루프를 깨워주는 장치입니다. cron, launchctl, systemd, 큐 러너 같은 것들이죠.

스케줄러는 일부러 에이전트보다 멍청하게 둡니다. 스케줄러가 생각을 하려 들면(상태를 보고 분기하거나, 건너뛸지 판단하거나) 며칠씩 티도 안 내고 조용히 실패합니다.

# crontab: run the loop every 30 min, log to disk
*/30 * * * * cd ~/my-loop && ./run.sh >> logs/$(date +\%Y-\%m-\%d).log 2>&1

macOS라면 launchd plist로도 가능합니다:

<key>StartCalendarInterval</key>
<dict>
  <key>Minute</key><integer>0</integer>
</dict>
<key>WorkingDirectory</key><string>/Users/me/my-loop</string>
<key>ProgramArguments</key>
<array><string>/bin/bash</string><string>run.sh</string></array>

나머지 절반은 상태 저장입니다. 반복할 때마다 무엇을 했고, 무엇을 시도했고, 다음이 무엇인지 기록으로 남겨야 합니다. 그러지 않으면 스케줄러가 깨워도 에이전트는 목표를 잊은 상태입니다.

즉석 세션을 예약 실행으로 승격시키는 패턴: Kanevry/session-orchestrator.

5. 실패 유형

첫 시도는 거의 전부 이 세 가지 실패 유형에서 무너집니다.

(a) 자신만만한 엉터리(confident garbage). 검증 단계가 없거나 허술합니다. 잘못된 출력이 통과하고, 반복을 거치며 불어납니다.

(b) 컨텍스트 부패(context rot). 하나의 긴 컨텍스트에서 모델이 임계점을 넘어 무뎌지는 현상입니다(Anthropic이 붙인 이름). 기록이 20만 토큰쯤 쌓이면 정확도가 무너집니다.

(c) 랠프 위검 루프(Ralph Wiggum loop). 디스크의 상태 파일에 진행 상황이 담기지 않아 같은 반복이 되풀이됩니다. 에이전트가 이미 끝낸 단계를 또 계획합니다.

Less Context, Better Agents 논문(arXiv 2606.10209)의 측정에서, 전체 기록을 끌고 가는 방식은 작업 완료율 71%, 쳐내고 요약하는 방식은 **91.6%**였습니다. 토큰은 훨씬 적게 쓰면서요.

Image
before: single-context loop, 1.48M tokens, 71% completion, three hidden hallucinations per run
after:  prune-and-summarize loop with verifier subagent, 553K tokens, 91.6% completion, every figure traced

moonrunnerkc/swarm-orchestrator는 에이전트가 "다 했다"고 꾸며낼 때 쓰는 11가지 꼼수를 정리해 놓았습니다. 느슨해진 테스트, 삼켜진 에러, 무늬만 이름 바꾸기, 스텁 반환, 주석 지워 놓고 고쳤다고 하기.

이 이름들을 외워 두세요. 여러분의 로그에서 그대로 다시 만나게 될 겁니다. 완결된 최소 구성은 하네스 파일 일곱 개를 하나의 동작하는 루프로 엮습니다. 프로젝트 디렉터리 모양은 이렇습니다:

my-loop/
├── .claude/
│   ├── CLAUDE.md            # 모든 세션에 깔리는 상시 컨텍스트
│   ├── settings.json        # allow 배열 + PostToolUse prettier 훅
│   ├── agents/
│   │   └── verifier.md      # Haiku, 새 컨텍스트에서 diff 리뷰
│   └── skills/
│       └── db-migration-writer/
│           └── SKILL.md     # 세 번 이상 반복한 작업을 스킬로 만든 것
├── .mcp.json                # github MCP, context7 MCP
├── PROMPT.md                # 목표 스펙 (루프가 반복마다 읽음)
├── IMPLEMENTATION_PLAN.md   # 상태 파일 (루프가 반복마다 갱신)
├── MEMORY.md                # 세션이 바뀌어도 유지되는 선호 기록
├── run.sh                   # 루프 실행기 (계획 -> 실행 -> 검증)
└── logs/                    # 실행 기록, cron 틱마다 파일 하나

연결은 한 방향입니다. 하네스가 규칙을 정하고, 루프가 그 안에서 돌고, 상태 파일이 N번째 반복과 N+1번째 반복을 이어줍니다.

한 번의 반복은 하네스 파일 일곱 개와 루프 조각 다섯 개를 이 순서로 지나갑니다. cronrun.sh를 실행하고, run.shclaude -p를 호출합니다. Claude Code는 CLAUDE.mdsettings.json을 읽고(하네스 1, 2), 모든 편집에 PostToolUse 훅을 적용하고(하네스 3), PROMPT.mdIMPLEMENTATION_PLAN.md를 읽고(루프 1단계), 계획하고 실행하고(루프 2단계), 검증자 서브에이전트를 새 컨텍스트로 내보내고(하네스 4 + 루프 2단계의 검증), 결과를 IMPLEMENTATION_PLAN.md에 다시 쓰고(루프 3단계), 새로 배운 선호가 있으면 MEMORY.md를 갱신하고(하네스 7), 종료합니다. cron은 다음 틱을 기다립니다(루프 4단계).

하네스 파일 일곱 개 중 하나라도 빠지면, 그에 대응하는 루프 단계가 망가집니다. CLAUDE.md가 없으면 플래너가 반복할 때마다 프로젝트 구조를 처음부터 다시 알아냅니다. 검증자 서브에이전트가 없으면 검증이 메인 컨텍스트 안에서 이루어지고 항상 통과해 버립니다. MEMORY.md가 없으면 같은 지적을 매주 화요일마다 다시 하게 됩니다.

하네스 파일 일곱 개는 한 번만 만들면 됩니다. 루프는 그 위에서 계속 돌아갑니다.

오늘 밤 할 일

여러분의 .claude/ 폴더를 열고 실행해 보세요:

ls -la .claude/

파일 개수를 세어 보세요.

아무것도 없거나 settings.json뿐이라면 CLAUDE.md부터 시작하세요. 300줄 아래로 유지하고, centminmod/my-claude-code-setup에서 형태를 베껴 오세요.

CLAUDE.mdsettings.json은 있는데 agents/가 없다면, 다음은 검증자 서브에이전트입니다. 리뷰를 메인 컨텍스트 밖으로 꺼내세요. 형태는 wshobson/agents를 참고하세요.

agents/는 있는데 skills/가 없다면, 자주 하는 작업 하나를 스킬로 승격시키세요. 이번 주에만 세 번 복사해 붙여 넣은 그 프롬프트 말입니다. 첫 스킬을 쓰기 전에 anthropics/skills에서 SKILL.md 세 개를 먼저 읽어 보세요.

하네스 파일 일곱 개가 다 있는데 도는 루프가 없다면, 반복되는 일 하나를 골라 목표 스펙을 쓰고 그 위에 계획-실행-검증 루프를 올리세요. 설치해서 바로 시작하기 가장 좋은 지점: Chachamaru127/claude-code-harness.

하나를 골랐다면 딱 한 가지만 하세요. 해당 저장소를 새 탭에서 열고 클론하는 것입니다.

하네스는 바닥입니다. 바닥이 없으면 모든 루프는 구멍 난 바닥 위를 달리게 됩니다.

Edit this page