
처음 AI를 사용할 때는 프롬프트를 한 줄씩 입력하고 결과를 받는 것만으로도 충분했다.
이후에는 프로젝트의 컨텍스트 파일을 함께 제공하고 원하는 방향을 설명하면 AI가 알아서 코드를 수정하는 수준까지 발전했고 지금은 에이전트(Agent)의 등장으로 AI가 프로젝트의 파일을 직접 읽고, 생성하고, 수정하는 단계에 이르렀다.
하지만 에이전트가 프로젝트를 직접 탐색하고 수정하기 시작하면서 새로운 문제가 발생했다.
이미 존재하는 컴포넌트를 새로 다시 만들거나, 필요한 설정 파일을 삭제하거나 기존코드를 무시하는 현상이 발생한 것이다.
그리고 이 문제를 해결하기위해 규칙과 컨텍스트를 체계적으로 설계하고 관리하는 하네스 엔지니어링(Harness Engineering)이라는 용어가 나왔다.
즉, AI에게 더 좋은 명령을 내리는 것이 아니라 AI가 코드를 잘 작성할 수밖에 없는 환경을 만드는 것이다.
아래는 내가 구성한 하네스 엔지니어링 폴더구조 예시이다
project-root/
│
├── docs/
│ ├── overview/
│ │ └── tech-stack.md
│ │
│ ├── architecture/
│ │ └── project-structure.md 전체 구조 맵
│ │
│ ├── requirements/ 요구사항 명세
│ │ ├── auth.md
│ │ ├── user.md
│ │ └── payment.md
│ │
│ ├── decisions/ 의사결정 기록
│ │ ├── ADR-001-state-management.md
│ │ ├── ADR-002-api-client.md
│ │ └── ADR-003-auth.md
│ │
│ ├── conventions/ 코딩컨벤션
│ │ ├── frontend.md
│ │ ├── api.md
│ │ ├── testing.md
│ │ └── git.md
│ │
│ ├── examples/ 정답 예제 ai 학습자료 이 프로젝트는 이런 스타일로 작성하는 구나를 배운다
│ │ ├── component.md
│ │ ├── hook.md
│ │ └── api.md
│ │
│ ├── prompts/ 자주 쓰는 프롬프트
│ │
│ ├── templates/ 템플릿 모음
│ │ ├── requirement-template.md
│ │ ├── decision-template.md
│ │ └── feature-template.md
│ │
│ ├── workflows/ AI 작업 절차
│ │
│ └── glossary/ 용어 사전
│
├── AGENTS.md 목차 역할 (~100줄)
│
└── package.json
docs/overview/
tech-stack.md
#tech-stack.md
## Tech Stack
### Framework
* Next.js (App Router)
### Language
* TypeScript
### Styling
* Tailwind CSS
### Form
* React Hook Form
* Zod
### Server State
* TanStack Query
### API
* Axios
### UI
* Design System
* Radix UI Icons
### Documentation
* Storybook
### Testing
* Vitest
* React Testing Library
---
## Development Rules
### Form
* 폼 상태 관리는 React Hook Form 사용
* 검증은 Zod 사용
### API
* HTTP 요청은 Axios 사용
* 서버 상태는 TanStack Query 사용
### UI
* 기존 디자인 시스템 컴포넌트 우선 사용
* 중복 컴포넌트 생성 금지
### Testing
* 기능 구현 시 테스트 코드 함께 작성
* UI 테스트는 React Testing Library 사용
docs/architecture/
project-structure.md
## 폴더 및 파일 구조
### 폴더·레이어
```
src/
├── app/ # 라우팅 전용 — layout.tsx·page.tsx만
│ ├── (main)/
│ └── (admin)/admin/
├── views/ # 페이지 단위 뷰 (라우팅 없음)
├── widgets/ # 여러 feature를 조합한 블록
├── features/ # 도메인 기능 (다른 feature import 금지)
├── shared/ # 어디서나 import 가능, 역방향 금지
│ ├── ui/
│ ├── layout/
│ ├── store/
│ ├── lib/
│ ├── hooks/
│ ├── constants/
│ ├── prompt/
│ ├── types/
│ └── api/
├── hooks/
└── stories/
```
- `app/` — 라우팅만. 비즈니스 로직·UI 금지
- `views/` — 페이지 조합. API·상태 허용
- `features/` — 도메인 로직
- `shared/` — 공용
docs/requirments/
requirments 템플릿 예시
# [기능명]
## 목표
이 기능이 해결하려는 문제는 무엇인가?
---
## 사용자 시나리오
사용자는 무엇을 할 수 있어야 하는가?
예시)
- 사용자는 이메일로 로그인할 수 있다.
- 사용자는 로그인 실패 사유를 확인할 수 있다.
---
## 화면
경로:
입력 요소:
버튼:
표시 정보:
---
## 유효성 검사
입력값 검증 규칙
예시)
- 이메일 형식 검사
- 비밀번호 8자 이상
---
## API
사용 API
Request
Response
---
## 예외 처리
예상되는 오류 상황
예시)
- 네트워크 오류
- 로그인 실패
- 권한 없음
---
## 완료 조건
기능이 완료되었다고 판단하는 기준
예시)
- 로그인 성공 시 대시보드 이동
- 에러 메시지 표시
- 로딩 상태 표시
docs/decisions/
decisions템플릿 예시
# [결정 제목]
날짜
YYYY-MM-DD
상태
제안 | 승인 | 폐기
---
## 결정 내용
무엇을 결정했는가?
---
## 이유
왜 이 결정을 내렸는가?
---
## 고려했던 대안
### 대안 1
장점
-
단점
-
### 대안 2
장점
-
단점
-
---
## 영향 범위
어떤 기능이나 영역에 영향을 주는가?
-
---
## AI 작업 시 주의사항
AI가 반드시 따라야 할 규칙
-
docs/conventions/
frontend.md
# Frontend Convention
## Component
- 컴포넌트는 PascalCase
- Props 타입은 {ComponentName}Props
## Hook
- 커스텀 훅은 use로 시작
## Component Structure
1. Type
2. State
3. Query
4. Hook
5. Handler
6. Effect
7. Return
## Import
- 상대경로 금지
- @ alias 사용
testing convention.md
# Testing Convention
## Framework
- Vitest
- Testing Library
## 테스트 대상
반드시 테스트 작성
- 비즈니스 로직
- 유틸 함수
- Custom Hook
- 인증
- 결제
- 폼 제출
선택 사항
- 단순 UI 컴포넌트
- 스타일 전용 컴포넌트
## 테스트 원칙
- 구현이 아닌 동작 검증
- getByRole 우선 사용
- DOM 구조 의존 금지
- className 의존 금지
## Mock 정책
- 외부 API만 Mock
- 비즈니스 로직 Mock 금지
## AI 작업 시 주의사항
- 테스트 없는 기능 구현 금지
- 새로운 Hook 생성 시 테스트 작성
docs/examples/
storybook.md
#storybook.md
## Storybook 규칙
### Stories 파일
- **파일명**: <컴포넌트명>.stories.tsx
- **Meta 설정**: 명확한 title과 component 지정
- **기본 Story**: Primary 또는 Default 스토리 포함
- **Args 정의**: 모든 Props에 대한 기본값 제공
### Storybook 타이틀
| 위치 | 타이틀 형식 |
| --- | --- |
| `shared/ui/` | `Shared/{ComponentName}` |
| `shared/layout/` | `Layout/{ComponentName}` |
| `features/` | `Features/{ComponentName}` |
### 예시
```tsx
// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta = {
title: 'Components/Button', //storybook 사이드바에 표시될 계층 구조
component: Button, //사용할 컴포넌트
parameters: {
docs: {
canvas: {
withToolbar: false, //문서 뷰에서 상단 툴바 숨기기
},
story: {
inline: false, // true: 페이지의 일부처럼 보여줌, false: iframe을 따로 만들어서 보여줌
height: '200px',
iframeHeight: 200,
},
},
layout: 'centered', //가운데 정렬
},
tags: ['autodocs'], //자동으로 api 문서를 생성하도록 설정
args: {
label: '2',
variant: 'surface',
disabled: true,
loading: false
},
argTypes: {
label: { control: 'text' },
variant: { control: { type: 'select', options: ['primary', 'secondary'] } },
disabled: { control: 'boolean' },
loading: { control: 'boolean' },
},
// decorators: 모든 스토리에 공통으로 적용할 래퍼(Wrapper)
decorators:[
(Story) => (
<Theme>
<Story />
</Theme>
)
]
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Playground: Story = {};
export const Secondary: Story = {
args: {
label: 'Click me',
variant: 'secondary',
},
};
export const Disabled: Story = {
args: {
label: 'Disabled',
disabled: true,
},
};
```
https://openai.com/ko-KR/index/harness-engineering/?utm_source=chatgpt.com
하네스 엔지니어링: 에이전트 우선 세계에서 Codex 활용하기
작성자: Ryan Lopopolo, 기술 스태프 멤버
openai.com
하네스엔지니어링을 해도 그걸 ai가 읽는 지 어떻게 알까?
실제로 이렇게 입력을 해도 ai가 읽지 않는 경우가 있다 그래서
작업 시작 시 이런식으로 명시적으로 주입하는 경우가 많다
@requirements.md
@architecture.md
@coding-rules.md
위 문서를 읽고 작업 시작해.