Claude Code 하네스로 개인 프로젝트 운영하기
DevOps

Claude Code 하네스로 개인 프로젝트 운영하기

2026-07-271

AI 코딩 도구를 오래 쓰다 보면 같은 말을 반복하게 됩니다. "이 프로젝트는 스펙을 먼저 고쳐야 해", "커밋 메시지는 이렇게 써", "이 컨벤션을 따라". 매 세션 같은 설명을 다시 하는 비용이 쌓입니다.

그래서 프로젝트마다 하네스(harness) 를 만들어 운영하고 있습니다. 에이전트 정의·스킬·훅으로 작업 방식 자체를 저장소에 박아두는 방식입니다. 이 글에서는 실제로 굴리고 있는 하네스의 구조와, 운영하며 얻은 규칙들을 정리합니다.

하네스 3층 구조

하네스는 세 층으로 나뉩니다

Claude Code 기준으로 설정은 크게 세 가지 위치에 들어갑니다. 이 세 층의 역할을 구분하는 것이 하네스 설계의 전부라고 해도 과하지 않습니다.

파일특성담아야 할 것
상시 컨텍스트CLAUDE.md매 세션 항상 로드프로젝트 정체성, 절대 규칙, 트리거
역할 정의.claude/agents/*.md서브에이전트 실행 시 로드담당 범위, 도구 권한, 금지 사항
작업 지침.claude/skills/*/SKILL.md필요할 때만 로드절차, 체크리스트, 도메인 지식

핵심은 상시 로드되는 것과 그렇지 않은 것의 분리입니다. CLAUDE.md는 모든 세션의 토큰을 먹습니다. 반대로 스킬은 관련 작업일 때만 읽히므로 길어도 됩니다.

실제로 이 원칙을 어겨서 한 번 정리했습니다. 프로젝트 CLAUDE.md에 기술 스택 나열, 에이전트 로스터, 하네스 변경 이력 표가 전부 들어가 4,606자까지 불었습니다. 코드를 보면 알 수 있는 내용과 스킬에 이미 있는 내용을 걷어내 2,164자로 줄였습니다(53% 감소). 변경 이력은 harness-changelog.md로 분리해 하네스를 고칠 때만 읽도록 했습니다.

프로젝트마다 팀 구성이 다릅니다

에이전트는 "역할"입니다. 프로젝트 성격에 따라 필요한 역할이 다르므로 팀도 달라집니다.

풀스택 웹 프로젝트(Spring + Next.js + OpenAPI)는 8명입니다.

spec-architect          OpenAPI 3.0 스펙 설계
spring-backend-engineer controller/service/repository/Flyway
nextjs-frontend-engineer App Router 페이지·훅·스토어
ui-component-designer   Tailwind/Radix 재사용 컴포넌트
test-engineer           Vitest + JUnit + E2E
code-reviewer           보안·성능·일관성 정성 리뷰
qa-integrator           경계면(spec ↔ backend ↔ frontend) 정합성
devops-engineer         Vercel/Docker/K8s/nginx 배포

iPad 터미널 앱 프로젝트는 완전히 다른 10명입니다. ssh-net-engineer, terminal-tmux-engineer, keyboard-input-specialist, cloudkit-sync-engineer, appstore-release-manager 같은 식입니다. 앱의 난이도가 몰려 있는 지점이 곧 에이전트 이름이 됩니다.

팀 구성을 맨손으로 짜지 않아도 됩니다

문제는 이 팀 구성을 처음부터 설계하는 일이 꽤 막막하다는 점입니다. 몇 명으로 나눌지, 누가 누구의 산출물을 받을지, 리뷰 게이트를 어디에 둘지를 백지에서 정해야 합니다.

저는 revfactory/harness 플러그인으로 초안을 뽑고 손질하는 방식으로 시작했습니다. 도메인을 설명하면 에이전트 정의(.claude/agents/)와 스킬(.claude/skills/)을 함께 생성해주는 메타 스킬입니다.

/plugin marketplace add revfactory/harness
/plugin install harness@harness-marketplace

설치 후 "하네스 구성해줘"라고 말하면 됩니다. 유용한 부분은 팀 구조를 아무렇게나 만드는 게 아니라, 미리 정의된 여섯 가지 팀 아키텍처 패턴 중에서 고른다는 점입니다.

패턴언제 쓰나
Pipeline순차 의존 작업
Fan-out/Fan-in병렬 독립 작업
Expert Pool맥락에 따라 선택 호출
Producer-Reviewer생성 후 품질 검토
Supervisor중앙 에이전트가 동적 배분
Hierarchical Delegation하향식 재귀 위임

