하네스 엔지니어링: 프롬프트에서 시스템으로 (프롬프트 엔지니어링 vs. 하네스 엔지니어링)

AI 코딩 에이전트의 성능은 모델이 아니라 모델을 감싸는 시스템에서 결정된다. 프롬프트를 잘 쓰는 것보다 에이전트가 일하는 환경-규칙, 도구, 피드백 루프-을 잘 설계하는 것이 더 중요해졌다. 하네스 엔지니어링은 "AI에게 뭘 시킬까"에서 "AI가 잘 일하는 구조를 어떻게 만들까"로의 전환이다.

하네스 엔지니어링: 프롬프트에서 시스템으로 (프롬프트 엔지니어링 vs. 하네스 엔지니어링)

프롬프트를 정교하게 다듬어도 같은 실수가 반복된다면, 문제는 질문이 아니라 환경에 있다.

1. 하네스 엔지니어링이란?

"모델은 지능을 담고, 하네스는 그 지능을 쓸모 있게 만든다."

하네스(Harness)는 말의 마구(馬具)에서 온 은유다. 아무리 힘센 말도 마구 없이는 방향을 잡을 수 없듯, AI 에이전트도 올바른 구조 없이는 신뢰할 수 있는 결과를 내지 못한다.

공식으로 정리하면 단순하다.

Agent = Model + Harness

하네스는 모델 자체를 제외한 모든 것이다. 에이전트에 지시를 내리는 문서, 코드를 실행하는 환경, 작업 결과를 검증하는 피드백 루프, 여러 세션에 걸쳐 맥락을 유지하는 메모리. 이 모든 것이 하네스다.

2. 왜 프롬프트 엔지니어링만으로는 부족한가?

2-1. 단일 호출의 한계

프롬프트 엔지니어링은 "한 번의 질문을 잘 쓰는 기술"이다. 코드 한 줄 완성, 함수 설명처럼 단발성 작업엔 충분하다. 하지만 여러 파일을 수정하고, 테스트를 작성하고, PR까지 올리는 복합 작업에선 단일 프롬프트의 품질로는 전체 파이프라인을 제어할 수 없다.

2-2. 재현 불가능한 결과

프롬프트가 아무리 정교해도 에이전트의 컨텍스트가 매번 달라지면 결과도 달라진다. 환경이 없으면 일관성이 없다. Jira 티켓만 던져준 경우 AI는 코드베이스를 직접 분석하지 않고 추측으로 구현하며, 이것이 환각(hallucination)의 주된 원인이 된다.

2-3. 스케일 불가

Stripe는 매주 1,300개의 AI 생성 PR을 배포한다. 이 규모는 개발자 한 명이 프롬프트를 1,300번 쓴 것이 아니다. 오케스트레이션된 파이프라인 인프라, 즉 하네스가 없으면 불가능한 수치다. 프롬프트 엔지니어링은 개인 생산성 도구이지, 조직 규모의 자동화 도구가 아니다.

한눈에 보는 차이

구분 프롬프트 엔지니어링 하네스 엔지니어링
초점 입력 품질 개선 환경 설계
적용 범위 단발성 작업 복합·반복 작업 파이프라인
결과 일관성 실행마다 다름 구조적으로 예측 가능
확장성 개인 단위 팀·조직 단위
오류 처리 프롬프트 재작성 피드백 루프 개선
인간의 역할 매 작업 직접 지시 체크포인트 검토·방향 설정

프롬프트 엔지니어링 = 질문을 잘 쓰는 기술, 하네스 엔지니어링 = AI가 잘 일하는 공장을 짓는 기술

3. 내부 구조: 하네스는 어떻게 동작하는가?

하네스는 Plan → Execute → Verify 루프로 작동한다. 구성요소는 각 단계에 정확히 대응한다.

Plan ──────────────────────────────────────────────
  지시 문서 (CLAUDE.md / AGENTS.md)
  아키텍처 제약
  구조화된 작업 템플릿
    ↓
