Claude Code 하네스를 직접 엔지니어링한 이야기 (1) — 무엇을 넣었나
AI 코딩 도구를 쓰다 보면, 결과가 마음에 안 들 때 제가 직접 바꿀 수 있는 것은 모델이 아니라 모델 주변이라는 걸 알게 됩니다. 그 주변을 보통 하네스(harness)라고 부릅니다. 이 시리즈는 제가 개인 프로젝트로 꾸린 하네스 이야기입니다 — 회사에서 정해 준 설정도, 팀이 공유하는 표준도 아니고, 순전히 제 작업 습관을 고쳐 보려고 제 홈 디렉터리
~/.claude/아래에 혼자서 하나씩 쌓아 올린 개인 환경입니다. 1편은 자랑이 아니라, 흩어져 있던 그 부품들을 한 번 정리해 둔다는 마음으로 "무엇을 넣었나" 만 다룹니다. 3부작 중 1편입니다.
1. "하네스" 라는 말부터
처음 AI 코딩 도구를 쓸 때 저는 결과가 마음에 안 들면 막연히 "모델이 더 똑똑해지면 나아지겠지" 라고 생각했습니다. 그런데 모델은 제가 못 바꿉니다. 제가 바꿀 수 있는 건 모델한테 무엇을 쥐여 주느냐 였습니다.
코딩 에이전트는 사실 모델 하나만으로 돌아가지 않습니다. 모델 + 그 모델한테 매번 같이 들어가는 것들의 묶음으로 돌아갑니다.
- 매 세션에 항상 주입되는 행동 지침 (시스템 프롬프트, 프로젝트 메모)
- 모델이 쓸 수 있는 도구와 그 도구의 권한 범위
- 특정 사건이 일어났을 때 끼어드는 훅(hook)
- 반복 절차를 캡슐화한 스킬
- 무거운 작업을 떼어 주는 서브에이전트
이 묶음 전체를 하네스라고 부릅니다. 하네스 엔지니어링은 그래서 "모델을 못 바꾸니까, 모델이 일하는 환경 쪽을 바꿔서 결과를 바꾸는 일" 입니다. 말 그대로 마구(馬具) 입니다. 말은 그대로인데, 어떤 굴레와 고삐를 채우느냐로 같은 말이 다르게 달립니다.
이 시리즈는 제가 그 굴레를 어떻게 채워 봤는지에 대한 회고입니다. 다시 한 번 짚어 두면, 이건 누가 시켜서 만든 게 아니라 순전히 제 개인 프로젝트입니다. 회사 업무에 쓰는 설정이 아니라 제 개인 작업 습관을 고치려고 혼자 꾸린 것이라, 정답이 아니라 한 사람의 사례로 읽어 주시면 됩니다. 1편은 부품을 펼쳐 보기만 하고, 왜 그 모양이 됐는지(2편)와 그래서 작업이 어떻게 흘러가는지(3편)는 다음 글로 넘깁니다.
2. 한눈에 본 ~/.claude/
제 하네스는 전부 홈 디렉터리의 ~/.claude/ 한 폴더 아래에 모여 있습니다. 핵심만 추리면 이런 모양입니다.
~/.claude/
├── CLAUDE.md # 모든 세션에 주입되는 행동 원칙
├── settings.json # 권한 · 훅 등록 · 실행 옵션
├── skills/ # 자작 스킬 (작업 절차를 캡슐화)
├── hooks/ # 이벤트 훅 셸 스크립트
├── agents/ # 서브에이전트 정의
├── commands/ # 슬래시 커맨드
└── statusline-command.sh # 터미널 하단 상태줄여기서 중요한 건 폴더 구조 자체가 아니라, 이게 코드처럼 버전이 매겨지고 점점 자라는 자산이라는 점입니다. 처음엔 빈 폴더였고, 작업하다 같은 실수를 두 번째로 마주칠 때마다 한 조각씩 늘었습니다. 아래 항목들을 차례로 보겠습니다.
3. CLAUDE.md — 항상 켜져 있는 행동 원칙
CLAUDE.md 는 모든 세션 맨 앞에 항상 주입되는 문서입니다. 뿌리는 LLM이 흔히 하는 실수를 줄이는 행동 원칙 네 가지입니다. (지금은 그 위에 단순성 사다리·승인 게이트·커밋 흐름·작업 생애주기 같은 운영 규칙이 더 얹혀 있지만, 시작은 이 네 줄이었습니다.)
- Think Before — 추측하지 말 것. 해석이 갈리면 고르지 말고 다 꺼내 놓을 것. 모호하면 멈추고 물어볼 것.
- Simplicity — 시킨 것만. 단발성 코드에 추상화 금지. 200줄을 짰는데 50줄로 되면 다시 쓸 것.
- Surgical — 건드려야 하는 것만 건드릴 것. 주변 코드를 "개선" 하지 말 것. 바뀐 줄은 전부 요청과 직접 연결돼야 함.
- Goal-Driven — "동작하게 해라" 같은 약한 기준 금지. "잘못된 입력에 대한 테스트를 쓰고 통과시켜라" 처럼 검증 가능한 목표로 바꿀 것.
이 네 줄이 왜 하필 이 모양인지는 2편에서 따로 다룹니다. 1편에서는 "세션이 시작될 때마다 이 헌법이 먼저 깔린다" 는 사실만 기억하면 됩니다. 나머지 부품들은 전부 이 헌법을 절차로 강제하는 장치들입니다.
4. settings.json — 권한과 훅의 등록부
settings.json 은 하네스의 배선도입니다. 저는 여기에 세 가지를 적어 뒀습니다.
(1) 권한 — deny / ask / allow 세 단계
deny : 되돌리기 어려운 명령은 아예 막는다
git commit / git reset --hard / git push --force /
rm -rf / sudo / DB 콘솔 직접 접속 …
ask : 민감하지만 필요한 건 매번 물어본다
설정 파일(application.yml 류) 읽기 …
allow : 안전하고 반복적인 건 묻지 않고 통과
자주 쓰는 자작 스킬, 읽기 전용 조회 명령 …핵심은 비가역적인 작업과 가역적인 작업을 갈라 둔 것입니다. 커밋·force-push·rm -rf 처럼 한 번 하면 되돌리기 힘든 건 deny, 그냥 파일을 읽는 정도는 통과. 이 경계를 어디에 그었는지가 2편의 큰 주제 중 하나입니다.
(2) 훅 등록 — 어떤 사건에 어떤 스크립트를 끼울지 (아래 6절).
(3) 실행 옵션 — 추론 강도(effort) 같은 값. 저는 추론 강도를 가장 높은 단계로 두고 씁니다. 느려도 한 번에 제대로 가는 쪽을 택했습니다.
5. 자작 스킬 — 절차를 캡슐화한 것
스킬은 "이런 상황에서는 이런 절차로 일해라" 를 파일 하나로 묶어 둔 것입니다. 모델이 상황을 만나면 해당 스킬을 펼쳐서 그 절차대로 움직입니다. 제가 직접 만든 스킬 중, 작업 환경과 무관하게 재현 가능한 범용 스킬은 다음 다섯 개입니다.
| 스킬 | 한 줄 책임 |
|---|---|
planning-before-work | 비자명한 작업 전, 구체 예시가 박힌 플랜을 강제로 먼저 쓰게 한다 |
codex-review-pingpong | 플랜·코드를 외부 모델과 교차검토(핑퐁)시키는 게이트 |
verifying-test-coverage | 구현이 끝난 뒤 git add 직전, 테스트 누락을 4항목으로 자기검수 |
writing-handoff-doc | 세션이 끝날 때 다음 세션이 이어받을 핸드오프 문서를 남긴다 |
creating-custom-skill | 스킬을 만드는 스킬 — 새 스킬이 기존 것들과 같은 구조를 갖게 한다 |
다섯 개를 따로따로 본 게 아니라, 하나의 작업 파이프라인을 다섯 토막으로 자른 것에 가깝습니다. 작업 전(planning) → 게이트(pingpong) → 작업 후(verifying) → 세션 종료(handoff) 로 이어지고, 마지막 creating-custom-skill 은 이 다섯 개를 같은 톤으로 유지하는 메타 스킬입니다. 이 흐름을 한 사이클 따라가 보는 것이 3편의 내용입니다.
이 다섯 외에 다파일 작업을 병렬로 처리하는 워크플로(fanout)가 더 있습니다. 이 시리즈에서는 누가 따라 해도 재현되는 범용 부분만 다룹니다.
6. 훅 — 내가 깜빡해도 도구가 대신 챙기는 자동화
훅은 특정 사건이 일어났을 때 자동으로 실행되는 작은 셸 스크립트입니다. 스킬이 "모델이 알아서 따르는 절차" 라면, 훅은 "모델 의지와 무관하게 무조건 실행되는 장치" 입니다. 제가 깜빡하든 모델이 깜빡하든 상관없이 돕니다. 제가 걸어 둔 것들입니다.
PreToolUse(Bash) → 보호 브랜치(main/master 등)로의 force-push 차단
Stop → 작업이 멈추면 데스크톱 알림 (변경 파일 수 포함)지금 상시로 걸어 둔 강제 훅은 이 둘입니다. 가장 자주 덕을 보는 건 force-push 차단입니다. (핸드오프도 한때는 SessionEnd·PreCompact 훅으로 자동 저장했지만, 지금은 5절의 writing-handoff-doc 스킬로 필요할 때 남기는 쪽으로 옮겼습니다 — 훅에서 스킬로 뺀 이유는 2편에서 다룹니다.)
force-push 차단 훅도 비슷한 성격입니다. settings.json 의 deny 가 한 겹 막고 있지만, refspec 없이 git push -f 만 쳤을 때처럼 문자열 매칭이 놓치는 구멍을 훅이 한 겹 더 막습니다. 같은 사고를 두 겹으로 막아 두는 것 — 1편에서는 이런 게 있다는 것만 봐 두고, 왜 두 겹인지는 2편에서 다룹니다.
그리고 이 훅들에는 공통으로 스코프 게이트가 붙어 있습니다. 지정해 둔 작업 디렉터리 안에서 세션이 열렸을 때만 훅이 동작하고, 그 바깥(예: 지금 이 블로그 글을 쓰는 폴더)에서는 조용히 빠집니다. 자동화가 엉뚱한 곳에서 튀지 않게 하는 안전장치입니다.
7. 에이전트와 커맨드
서브에이전트는 메인 대화의 컨텍스트를 더럽히지 않고 무거운 일을 떼어 보내는 일꾼입니다. 저는 광역 코드 탐색, 외부 모델 핑퐁 실행, 아키텍처 리뷰 같은 일을 서브에이전트에 위임합니다. 메인 세션은 결론만 돌려받으므로 컨텍스트가 깨끗하게 유지됩니다.
손으로 부르고 싶은 절차는 전용 슬래시 커맨드 대신 스킬 호출로 처리합니다. 세션을 정리할 때 writing-handoff-doc 스킬을 불러 다음 세션이 이어받을 문서를 한 장 남기는 식입니다. (예전엔 /handoff 같은 전용 커맨드를 뒀는데, 같은 일을 스킬로 합치면서 커맨드 폴더는 비웠습니다.)
8. 다음 편 예고
여기까지가 부품 목록입니다. 정리하면 제 하네스는 이렇게 구성돼 있습니다.
CLAUDE.md → 행동 원칙 4가지 + 그 위에 얹힌 운영 규칙(단순성 사다리·승인 게이트·커밋 흐름·작업 생애주기)
settings.json → 권한(deny/ask/allow) + 훅 등록 + 실행 옵션
skills/ → 작업 파이프라인(계획→핑퐁→검수→핸드오프) + 병렬 워크플로
hooks/ → 사건 기반 강제 자동화 (force-push 차단·작업 종료 알림)
agents/ → 무거운 일을 떼어 보내는 서브에이전트그런데 부품을 나열하는 것만으로는 절반밖에 안 됩니다. 진짜 이야기는 "왜 하필 이 모양인가" 에 있습니다.
- 2편 — 무슨 고민을 했나: 행동 원칙이 왜 네 줄인지, 스킬을 왜 하나로 안 두고 쪼갰는지, 게이트를 몇 개 둘지, 무엇을 자동화하고 무엇을 승인받게 둘지.
- 3편 — 어떤 절차로 업무를 시작하나: 실제 작업 한 건이 플랜부터 커밋까지 이 부품들을 어떤 순서로 통과하는지.
마무리
1편을 쓰면서 정리된 한 줄은 이것이었습니다.
모델은 내가 못 바꾸지만, 모델이 일하는 환경은 내가 코드처럼 쌓을 수 있다.
처음엔 ~/.claude/ 가 빈 폴더였습니다. 지금의 모양은 한 번에 설계한 게 아니라, 같은 실수를 두 번째로 마주칠 때마다 한 조각씩 붙인 결과물입니다. 그래서 이 글도 완성된 설계도가 아니라 자라는 중인 자산의 현재 단면에 가깝습니다.
다음 편에서는 이 단면 하나하나가 왜 그렇게 생겼는지, 제가 어떤 트레이드오프 앞에서 망설였는지를 적어 보겠습니다.