앞에서 말한 풀스택 8명 팀은 이 중 Producer-Reviewer(구현 후 리뷰·QA 게이트)에 가깝습니다. 생성된 결과를 그대로 쓰기보다는, 초안을 받아 실제 사고 사례를 반영하며 고쳐 나가는 출발점으로 쓰는 편이 좋았습니다. 어차피 뒤에서 이야기할 규칙들은 직접 겪어야 나오는 것들입니다.

📌 에이전트를 나누는 기준은 "기술 스택"이 아니라 책임 경계입니다. 프론트/백엔드로 나누는 것보다, "누가 이 파일을 소유하는가"로 나눠야 병렬 작업에서 충돌이 안 납니다. 실제로 SFTP 기능은 백엔드 담당과 UI 담당을 나눠 동시에 진행했고, 둘 사이의 인터페이스는 계약 문서로 먼저 동결했습니다.

스킬은 에이전트가 참조하는 작업 지침서입니다

에이전트 정의는 "누구인가"만 담고, "어떻게 하는가"는 스킬로 뺍니다. 같은 지침을 여러 에이전트가 공유할 수 있고, 상시 로드되지 않기 때문입니다.

예를 들어 서브모듈 3개가 얽힌 저장소에는 작업 순서 자체가 지침입니다.

[S1] api-spec 서브모듈: 브랜치 생성 → 스펙 수정 → push → 커밋 SHA 전달
[S2] 백엔드 레포: 서브모듈 포인터를 S1 커밋으로 갱신 → stub 재생성 → 구현
[S3] 프론트 레포: 포인터를 S1과 **동일한 커밋**으로 → client 재생성 → 구현
[S4] 모노레포: 서브모듈 포인터 스냅샷 커밋(선택)

이 순서를 스킬에 못박아 두면, 프론트를 먼저 건드려 스펙 포인터가 어긋나는 사고가 사라집니다. 이 순서가 왜 이렇게 정해졌는지는 OpenAPI-first 모노레포의 서브모듈 워크플로우에 따로 적었습니다. 검증도 규칙화합니다. "백엔드의 api-spec 포인터 == 프론트의 api-spec 포인터"를 QA 항목에 넣어두면 사람이 기억할 필요가 없습니다.

터미널 앱 쪽 스킬은 더 도메인 지식에 가깝습니다. tmux-raw-mode 스킬에는 이런 문장이 들어 있습니다.

control mode(-CC)는 Phase 16에서 완전 삭제됐다. -CC 파서·%output 통지 처리를 부활시키지 말 것.

한 번 폐기한 아키텍처로 되돌아가지 않게 막는 가드레일입니다. 이런 문장이 없으면, 몇 달 뒤 다른 세션에서 "tmux를 제어하려면 control mode를 쓰면 되겠다"는 합리적인 판단으로 되돌아갑니다.

훅은 규칙을 강제합니다

문서는 지켜지지 않을 수 있지만 훅은 실행됩니다. 설정에는 도구 호출 전후, 프롬프트 제출, 세션 시작·종료 시점에 스크립트를 걸 수 있습니다.

{
  "hooks": {
    "PreToolUse":  [{ "matcher": "Bash",         "hooks": [{ "type": "command", "command": "..." }] }],
    "PostToolUse": [{ "matcher": "Write|Edit|Bash", "hooks": [{ "type": "command", "command": "..." }] }],
    "SessionStart": [{ "hooks": [{ "type": "command", "command": "..." }] }]
  }
}

제가 쓰는 조합은 이렇습니다.

  • PostToolUse — 파일이 수정될 때마다 워크플로 핸들러 실행(린트·타입 체크 유도)
  • PreToolUse — Bash 명령을 토큰 절약형 프록시로 재작성
  • Notification / Stop — 작업 완료·권한 요청 시점을 외부 알림으로 전달

외부 알림 경로는 두 갈래로 씁니다. 터미널 안에서 난 신호는 tmux 출력 스트림을 통해 iPad 터미널 앱이 받아 띄우고(muxpad 회고), 맥 알림센터까지 올라온 것은 macOS 알림을 셀프호스팅 Bark 서버로 전달하기에서 만든 에이전트가 아이폰으로 넘깁니다.

⚠ 가장 재미있게 쓴 것은 조건부 Stop 훅입니다. 세션에 목표를 걸어두면, 그 조건이 충족되지 않은 상태에서 종료하려 할 때 훅이 판정해 작업을 계속시킵니다.

A session-scoped Stop hook is now active with condition:
"공식 문서의 모든 기능 탐색해서 구현"