Execute ────────────────────────────────────────────
  파일시스템 & 코드 실행 환경
  메모리 & 지식 저장소
    ↓
Verify ─────────────────────────────────────────────
  계산형 센서: 린터 · 타입 체커 · 테스트
  추론형 센서: AI 코드 리뷰
    ↓ 실패 시 Execute로 복귀
    ↓ 통과 시
완료

Plan — 에이전트에게 맥락을 넘긴다

에이전트가 추측 없이 일할 수 있도록 행동 전에 방향을 설정한다. 인간의 개입이 가장 집중되는 단계다.

  • 지시 문서 (CLAUDE.md / AGENTS.md): 코딩 규칙, 금지 패턴, PR 작성 방식 등 에이전트가 따를 규칙을 사전에 명시한다.
  • 아키텍처 제약: 접근 가능한 파일 범위, 사용 가능한 도구, 변경 허용 경계를 구조적으로 제한한다.
  • 구조화된 작업 템플릿: 막연한 "이 기능 추가해줘"가 아니라 수정 대상 파일 경로, 구현 노트, 승인 기준이 명시된 작업 단위를 넘긴다.

Execute — 에이전트가 자율적으로 실행한다

하네스가 제공하는 도구를 바탕으로 에이전트가 구현한다. 이 단계에서 인간이 개입할수록 Plan이 부실하다는 신호다.

  • 파일시스템 & 코드 실행 환경: 샌드박스 내에서 코드를 작성하고 실행한다. 상태는 파일시스템에 유지되어 세션이 끊겨도 작업이 지속된다.
  • 메모리 & 지식 저장소: Git 이력과 지식 파일을 통해 여러 세션에 걸친 컨텍스트를 유지한다.

Verify — 센서가 결과를 검증한다

결과물이 기준을 충족하는지 자동으로 판단하고, 실패하면 Execute로 피드백을 돌려보낸다.

  • 계산형 센서 (Computational): 린터, 타입 체커, 테스트 — 빠르고 결정론적. 먼저 실행해 빠른 피드백을 준다.
  • 추론형 센서 (Inferential): AI 코드 리뷰 — 느리지만 계산형 센서가 잡지 못하는 설계 냄새를 의미론적으로 감지한다.

이 루프가 안정적으로 돌아갈수록 인간의 역할은 매 실행 지시에서 루프 설계와 체크포인트 검토로 이동한다.

4. 직접 구축해보기: Claude Code로 최소 하네스 체험

Claude Code 하나로 Plan → Execute → Verify 루프를 실제로 돌려볼 수 있다. 아래 순서대로 따라하면 10분 안에 체감 가능하다.

사전 준비

mkdir harness-demo && cd harness-demo
npm init -y
npm install --save-dev eslint jest

eslint.config.mjs — 확장자를 .js가 아닌 .mjs로 만든다. CommonJS 프로젝트에서 .js로 만들면 ESLint가 모듈 형식을 인식하지 못해 동작하지 않는다.

export default [{ rules: { "no-console": "error", "no-unused-vars": "error" } }];

package.json scripts — 빈 src/나 테스트 파일이 없을 때 lint·test가 에러로 종료되지 않도록 옵션을 추가한다.

"lint": "eslint src/ --no-error-on-unmatched-pattern",
"test": "jest --passWithNoTests"

Plan — CLAUDE.md와 검증 자동화를 설정한다

에이전트 실행 전에 두 가지를 준비한다. 하나는 에이전트가 따를 규칙(CLAUDE.md), 다른 하나는 결과를 자동 검증할 hook 설정이다.

프로젝트 루트에 CLAUDE.md를 만든다. Claude Code는 실행 시 이 파일을 자동으로 읽어 행동 기준으로 삼는다.

# CLAUDE.md

## 코딩 규칙
- 함수는 단일 책임 원칙을 따른다
- 모든 함수에 Jest 테스트를 작성한다
- console.log 사용 금지

