본문 바로가기
AI

하네스 엔지니어링 🐴

by hong7 2026. 4. 24.

Claude Code로 작업하다 보면 이런 경험이 생깁니다.

분명히 print() 쓰지 말라고 했는데 어느 순간 다시 쓰고 있습니다.

세션이 길어지면 앞에서 정한 규칙을 슬그머니 잊어버립니다.

같은 말을 반복하게 됩니다.

이건 모델이 멍청해서가 아닙니다.

구조가 없어서입니다.

 

AI 활용의 발전 흐름

AI를 활용하는 방식은 점점 "프롬프트 잘 쓰기"에서 "환경 설계"로 이동하고 있습니다.

 

프롬프트 엔지니어링: AI에게 프롬프트를 잘 작성하는 기술
컨텍스트 엔지니어링: 프로젝트 상황을 AI에게 알려주기
MCP + 스킬: 도구 연결 + AI가 쓸 수 있는 범위 정하기
하네스 엔지니어링: AI가 일하는 전체 환경을 설계

 

도구를 계속 얹어주는 게 아니라, 정확하게 동작할 수밖에 없는 환경 자체를 설계하는 것입니다.

 

프롬프트는 "부탁"이다

프롬프트로 규칙을 전달하는 건 결국 부탁입니다.

print() 쓰지 마. OSLog 써줘.
try! 쓰지 마. do-catch로 처리해줘.

Claude가 들어줄 수도 있고, 세션이 길어지면 잊어버릴 수도 있습니다.

강제가 아니기 때문입니다.

하네스 엔지니어링은 다르게 접근합니다.

규칙을 어기는 게 구조적으로 불가능하도록 만듭니다.

 

하네스란

하네스(harness)는 말에 씌우는 마구입니다.

고삐로 방향을 잡고, 안장으로 의도를 전달하고, 끈으로 트랙 안에서만 달리게 만듭니다.

말의 힘을 제한하는 게 아니라, 힘이 올바른 방향으로 쓰이게 만드는 장치입니다.

이 개념을 AI에 적용하면 이렇게 됩니다.

 

"에이전트가 실수를 할 때마다, 그 실수가 구조적으로 반복 불가능하게 만드는 설계"

 

하네스가 필요한 이유

컨텍스트 부패

Claude는 컨텍스트 윈도우라는 제한된 공간 안에서 동작합니다.

작업이 길어질수록 이 공간이 차오르고, 앞에서 했던 내용을 잊기 시작합니다.

처음엔 규칙을 잘 지키다가 대화가 길어지면 슬그머니 무너지는 게 이 때문입니다.

강제가 없는 규칙

Claude는 규칙을 "모르는" 게 아닙니다. 

try!가 나쁜 패턴이라는 걸 압니다. 그런데도 씁니다.

구조적 제약이 없으면, 규칙은 그냥 참고사항이 됩니다.

 

하네스의 3요소

사람이 시스템을 만들고, Claude는 그 시스템 안에서 수행만 합니다.

AI의 성능을 좌우하는 것은 모델의 지능이 아니라 하네스입니다.

1. 컨텍스트 파일 — 기억을 강제한다

CLAUDE.md는 프로젝트 루트에 두는 마크다운 파일입니다.

Claude Code는 매 세션마다 이 파일을 가장 먼저 자동으로 읽습니다.

세션이 초기화되어도, 컨텍스트가 날아가도 CLAUDE.md는 항상 먼저 읽힙니다.

 

모든 규칙을 담으려 하면 안 됩니다.

CLAUDE.md는 "모든 규칙이 있는 곳"이 아니라 "모든 규칙을 찾을 수 있는 곳"이어야 합니다.

항상 적용되는 핵심 규칙만 담고, 세부 내용은 별도 파일로 분리해서 필요할 때 참조하게 합니다.

# RunCoach — Claude Guide

## 프로젝트 개요
AI 코칭 러닝 앱. SwiftUI + Clean Architecture.

## 아키텍처
Clean Architecture 4계층: Domain / Data / Presentation / Infrastructure
ViewModel은 @MainActor. 비동기 처리는 async/await.

