"Agent = Model + Harness." 모델이 엔진이라면, 하네스는 차체·브레이크·핸들·계기판 전부다.
2026년 들어 AI 업계에서 가장 빠르게 자리 잡은 용어 중 하나가 하네스 엔지니어링(Harness Engineering) 이다. Martin Fowler 블로그, Anthropic 엔지니어링, arxiv 논문, Medium 글까지 동시다발로 쏟아지고 있는데, 정작 한국어로 정리된 자료는 드물다. 개발자 입장에서 이게 왜 중요하고, 기존의 프롬프트 엔지니어링 / 컨텍스트 엔지니어링과 어떻게 다른지 짚어보자.
하네스(Harness)란 무엇인가
하네스는 원래 말(馬)에게 채우는 마구(馬具), 혹은 소프트웨어 테스트 하네스에서처럼 "대상 주변을 감싸는 제어 장치" 를 의미한다. LLM 맥락에서는 이렇게 정의된다.
Harness = 에이전트에서 모델(LLM)을 뺀 나머지 전부.
즉 프롬프트, 도구(tools), 메모리, 플래닝 루프, 샌드박스, 오케스트레이션, 서빙 레이어, 컨텍스트 파이프라인까지 — 모델을 둘러싸고 있는 모든 것이 하네스다. 모델은 교체 가능한 부품이지만, 하네스는 제품의 정체성 을 결정한다.
업계에서 이런 말이 정착됐다.
"The harness makes or breaks an AI product."
같은 Claude/GPT를 쓰더라도, 하네스가 잘 짜인 제품과 그렇지 않은 제품의 사용자 경험은 완전히 다르다.
왜 지금 "하네스 엔지니어링"이 뜨는가
2024~2025년은 프롬프트 엔지니어링 이 주연이었다. 2025년 중반부터는 컨텍스트 엔지니어링(context engineering) 이 새로 등장해, "무엇을 컨텍스트 윈도우에 넣을 것인가" 가 화두가 됐다. 2026년에는 한 층 더 올라와서, 이 모든 걸 감싸는 시스템 설계 가 문제의 중심이 됐다. 그게 하네스 엔지니어링이다.
세 레이어를 정리하면:
- 프롬프트 엔지니어링 — 모델에 들어갈 문장 을 설계한다.
- 컨텍스트 엔지니어링 — 컨텍스트 윈도우에 무엇을, 언제 넣을지를 설계한다.
- 하네스 엔지니어링 — 그 위의 앱·인프라 전체 를 설계한다. 언제 컨텍스트를 로드하고, 어떤 도구를 쓸 수 있고, 어떤 액션이 허용되고, 실패를 어떻게 복구할지를 전부 포함한다.
Gartner는 2026년 말까지 엔터프라이즈 애플리케이션의 40%가 태스크 특화 AI 에이전트를 포함할 것으로 전망했다. 이 40%가 실제로 작동하게 만드는 레이어가 하네스다.
하네스의 구성 요소 — 개발자 관점
여러 자료를 종합하면 하네스는 대체로 다음 컴포넌트로 이뤄진다.
1) ReAct 기반 중심 루프
사전 점검 및 컴팩션 → 사고(thinking) → 자기 검증(self-critique) → 액션 → 도구 실행 → 후처리. 이 6단계가 돌아가는 동안 토큰 버짓, 타임아웃, 리트라이 정책이 붙는다.
2) 도구(Tools) 레이어
파일 읽기/쓰기, 셸 실행, 웹 검색, DB 쿼리, MCP 서버 연결. 도구 스키마 품질이 에이전트 성능에 직결된다 — 애매한 파라미터 설명은 곧 잘못된 호출 로 이어진다.
3) 메모리
- 단기: 현재 태스크의 중간 산출물.
- 장기: 세션을 넘어서는 사실/선호/가이드라인. 파일 기반 메모리(MEMORY.md류)나 벡터 DB가 흔하다.
4) 컨텍스트 파이프라인
RAG, 코드 인덱싱, 문서 검색. "지금 필요한 정보만" 적시에 주입하는 게 핵심.
5) 샌드박스
셸·코드 실행을 격리된 환경(컨테이너, VM, WASM)에서 돌려서 사고 범위를 제한한다.
6) 가이드(Guides)와 센서(Sensors)
Martin Fowler의 표현을 빌리면:
- Guides(피드포워드 제어): 에이전트가 행동하기 전에 방향을 잡아주는 장치. 시스템 프롬프트, Skill 파일, 가드레일 훅.
- Sensors(피드백 제어): 행동 후에 검증하고 자기 교정을 유도. 테스트 러너, 린터, 타입 체커, 정적 분석, LLM-as-a-judge.
7) 오케스트레이션 & 서빙
멀티 에이전트 협업, 태스크 큐, 라우팅, 관측성(토큰 사용, 레이턴시, 실패율 대시보드), 비용 관리.
실제로 뭘 해야 하나 — 체크리스트
개발자가 하네스를 의식적으로 설계하기 시작할 때 가장 먼저 손대면 효과가 큰 것들:
✅ 관측성부터 깔기
에이전트가 뭘 했는지 모르면 개선도 못 한다. 모든 도구 호출, LLM 응답, 토큰 사용량을 로깅해서 타임라인으로 복원할 수 있어야 한다. "에이전트 디버거" 라는 관점으로 접근.
✅ 실패 모드 카탈로그화
컨텍스트 폭발, 무한 루프, 잘못된 도구 선택, 환각(hallucinated) API 호출 — 이 네 가지는 거의 모든 에이전트가 겪는다. 각각에 대해 감지 방법 + 회복 전략 을 둔다.
✅ 센서 강화
에이전트가 "다 했습니다" 라고 말할 때, 실제로 됐는지 결정론적으로 검증 하는 게이트를 걸어라. 테스트 실행, 빌드, 타입 체크, 스키마 검증. LLM 자가 검증은 보조수단.
✅ 컨텍스트 예산 명시
한 태스크당 쓸 수 있는 토큰 상한을 정해두고, 그 안에서 가장 관련 높은 정보만 추려 넣는 파이프라인을 만든다. "전부 다 넣자" 는 비용도 비용이지만 성능 자체도 떨어뜨린다.
✅ 도구 스키마 다듬기
도구 설명을 1인칭 사용자 매뉴얼처럼 쓴다. 예시 입력/출력, 실패 케이스, "언제 쓰지 말 것" 까지. 이것만 다듬어도 체감 성능이 20~30% 오르는 경우가 흔하다.
✅ 데이터 품질
현업 데이터는 지저분하다. 업계 조사에 따르면 에이전트 구현 시간의 80%가 프레임워크 설정이 아니라 데이터 엔지니어링과 거버넌스에 쓰인다. 모델이나 프레임워크를 고민하기 전에 데이터부터 봐라.
자주 나오는 오해
❌ "프레임워크만 잘 고르면 된다"
LangGraph, AutoGen, CrewAI, Mastra — 프레임워크는 골격을 제공할 뿐, 센서·가이드·컨텍스트 파이프라인은 결국 도메인별로 직접 짜야 한다.
❌ "더 큰 모델로 바꾸면 해결된다"
모델 업그레이드는 상한선을 올려주지만, 하네스가 부실하면 상한 근처에도 못 간다. 반대로 하네스가 잘 짜여 있으면, 더 작은 모델로도 프로덕션에 쓸 만한 결과가 나온다.
❌ "에이전트 = 프롬프트 잘 쓰기"
프롬프트 엔지니어링은 하네스의 한 컴포넌트일 뿐 이다. 전체 시스템 설계 없이 프롬프트만 다듬는 건, 엔진만 튜닝하면서 브레이크 없이 달리는 셈.
내일부터 시도할 3가지
- 에이전트 세션 타임라인 뷰어를 만들어라. 기존 LLM 호출 로그에 trace_id를 붙이고, 도구 호출/응답/토큰을 시간순으로 재생 가능하게. 이게 있으면 "왜 이상한 짓을 했지?" 에 답할 수 있다.
- 센서 1개만 먼저 추가해라. 코드 에이전트라면 PostToolUse 단계에서 테스트 자동 실행 + 실패 시 에이전트에게 피드백 루프. 가장 ROI 높은 투자.
- 컨텍스트 예산 계측. 태스크별 평균 토큰 소비, 컨텍스트 점유율, 관련도(retrieval precision)를 찍어보면 어디서 새는지 바로 보인다.
마무리
2025년까지의 키워드가 "모델이 얼마나 똑똑해졌는가"였다면, 2026년 이후의 질문은 "그 똑똑한 모델을 어떻게 제어하고 통합하는가" 다. 하네스 엔지니어링은 마케팅 용어가 아니라, 프로덕션에서 에이전트가 실제로 작동하게 만드는 유일한 길이다.
그리고 좋은 소식은 — 이건 결국 소프트웨어 엔지니어링 문제 라는 점이다. 관측성, 테스트 가능성, 실패 복구, 인터페이스 설계. 우리가 지난 수십 년 동안 갈고닦아 온 기술이 그대로 통하는 영역이다. AI 시대에 백엔드 개발자의 감각이 오히려 더 귀해지고 있다.
실전 적용 가이드 — 코딩 에이전트 프로젝트에 하네스 얹기
이론만으로는 감이 안 오니, Claude Code 같은 코딩 에이전트를 팀 프로젝트에 도입한다 고 가정하고 단계별로 하네스를 구성해보자. 예시 프로젝트 이름은 acme-api (Node/TypeScript 백엔드)로 잡는다.
단계 0 — 리포 구조 준비
에이전트 설정은 리포 안에 커밋하는 게 원칙이다. 팀원 모두가 같은 하네스를 쓰게 만들고, PR 리뷰 대상이 된다.
acme-api/
├── .claude/
│ ├── settings.json # 훅, 퍼미션, 모델 설정
│ ├── skills/
│ │ ├── code-review/SKILL.md
│ │ ├── migration/SKILL.md
│ │ └── api-docs/SKILL.md
│ └── commands/ # 슬래시 커맨드
├── .mcp.json # MCP 서버 정의
├── CLAUDE.md # 프로젝트 헌법
└── scripts/
└── agent-hooks/ # 훅에서 호출하는 셸 스크립트
단계 1 — CLAUDE.md로 "헌법" 만들기
에이전트가 매 세션마다 제일 먼저 읽는 파일. 이걸 부실하게 두면 뒤에서 무엇을 해도 안 된다.
# acme-api
## 스택
- Node 20, TypeScript strict, Fastify, Prisma(Postgres)
- 테스트: Vitest. 빌드: tsup. 린트: Biome.
## 절대 규칙
- `src/generated/**`는 수정 금지 (Prisma 생성물)
- `.env*`, `infra/secrets/**`는 읽기·쓰기 모두 금지
- 마이그레이션은 반드시 `pnpm db:migrate:dev`로 생성 (직접 SQL 편집 금지)
- 커밋 전 `pnpm check` (typecheck + biome + vitest --run) 통과 필수
## 관례
- API 핸들러는 `src/routes/**`에, 도메인 로직은 `src/domain/**`에.
- 에러는 `AppError` 서브클래스로만. 맨 `throw new Error(...)` 금지.
단계 2 — 가드레일 Hooks 설정
여기가 사고 방지의 핵심. settings.json 예시:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "./scripts/agent-hooks/block-sensitive.sh" }
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "./scripts/agent-hooks/format-and-log.sh" }
]
}
],
"Stop": [
{
"hooks": [
{ "type": "command", "command": "./scripts/agent-hooks/final-check.sh" }
]
}
]
}
}
민감 파일 차단 스크립트 (block-sensitive.sh):
#!/usr/bin/env bash
set -euo pipefail
# stdin으로 훅 이벤트가 JSON으로 들어옴
payload=$(cat)
path=$(echo "$payload" | jq -r '.tool_input.file_path // empty')
case "$path" in
*.env*|*/infra/secrets/*|*/src/generated/*)
echo "BLOCKED: $path는 수정 금지 (CLAUDE.md 참조)" >&2
exit 2 # non-zero exit → 에이전트에 차단 피드백
;;
esac
exit 0
종료 시점 최종 검증 (final-check.sh):
#!/usr/bin/env bash
set -euo pipefail
# 에이전트가 "끝났다" 고 할 때 실제로 빌드/테스트 통과하는지 결정론적으로 확인
if ! pnpm -s check >/tmp/agent-check.log 2>&1; then
echo "FAIL: pnpm check 실패. 아래 로그 참고해서 수정 필요." >&2
tail -50 /tmp/agent-check.log >&2
exit 2
fi
이 두 개만 걸어두면 "시크릿 유출" 과 "거짓 완료 보고" 라는 가장 흔한 두 가지 사고를 막을 수 있다.
단계 3 — 팀 지식을 Skills로 정착
예: code-review 스킬.
---
name: code-review
description: acme-api의 PR을 리뷰할 때 사용. 보안/성능/도메인 규칙을 순서대로 점검.
---
# PR 리뷰 절차
1. 변경된 파일 목록을 `git diff --name-only origin/main...HEAD`로 파악.
2. 다음 체크리스트를 순서대로 적용:
- [ ] `src/routes/**`의 새 엔드포인트에 Zod 스키마가 있는가?
- [ ] 외부 입력이 Prisma raw 쿼리에 직접 들어가지 않는가?
- [ ] `AppError` 외의 throw가 있는가?
- [ ] N+1 쿼리 패턴(반복문 안 `findUnique`)이 있는가?
3. 발견한 이슈는 GitHub 인라인 코멘트 포맷으로 정리:
`file.ts:123 — [BLOCKING|NIT] 설명`
4. 최종적으로 BLOCKING이 없으면 "LGTM with N nits" 형태로 요약.
migration/SKILL.md 도 비슷하게, "Prisma 마이그레이션 생성 → 리뷰 → 롤백 플랜 작성" 절차를 고정해둔다.
단계 4 — MCP로 외부 시스템 연결
.mcp.json 예시:
{
"mcpServers": {
"postgres-readonly": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "$DATABASE_URL_READONLY"],
"env": { "DATABASE_URL_READONLY": "${DB_RO}" }
},
"linear": {
"command": "npx",
"args": ["-y", "@linear/mcp-server"],
"env": { "LINEAR_API_KEY": "${LINEAR_API_KEY}" }
},
"github": {
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "GITHUB_TOKEN", "ghcr.io/github/github-mcp-server"]
}
}
}
핵심은 "쓰기 권한이 필요한가" 를 초기에 구분하는 것. DB는 읽기 전용 커넥션 으로 시작하고, 쓰기는 마이그레이션 도구를 통해서만. Linear/GitHub도 처음엔 read-only 토큰으로 붙였다가 필요해지면 확장.
단계 5 — 관측성: 모든 호출 로깅
format-and-log.sh에 감사 로그를 박는다:
#!/usr/bin/env bash
set -euo pipefail
payload=$(cat)
ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
path=$(echo "$payload" | jq -r '.tool_input.file_path // empty')
tool=$(echo "$payload" | jq -r '.tool_name // empty')
# SQLite 한 줄 삽입 — 나중에 타임라인 재구성용
sqlite3 .claude/audit.db \
"INSERT INTO events(ts, tool, path, payload) VALUES('$ts','$tool','$path', json('$payload'));"
# 동시에 자동 포맷
if [[ "$path" == *.ts || "$path" == *.tsx ]]; then
pnpm biome format --write "$path" >/dev/null 2>&1 || true
fi
나중에 sqlite3 .claude/audit.db 'select ts,tool,path from events order by ts desc limit 50' 만 쳐도 "에이전트가 뭘 했나" 가 보인다. 이게 에이전트 디버거의 최소 형태.
단계 6 — 탐색은 Subagent에 위임
메인 에이전트 프롬프트에 다음 가이드를 넣는다:
"파일 탐색, 심볼 검색, 대량의 읽기 전용 조사는 반드시 Explore 서브에이전트에 위임할 것. 메인 컨텍스트에는 요약만 받는다."
이렇게만 해도 메인 세션 토큰 소비가 체감 50% 이상 줄어든다. 컨텍스트가 깨끗해지니 긴 작업의 말미에도 판단력이 유지 된다.
단계 7 — "평가(eval)" 파이프라인 시작
완벽할 필요 없다. 아래 정도만 갖추면 개선 사이클이 돈다.
evals/
├── cases/
│ ├── 001-add-endpoint/ # 실제 태스크 시나리오
│ │ ├── task.md # 에이전트에게 줄 프롬프트
│ │ ├── repo-state/ # 출발 상태
│ │ └── expected.sh # 성공 판정 스크립트 (테스트 통과 여부 등)
│ └── 002-fix-n-plus-one/
└── run.ts # 케이스를 돌리고 성공률 집계
하네스를 바꿀 때마다(훅 추가, 스킬 수정, 모델 변경) 이 평가를 돌려서 회귀(regression) 를 잡는다. 프롬프트만 만지다가 다른 곳이 깨지는 일이 에이전트 개발에서 정말 자주 일어난다.
최소 구성 로드맵 — "한 달 플랜"
현실적으로 다 한 번에 못 한다. 다음 순서를 추천한다.
- 1주차: CLAUDE.md + 민감 파일 차단 Hook + pnpm check Stop Hook. 이것만으로도 "사고 안 나는 에이전트" 가 된다.
- 2주차: 감사 로그(SQLite) + 자동 포맷 Hook. 관측성 확보.
- 3주차: 가장 반복되는 태스크 1개를 Skill로. (보통 "PR 리뷰" 혹은 "API 엔드포인트 추가")
- 4주차: 읽기 전용 MCP 1개 연결(DB 또는 Linear). 평가 케이스 3개 작성.
이후부터는 평가 성공률을 지표로 하네스를 리팩터링해 나가면 된다. "그냥 프롬프트 고쳐봤더니 좋더라" 의 시대는 지났다.
참고 자료
- Harness engineering for coding agent users — Martin Fowler
- Effective harnesses for long-running agents — Anthropic Engineering
- What is an agent harness in the context of LLMs? — Parallel Web Systems
- The Anatomy of an Agent Harness — Avi Chawla
- Agentic Harness Engineering: LLMs as the New OS — decodingai.com
- The Rise of AI Harness Engineering — Cobus Greyling (Medium)
- Context Engineering for Coding Agents — Martin Fowler
- Building AI Coding Agents for the Terminal — arXiv
- What Is Harness Engineering AI? The Definitive 2026 Guide — Atlan
- Agent Harness Engineering Guide 2026 — QubitTool
'AI Tips' 카테고리의 다른 글
| 클로드(Claude)에서 카톡 보내는 시대가 왔다 — 카카오 PlayMCP 완벽 가이드 (0) | 2026.04.21 |
|---|---|
| Managed Agents: Brain과 Hands를 분리하다 (0) | 2026.04.16 |
| andrej-karpathy-skills 완전 분석: Karpathy의 관찰을 CLAUDE.md 한 파일로 증류하다 (1) | 2026.04.14 |
| Claude-Mem 완전 분석: Claude Code에 장기 기억을 심는 방법 (0) | 2026.04.14 |
| Graphify 완전 분석: AI 코딩 어시스턴트를 위한 지식 그래프 엔진 (1) | 2026.04.14 |