## 금지 패턴
- 환경변수 하드코딩

.claude/settings.jsonPostToolUse hook을 설정한다. 에이전트가 파일을 저장할 때마다 lint와 test가 자동 실행되고, 실패 결과가 에이전트에게 피드백으로 돌아간다.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npm run lint && npm test"
          }
        ]
      }
    ]
  }
}

Execute — 에이전트를 실행하고 작업을 지시한다

터미널에서 Claude Code를 실행한다.

claude

에이전트는 CLAUDE.md를 읽어 규칙을 파악한 뒤 작업을 기다린다. 프롬프트는 짧아도 된다.

src/math.js를 새로 만들어줘. 두 수를 더하는 add 함수.

CLAUDE.md의 규칙이 나머지를 처리한다. 모든 함수에 Jest 테스트를 작성한다 규칙에 따라 테스트 파일이 자동 생성되고, console.log 사용 금지 규칙에 따라 lint 에러 없는 코드가 만들어진다. 승인 기준을 프롬프트에 쓰지 않아도 하네스가 기준을 강제한다.

// src/math.js — console.log 없음, 규칙 준수
function add(a, b) {
  return a + b;
}
module.exports = { add };
// src/math.test.js — 프롬프트에 요청하지 않았지만 자동 생성
const { add } = require('./math');

describe('add', () => {
  test('두 양수를 더한다', () => { expect(add(2, 3)).toBe(5); });
  test('음수를 더한다', () => { expect(add(-1, -2)).toBe(-3); });
  test('양수와 음수를 더한다', () => { expect(add(5, -3)).toBe(2); });
  test('0을 더한다', () => { expect(add(0, 0)).toBe(0); });
});

Verify — hook이 자동으로 발동한다

파일이 저장되는 순간 Plan에서 설정한 hook이 실행된다.

통과 시 — 아래 결과가 에이전트에게 반환된다.

Tests:  2 passed, 2 total
Time:   0.093s

규칙 위반 시 (예: console.log 포함) — 즉시 에러가 반환되고 에이전트가 스스로 재수정을 시도한다.

/src/math.js
  2:3  error  Unexpected console statement  no-console

✖ 1 problem (1 error, 0 warnings)

문서 관리에도 동일하게 적용된다

코드가 아닌 문서 작업도 구조는 같다.

Plan — CLAUDE.md에 문서 작성 규칙을 정의한다.

## 문서 작성 규칙
- 문체: 간결한 서술형, 경어 사용 금지
- 섹션마다 한 줄 요약으로 시작한다
- 이모지 사용 금지

Execute — 짧은 프롬프트로 지시한다.

auth 모듈 README.md 작성해줘.

에이전트는 CLAUDE.md의 문체 규칙에 따라 문서를 생성한다. 이모지 없이, 경어 없이, 각 섹션 첫 줄에 요약이 붙은 형태로 만들어진다. 프롬프트에 스타일을 명시하지 않아도 결과가 일관된다.

Verify — 문서는 코드처럼 결정론적 검증이 어렵다. markdownlint로 형식을 자동 검사할 수 있고, 내용 품질은 추론형 센서(AI 리뷰)가 담당한다.

"command": "markdownlint docs/ --fix"

5. 하네스 엔지니어링을 위한 도구

전체 사이클(Plan → Execute → Verify)을 관리할 수 있는 주요 도구다. 도구 간 핵심 차이는 개발자가 루프에 얼마나 개입하는가다.

많은 개입 ←──────────────────────────────→ 적은 개입
Claude Code / Aider        OpenCode           OpenHands
  (스텝 바이 스텝)       (에이전트 전환·확장)    (완전 자율)
  • Claude Code — Claude 전용. CLAUDE.md + hooks로 하네스를 구성. 이 글의 실습 환경
  • Aider — 모델 무관 CLI. Claude Code와 구조는 같고, GPT-4·Gemini·로컬 모델로 교체 가능
  • OpenCode — MIT 오픈소스 CLI. Plan·Build 에이전트 전환으로 작업 흐름을 조율하며, OMO 플러그인으로 멀티에이전트 자율 실행까지 확장 가능
  • OpenHands — Docker 샌드박스에서 에이전트가 완전 자율 실행. 셀프호스팅 가능