<rules>
- print() 금지. 로그는 OSLog.Logger 사용
- try! 금지. do-catch + logger.error() 처리
- 외부 라이브러리 추가 전 반드시 확인
</rules>

## 상세 가이드
- 아키텍처 원칙: docs/architecture.md 참고
- 코딩 컨벤션: docs/conventions.md 참고

금지 형태로 쓰는 것이 중요합니다.

"OSLog를 사용해줘"보다 "print() 금지"가 훨씬 잘 지켜집니다.

CLAUDE.md는 한 번 쓰고 끝내는 파일이 아닙니다.

200줄 이내로 유지하면서, 실패할 때마다 한 줄씩 추가하며 진화시킵니다.

 

Claude가 try!를 썼다         → "try! 금지" 추가
Claude가 print()를 썼다      → "print() 금지" 추가
Claude가 라이브러리를 추가했다 → "외부 라이브러리 추가 전 확인" 추가

처음부터 완벽하게 만드는 게 아니라, 실패할 때마다 정교해집니다.

 

2. 자동 강제 시스템 — 행동을 강제한다

Claude Code에는 Hooks라는 자체 기능이 있습니다.

Claude가 파일을 수정하거나 작업을 완료하려는 순간에 자동으로 검사 스크립트가 실행되는 시스템입니다.

동작 방식은 단순합니다.

  1. Claude가 코드를 작성하고 저장하려 합니다
  2. Hook이 자동으로 실행되어 규칙을 검사합니다
  3. 규칙 위반이 있으면 Claude에게 피드백을 전달합니다
  4. Claude가 피드백을 받고 스스로 수정합니다
  5. 사람 개입 없음

이것이 자동 교정 루프입니다.

말이 고삐에 의해 방향이 틀어지면 자연스럽게 올바른 방향으로 돌아오는 것처럼,
Claude도 Hook의 피드백을 받고 스스로 올바른 코드로 돌아옵니다.

 

성공은 조용히, 실패만 시끄럽게.

검사를 통과하면 아무 말도 없고, 실패했을 때만 Claude에게 알립니다.

Hooks는 실행 시점에 따라 두 가지로 나뉩니다.

  • PreToolUse: 파일 수정 에 실행. 규칙을 위반하면 저장 자체를 차단합니다.
  • PostToolUse: 파일 수정 에 실행. 규칙을 위반하면 Claude에게 피드백을 전달하고 스스로 수정하게 만듭니다.

.claude/settings.json에 설정합니다.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "스크립트 실행 → 위반 감지 시 exit 2로 저장 차단"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "swiftlint lint --fix \\"$CLAUDE_PROJECT_DIR\\""
          }
        ]
      }
    ]
  }
}

PreToolUse는 나쁜 코드를 저장 자체가 안 되게 막습니다.

Claude가 규칙을 위반한 채로 파일을 저장하려는 순간, Hook이 가로채서 차단합니다.

프롬프트로 "하지 마"라고 부탁하는 게 아니라, 구조적으로 불가능하게 만드는 것입니다.

 

PostToolUse는 저장 후 SwiftLint를 자동으로 실행해서 위반된 부분을 잡아내고 바로 수정합니다.

오류 메시지에 수정 방법까지 담으면 Claude가 추측 없이 바로 고칩니다.

프롬프트로 "좋은 코드 써줘"라고 부탁하는 게 아니라, 규칙을 어기면 즉시 피드백이 돌아오는 구조를 만드는 것입니다.

 

3. 가비지 컬렉션 — 하네스를 진화시킨다

Claude는 기존 코드를 보고 패턴을 따라갑니다.

문제는 나쁜 패턴이 있으면 그것도 그대로 복제한다는 점입니다.

나쁜 패턴이 눈덩이처럼 불어나기 때문에, 주기적인 정리가 필요합니다.

이를 위해 청소 전담 Sub-agent를 만듭니다.

.claude/agents/cleanup.md 파일로 정의해두면, 필요할 때 새 세션에서 호출해서 실행할 수 있습니다.

<!-- .claude/agents/cleanup.md -->

너는 이 프로젝트의 코드 품질 관리자야.
아래 항목을 점검하고 발견하면 수정해줘.

점검 항목:
- print()가 남아있는 파일
- try!가 쓰인 곳
- 사용하지 않는 Extension, Protocol
- CLAUDE.md 규칙을 위반한 코드
- 문서와 실제 코드가 달라진 부분

수정 후, 기존 CLAUDE.md에 없는 새로운 나쁜 패턴이 발견됐다면
CLAUDE.md rules 섹션에 금지 규칙으로 추가해줘.

여기서 하네스가 진화합니다.

청소 에이전트가 새로운 나쁜 패턴을 발견할 때마다 CLAUDE.md에 규칙이 한 줄씩 추가됩니다.

시간이 지날수록 하네스가 정교해지고, 같은 실수는 구조적으로 반복이 불가능해집니다.

 

역할별 Sub-agent로 팀을 구성한다

CLAUDE.md가 프로젝트 전체에 적용되는 공통 규칙이라면,
Sub-agent는 특정 역할에 집중하는 전문가입니다.

 

.claude/agents/ 폴더에 역할별로 마크다운 파일을 정의해두면,
필요한 순간에 해당 전문가를 새 세션에서 호출할 수 있습니다.

.claude/agents/
├── cleanup.md            # 코드 품질 관리자
├── doc-writer.md         # 문서화 담당
└── dependency-checker.md # 아키텍처 경계 검사

세 에이전트 모두 주기적으로 돌리는 유지보수 전담 역할입니다.

기능 구현과 분리된 독립 세션에서 실행되기 때문에, 메인 작업 흐름을 방해하지 않습니다.

 

dependency-checker.md는 Clean Architecture의 레이어 경계를 주기적으로 점검합니다.

프로젝트가 커질수록 Presentation이 Data를 직접 참조하는 등 경계가 슬그머니 무너지는데, 이걸 자동으로 잡아냅니다.

<!-- .claude/agents/dependency-checker.md -->

너는 이 프로젝트의 아키텍처 경계 검사자야.
Clean Architecture 레이어 규칙을 기준으로 의존성을 점검해줘.

레이어 규칙:
- Presentation → Domain만 참조 가능
- Data → Domain만 참조 가능
- Domain → 외부 레이어 참조 금지

위반 사항이 있으면 파일명과 라인 번호를 명시하고,
올바른 의존성 방향으로 수정하는 방법을 함께 제안해줘.
"dependency-checker 에이전트로 전체 레이어 의존성 점검해줘"

CLAUDE.md가 프로젝트의 헌법이라면, Sub-agent는 각 분야의 전문가입니다.

헌법 안에서 각자의 역할을 수행합니다.

 

핵심 철학

에이전트가 실수를 할 때, 일반적인 방식은 프롬프트를 수정합니다.

"이거 하지마"

 

하네스는 다르게 접근합니다.

"이걸 아예 못 하게 구조를 바꾼다"

 

규칙이 사람의 판단에 의존하는 게 아니라, 시스템에 내장되어 자동으로 강제됩니다.

 

마치며

한 번에 완벽하게 만들려고 하지 않아도 됩니다.

Claude가 실제로 실패한 경우에만 그 케이스를 모아서 하네스를 조금씩 개선하면 됩니다.

 

모델이 똑똑해질수록 하네스는 더 단순해져야 합니다.

모델이 업그레이드될 때마다 하드코딩된 규칙을 더 추가하고 있다면, 흐름을 거슬러가고 있는 것입니다.

Claude가 스스로 판단할 수 있는 걸 사람이 억지로 제약하고 있는 것이니까요.

 

앞으로는 하네스가 프로젝트의 시작점이 될 것입니다.

새로운 프로젝트를 시작할 때마다 "이 기술 스택용 하네스"처럼 템플릿으로 제공되고, 기술 스택 선택 기준도 개발자 경험이 좋은 프레임워크가 아니라 좋은 하네스가 갖춰진 프레임워크로 바뀔 수 있습니다.

 

코드를 직접 작성하는 것에서, AI가 올바르게 코드를 작성할 수 있는 환경을 설계하는 것으로 개발자의 역할이 바뀌고 있습니다.

잘 구성된 환경과 적절한 도구, 그리고 검증은 모델의 성능과 무관하게 어떤 에이전트든 더 효과적으로 만듭니다.