한 프로젝트에서 이 훅을 걸고 자율 루프를 돌렸습니다. 훅은 매 종료 시도마다 대화 기록을 근거로 미충족 항목을 지적했습니다.

조건 미충족. 마지막 메시지에서 직접 명시: '남은 4개' 섹션 …
조건 미충족. '남은 3개' 섹션에서 …
조건 미충족. '남은 2개 — 이제 정말 근본적으로 막힌 것만 남음'

십수 회 반복 끝에 "구현 가능한 것은 전부 구현됐고, 남은 것은 플랫폼 제약으로 근본적으로 막혔다"는 지점까지 수렴했습니다. 사람이 매번 "계속해"를 입력했다면 도중에 포기했을 작업입니다.

다만 이 방식은 종료 조건을 검증 가능하게 쓸 때만 동작합니다. "잘 만들어"처럼 판정 불가능한 조건을 걸면 훅은 영원히 미충족을 반환합니다.

회귀가 규칙을 만듭니다

하네스에서 가장 가치 있는 부분은 에이전트 목록이 아니라 변경 이력입니다. 규칙은 대부분 사고 뒤에 생깁니다.

회귀에서 규칙으로

실제 변경 이력에서 세 가지만 옮기면 이렇습니다.

1) 공통 베이스 미사용 회귀 — 새 도메인을 추가하면서 공통 엔티티를 상속하지 않고, 메뉴 등록을 빠뜨리고, 이미 있는 컴포넌트를 인라인으로 다시 만든 일이 한 번에 발생했습니다. 이후 구현 스킬에 "작업 전 유사 구현 grep" 과 "산출물 검증 게이트"를 추가했습니다.

2) 커밋 정체성 오염 — 커밋이 의도치 않은 이메일과 AI 공동작성자 트레일러로 기록됐습니다. 이후 전역·프로젝트 양쪽 지침에 "저장소 설정의 정체성만 사용, -c 오버라이드 금지, 공동작성자 트레일러 금지"를 최상위 규칙(MUST)으로 올렸습니다.

3) 런타임에서만 터지는 검증 예외 — 조회 API 호출 시 ConstraintDeclarationException: HV000151로 500이 났습니다. 원인은 생성된 스펙 인터페이스의 파라미터 제약을 구현 메서드가 다시 선언한 것이었고, 같은 패턴이 6개 컨트롤러에 잠복해 있었습니다. 컴파일은 통과하고, 인증 인터셉터가 401로 먼저 끊어 테스트에서도 재현되지 않는 종류의 결함입니다. 이후 구현 규칙에 금지 항목으로, 리뷰 체크리스트에 Blocker로 각각 추가했습니다.

세 사례의 공통점은 명확합니다. 한 번 겪은 사고는 문서가 아니라 규칙으로 바꿔야 재발하지 않습니다. 그리고 규칙에는 반드시 "왜"를 함께 적어야 합니다. 이유 없는 규칙은 다음 리팩터링에서 지워집니다.

한계

✅ 효과는 분명하지만 만능은 아닙니다.

  • 하네스도 유지보수 대상입니다. 아키텍처가 바뀌면 스킬도 같이 고쳐야 합니다. control mode를 폐기했을 때 에이전트 정의·스킬 이름·페이즈 매핑을 전부 갱신해야 했습니다.
  • 규칙이 많아지면 읽히지 않습니다. 상시 로드 문서는 주기적으로 줄이는 작업이 필요합니다.
  • 에이전트를 잘게 쪼갠다고 품질이 오르지 않습니다. 책임 경계가 겹치면 같은 파일을 두 에이전트가 고쳐 충돌합니다. 계약을 먼저 동결하는 편이 낫습니다.

정리

  • 상시 로드(CLAUDE.md)·역할(agents)·절차(skills)를 분리하고, 상시 로드는 계속 줄입니다.
  • 에이전트는 기술 스택이 아니라 책임 경계로 나눕니다.
  • 문서로 부족한 규칙은 훅으로 강제합니다. 종료 조건은 검증 가능하게 씁니다.
  • 사고가 날 때마다 규칙을 추가하고, 규칙마다 이유를 함께 기록합니다.

하네스는 결국 "이 프로젝트에서 일하는 방식"을 코드화한 것입니다. 사람이 새로 합류할 때 필요한 문서와 크게 다르지 않습니다. 인프라에서 GitOps 기반 배포 자동화를 정리하며 얻은 감각과도 닿아 있습니다. 운영 상태를 사람 머릿속이 아니라 선언된 파일에 두면, 그 파일이 곧 기준이 됩니다. 하네스는 같은 원칙을 작업 방식에 적용한 것입니다.

참고링크