Claude Code Aider OpenCode OpenHands
Plan CLAUDE.md + hooks CONVENTIONS.md 에이전트 역할 설정 지시 문서
Execute 파일시스템 + 메모리 파일 직접 수정 파일시스템 + 멀티에이전트 Docker 샌드박스
Verify PostToolUse hooks 테스트 자동 실행 hooks + 에이전트 전환 자동 검증
지원 모델 Claude 전용 모델 무관 모델 무관 모델 무관
라이선스 독점 Apache 2.0 MIT MIT
형태 CLI CLI (에디터 연동) CLI (TUI · 멀티에이전트) 웹 · 셀프호스팅

OMO (Oh My OpenAgent) — OpenCode를 기반으로 동작하는 멀티에이전트 오케스트레이션 플러그인. OpenCode가 "개발자와 함께 일하는 에이전트"라면, OMO는 "개발자 대신 팀을 꾸려 일하는 시스템"이다. ultrawork 명령 하나로 Sisyphus(오케스트레이터)가 작업을 분해해 Prometheus(전략)·Hephaestus(실행)·Oracle(아키텍처) 등 전문 에이전트에게 병렬 위임한다. OpenCode + LSP + AST-Grep + Tmux가 전제 조건이며, 독립 실행 에이전트가 아닌 오케스트레이션 레이어다. GitHub →

6. 도입 전 고려사항

행동 검증의 한계

lint와 테스트가 통과해도 기능이 올바르게 동작하는지는 별개의 문제다. "코드가 실행된다"와 "코드가 맞다"는 다르다. 의도한 대로 동작하는지 판단하는 영역은 여전히 가장 미성숙하고, 인간의 검토가 필요하다.

비결정론

같은 하네스, 같은 프롬프트로도 실행마다 다른 결과가 나올 수 있다. 하네스는 나쁜 출력의 확률을 줄이지, 제거하지 못한다. 중요한 작업일수록 반복 실행과 검토가 필요하다.

하네스 드리프트

코드베이스는 계속 변하지만 CLAUDE.md는 그대로인 경우가 많다. 규칙이 실제 코드와 어긋나면 에이전트는 오래된 규칙을 충실히 따르면서 잘못된 방향으로 일한다. 하네스도 코드처럼 유지보수가 필요하다.

규칙 충돌

규칙이 많아질수록 서로 모순되거나 에이전트가 어느 규칙을 우선할지 혼란스러워진다. 잘 동작하는 하네스는 규칙의 양이 아니라 질로 만들어진다.

초기 구축 비용

빈 프로젝트에 하네스를 구축하는 건 쉽지만, 기존 코드베이스에 하네스를 씌우는 건 상당한 시간이 필요하다. 점진적 도입이 현실적이다.

완성된 하네스는 없다. 에이전트를 운용하면서 드리프트를 감지하고 규칙을 갱신하는 것 자체가 하네스 엔지니어링의 일부다.

7. 마치며

소프트웨어 엔지니어의 일이 변하고 있다. "코드를 직접 쓰는 사람"에서 "AI가 코드를 올바르게 쓸 수 있는 환경을 설계하는 사람"으로. 이 전환이 낯설게 느껴진다면, 그건 아직 프롬프트에 집중하고 있기 때문일 것이다.

하네스 엔지니어링은 새로운 도구를 배우는 것이 아니다. 문제를 바라보는 관점을 바꾸는 것이다. 모델을 믿는 것이 아니라, 모델이 잘 일하는 구조를 믿는 것.

참조

Subscribe to PAASUP IDEAS

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe