[Harness Engineering] 도메인별 하네스 템플릿




복사해서 바로 쓸 수 있는 실전 하네스 3종


9편에서 하네스의 물리적 구조를 해부했다. 이 편에서는 실제 도메인에 맞춰 작성된 하네스 템플릿 3종을 제공한다. 각 템플릿은 해당 도메인의 특성을 반영한 완결된 하네스로, 프로젝트에 복사한 후 자신의 맥락에 맞게 수정하여 사용할 수 있다.


템플릿 1: REST API 백엔드

웹 서비스의 백엔드 API를 구축하는 가장 일반적인 시나리오다. Express/NestJS/FastAPI 등 프레임워크에 관계없이 적용할 수 있는 구조다.

디렉토리 구조

my-api/
├── .claude/
│   ├── CLAUDE.md
│   ├── agents/
│   │   ├── api-designer.md
│   │   ├── backend-dev.md
│   │   ├── db-engineer.md
│   │   └── qa-engineer.md
│   └── skills/
│       ├── endpoint-creation/
│       │   └── skill.md
│       └── db-migration/
│           └── skill.md
├── feature-list.json
├── progress.md
├── tests/
│   └── architecture.test.ts
└── .github/workflows/
    └── pr-check.yml

CLAUDE.md

# CLAUDE.md — REST API 백엔드

## 프로젝트 개요
[서비스명]의 백엔드 REST API.
[프레임워크] + TypeScript + [DB] 기반.
[핵심 도메인 한 줄 설명].

## 기술 스택
- Runtime: Node.js 20
- Framework: [Express / NestJS / Fastify]
- ORM: [Prisma / TypeORM / Drizzle]
- DB: [PostgreSQL / MySQL]
- Cache: [Redis] (사용 시)
- Auth: [JWT / Session / OAuth]
- Test: Jest + Supertest

## 디렉토리 구조
src/
├── controllers/    # HTTP 요청 처리, 입력 검증, 응답 포맷팅
├── services/       # 비즈니스 로직 (HTTP 무관)
├── repositories/   # 데이터 접근 (DB 쿼리)
├── models/         # 데이터 모델, 타입 정의
├── middlewares/     # 인증, 에러 핸들링, 로깅
├── validators/     # 입력 검증 스키마
└── utils/          # 공용 유틸리티

## 의존성 규칙
controllers/ → services/, validators/, models/
services/    → repositories/, models/, utils/
repositories/→ models/, utils/
models/      → (의존 없음)
middlewares/ → services/, utils/
validators/  → models/

역방향 의존 금지:
- services/에서 controllers/ import 금지
- repositories/에서 services/ import 금지
- models/에서 어떤 레이어도 import 금지

## 코딩 컨벤션
- 함수명: camelCase
- 파일명: kebab-case.ts
- 클래스명: PascalCase
- HTTP 상태 코드: controllers/에서만 사용
- DB 쿼리: repositories/에서만 실행
- 환경 변수: config/ 모듈을 통해서만 접근

## 에러 처리
- 비즈니스 에러: src/utils/errors.ts의 AppError 상속
  - NotFoundError (404)
  - ValidationError (400)
  - UnauthorizedError (401)
  - ForbiddenError (403)
  - ConflictError (409)
- services/에서 HttpException 사용 금지
- error middleware가 AppError → HTTP 응답 매핑

## API 설계 원칙
- RESTful 네이밍: 복수형 명사 (/users, /projects)
- 페이지네이션: cursor 기반 (?cursor=xxx&limit=20)
- 필터링: 쿼리 파라미터 (?status=active&sort=createdAt:desc)
- 응답 형식: { data: T, meta?: { cursor, hasMore } }
- 에러 형식: { error: { code, message, details? } }

## 테스트
- controllers/: E2E 테스트 (Supertest)
- services/: 단위 테스트 (의존성 mock)
- repositories/: 통합 테스트 (실제 DB)
- 파일 위치: 대상 파일 옆에 *.test.ts
- 커버리지 목표: 라인 80% 이상

## 에이전트 팀
- api-designer: 엔드포인트 설계, OpenAPI 스펙
- backend-dev: 서비스/컨트롤러/리포지토리 구현
- db-engineer: 스키마 설계, 마이그레이션, 쿼리 최적화
- qa-engineer: 테스트 작성, 커버리지 확보

엔드포인트 생성 스킬

# .claude/skills/endpoint-creation/skill.md

## 트리거
"엔드포인트 만들어줘", "API 추가", "CRUD 만들어줘"

## 절차
1. 리소스명과 필드 정의 확인
2. 파일 생성 순서:
   a. models/{resource}.ts — 타입/인터페이스 정의
   b. validators/{resource}.validator.ts — 입력 검증 스키마
   c. repositories/{resource}.repository.ts — DB 접근
   d. services/{resource}.service.ts — 비즈니스 로직
   e. controllers/{resource}.controller.ts — HTTP 핸들러
   f. routes/{resource}.routes.ts — 라우팅

3. 각 파일 작성 시:
   - models/: 순수 타입만, 외부 의존 없음
   - validators/: Zod 스키마, 에러 메시지 한글
   - repositories/: ORM 사용, raw SQL 금지
   - services/: HTTP 관련 코드 금지, AppError 사용
   - controllers/: 검증 → 서비스 호출 → 응답 포맷팅만

4. 테스트 작성:
   - services/*.test.ts (단위)
   - controllers/*.test.ts (E2E)

5. 확인:
   - npm run test:architecture 통과
   - npm run lint 통과
   - npm test 통과

템플릿 2: 프론트엔드 웹 애플리케이션

React/Next.js 기반 프론트엔드 프로젝트를 위한 하네스다. 컴포넌트 설계, 상태 관리, API 연동의 경계를 잡는 데 초점을 둔다.

디렉토리 구조

my-frontend/
├── .claude/
│   ├── CLAUDE.md
│   ├── agents/
│   │   ├── ui-designer.md
│   │   ├── frontend-dev.md
│   │   ├── state-engineer.md
│   │   └── a11y-reviewer.md
│   └── skills/
│       ├── component-creation/
│       │   └── skill.md
│       └── page-creation/
│           └── skill.md
├── feature-list.json
├── progress.md
└── tests/
    └── architecture.test.ts

CLAUDE.md

# CLAUDE.md — 프론트엔드 웹 애플리케이션

## 프로젝트 개요
[서비스명]의 웹 프론트엔드.
Next.js 14 (App Router) + TypeScript + Tailwind CSS.
[핵심 사용자 경험 한 줄 설명].

## 기술 스택
- Framework: Next.js 14 (App Router)
- Language: TypeScript (strict mode)
- Styling: Tailwind CSS + shadcn/ui
- State: Zustand (클라이언트) / TanStack Query (서버)
- Form: React Hook Form + Zod
- Test: Vitest + Testing Library + Playwright

## 디렉토리 구조
src/
├── app/                  # Next.js App Router (라우팅)
│   ├── (auth)/           # 인증 관련 페이지 그룹
│   ├── (dashboard)/      # 대시보드 페이지 그룹
│   └── api/              # Route Handlers (BFF)
├── components/
│   ├── ui/               # shadcn/ui 기반 원자 컴포넌트
│   ├── features/         # 도메인 기능 컴포넌트
│   └── layouts/          # 레이아웃 컴포넌트
├── hooks/                # 커스텀 훅
├── stores/               # Zustand 스토어
├── services/             # API 클라이언트, 외부 서비스 연동
├── types/                # 공유 타입 정의
└── lib/                  # 유틸리티, 헬퍼

## 의존성 규칙
app/          → components/, hooks/, services/, stores/
components/ui/→ types/, lib/ (다른 컴포넌트 금지)
components/features/ → components/ui/, hooks/, services/, stores/, types/
hooks/        → services/, stores/, types/, lib/
stores/       → types/, lib/ (services/ 금지 — 훅에서 연결)
services/     → types/, lib/
types/        → (의존 없음)
lib/          → (의존 없음)

역방향 의존 금지:
- components/ui/에서 features/ import 금지
- services/에서 components/ import 금지
- stores/에서 services/ 직접 import 금지

## 컴포넌트 규칙
- ui/ 컴포넌트: 순수 UI, 비즈니스 로직 없음, props만으로 동작
- features/ 컴포넌트: 도메인 로직 포함 가능, hooks/stores 사용 가능
- 모든 컴포넌트에 displayName 설정
- Props 타입은 컴포넌트 파일 안에서 정의 (별도 파일 금지)
- children을 받는 컴포넌트는 PropsWithChildren 사용

## 상태 관리 원칙
- 서버 상태: TanStack Query (캐싱, 재검증, 낙관적 업데이트)
- 클라이언트 상태: Zustand (UI 상태, 폼 외의 로컬 상태)
- 폼 상태: React Hook Form (유효성 검증은 Zod)
- URL 상태: Next.js searchParams (필터, 페이지네이션)
- 전역 상태 남용 금지: "이 상태가 정말 전역이어야 하는가?" 확인

## 스타일링 규칙
- Tailwind 유틸리티 클래스 사용
- 인라인 style 속성 금지
- 커스텀 CSS 최소화 (글로벌 스타일은 globals.css에만)
- 반응형: mobile-first (sm → md → lg → xl)
- 다크 모드: Tailwind dark: 접두사 사용

## 접근성 (a11y)
- 모든 img에 alt 속성 필수
- 인터랙티브 요소에 aria-label 필수 (텍스트 없는 경우)
- 키보드 내비게이션 지원
- 색상 대비 WCAG AA 이상

## 테스트
- 컴포넌트: Vitest + Testing Library (사용자 관점 테스트)
- 훅: renderHook으로 테스트
- E2E: Playwright (핵심 사용자 흐름만)
- 파일 위치: 대상 파일 옆에 *.test.tsx
- 커버리지 목표: 라인 75% 이상

## 에이전트 팀
- ui-designer: 컴포넌트 설계, UI/UX 구조
- frontend-dev: 페이지/컴포넌트/훅 구현
- state-engineer: 상태 관리, API 연동, 캐싱 전략
- a11y-reviewer: 접근성 검수, 키보드/스크린리더 테스트

컴포넌트 생성 스킬

# .claude/skills/component-creation/skill.md

## 트리거
"컴포넌트 만들어줘", "UI 추가", "화면 만들어줘"

## 분류
먼저 컴포넌트 유형을 판별한다:

1. UI 컴포넌트 (components/ui/)
   - 비즈니스 로직 없음, props만으로 동작
   - 예: Button, Input, Card, Modal, Badge

2. Feature 컴포넌트 (components/features/)
   - 특정 도메인에 종속
   - hooks, stores, services 사용 가능
   - 예: TaskCard, ProjectList, UserProfile

## UI 컴포넌트 작성 규칙
```tsx
// components/ui/button.tsx

import { cva, type VariantProps } from 'class-variance-authority';
import { forwardRef, type ButtonHTMLAttributes } from 'react';
import { cn } from '@/lib/utils';

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2',
  {
    variants: {
      variant: {
        primary: 'bg-blue-600 text-white hover:bg-blue-700',
        secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
        destructive: 'bg-red-600 text-white hover:bg-red-700',
        ghost: 'hover:bg-gray-100',
      },
      size: {
        sm: 'h-8 px-3 text-sm',
        md: 'h-10 px-4 text-sm',
        lg: 'h-12 px-6 text-base',
      },
    },
    defaultVariants: { variant: 'primary', size: 'md' },
  }
);

interface ButtonProps
  extends ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  isLoading?: boolean;
}

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, isLoading, children, disabled, ...props }, ref) => (
    <button
      ref={ref}
      className={cn(buttonVariants({ variant, size }), className)}
      disabled={disabled || isLoading}
      {...props}
    >
      {isLoading ? <Spinner className="mr-2 h-4 w-4" /> : null}
      {children}
    </button>
  )
);
Button.displayName = 'Button';
export { Button, buttonVariants };

Feature 컴포넌트 작성 규칙

  • ‘use client’ 지시문 필요 여부 판단
  • 상태 관리 훅은 컴포넌트 최상위에서만 호출
  • 로딩/에러/빈 상태 모두 처리
  • 컴포넌트 당 JSX 50줄 초과 시 하위 컴포넌트로 분리

---

## 템플릿 3: 데이터 파이프라인

데이터 수집, 변환, 적재(ETL) 파이프라인을 위한 하네스다. Python 기반이며, 데이터 품질 검증과 멱등성에 초점을 둔다.

### CLAUDE.md

```markdown
# CLAUDE.md — 데이터 파이프라인

## 프로젝트 개요
[데이터 소스]에서 데이터를 수집하여 [데이터 웨어하우스]에 적재하는 ETL 파이프라인.
Python + [Airflow / Prefect / Dagster] 기반.

## 기술 스택
- Language: Python 3.12
- Orchestrator: [Airflow / Prefect / Dagster]
- Data Validation: Pydantic + Great Expectations
- DB: [BigQuery / Snowflake / PostgreSQL]
- Storage: [S3 / GCS]
- Test: pytest + pytest-mock
- Linter: ruff + mypy

## 디렉토리 구조
src/
├── extractors/         # 데이터 소스별 추출 로직
│   ├── base.py         # BaseExtractor 추상 클래스
│   ├── api_extractor.py
│   ├── db_extractor.py
│   └── file_extractor.py
├── transformers/       # 데이터 변환 로직
│   ├── base.py         # BaseTransformer 추상 클래스
│   ├── cleaning.py
│   ├── enrichment.py
│   └── aggregation.py
├── loaders/            # 데이터 적재 로직
│   ├── base.py         # BaseLoader 추상 클래스
│   ├── bigquery_loader.py
│   └── file_loader.py
├── validators/         # 데이터 품질 검증
│   ├── schemas.py      # Pydantic 모델
│   └── expectations.py # Great Expectations 스위트
├── pipelines/          # 파이프라인 정의 (DAG)
│   ├── daily_sync.py
│   └── hourly_metrics.py
├── models/             # 데이터 모델, 타입 정의
└── utils/              # 공용 유틸리티
    ├── config.py
    ├── logging.py
    └── retry.py

## 의존성 규칙
extractors/   → models/, utils/, validators/
transformers/ → models/, utils/, validators/
loaders/      → models/, utils/, validators/
validators/   → models/
pipelines/    → extractors/, transformers/, loaders/, validators/
models/       → (의존 없음)
utils/        → (의존 없음)

역방향 의존 금지:
- extractors/에서 transformers/, loaders/ import 금지
- transformers/에서 extractors/, loaders/ import 금지
- models/에서 어떤 레이어도 import 금지

## 핵심 원칙

### 멱등성
- 모든 파이프라인은 같은 입력에 대해 같은 결과를 보장
- 적재 시 MERGE/UPSERT 사용 (INSERT만 금지)
- 재실행 시 중복 데이터가 생기지 않아야 함

### 데이터 검증
- 추출 직후: 스키마 검증 (Pydantic)
- 변환 직후: 비즈니스 규칙 검증 (Great Expectations)
- 적재 직후: row count 및 null 비율 확인

### 에러 처리
- 개별 레코드 실패가 전체 파이프라인을 중단시키지 않음
- 실패 레코드는 dead_letter_queue에 저장
- 임계값 초과 시 (전체의 5% 이상 실패) 파이프라인 중단

### 로깅
- 각 단계의 입력/출력 row count 기록
- 처리 시간 기록
- 에러 발생 시 원인 레코드 샘플 (최대 5건) 기록
- print() 금지 → structlog 사용

## 코딩 컨벤션
- Type hints 필수 (mypy strict mode)
- 함수명: snake_case
- 클래스명: PascalCase
- 상수: UPPER_SNAKE_CASE
- docstring: Google 스타일
- 최대 라인 길이: 120

## 테스트
- extractors/: mock 외부 소스 + 스키마 검증 테스트
- transformers/: 입력→출력 매핑 테스트 (순수 함수)
- loaders/: testcontainers 또는 mock DB
- validators/: 유효/무효 데이터 경계값 테스트
- 파이프라인: 통합 테스트 (소규모 샘플 데이터)
- 커버리지 목표: 라인 85% 이상

## 에이전트 팀
- data-architect: 파이프라인 설계, 스키마 설계, 데이터 모델링
- pipeline-dev: Extractor/Transformer/Loader 구현
- data-qa: 데이터 품질 검증, 테스트 작성
- infra-engineer: Airflow DAG, 스케줄링, 모니터링

데이터 검증 스킬

# .claude/skills/data-validation/skill.md

## 트리거
"데이터 검증 추가", "스키마 만들어줘", "데이터 품질 체크"

## 절차
1. 데이터 소스의 필드 목록과 타입 확인
2. Pydantic 모델 작성 (스키마 검증)
3. Great Expectations 스위트 작성 (비즈니스 규칙)
4. 파이프라인의 적절한 위치에 검증 삽입
5. 실패 시 처리 로직 구현 (dead letter queue)

## Pydantic 모델 템플릿
```python
from pydantic import BaseModel, Field, field_validator
from datetime import datetime

class SalesRecord(BaseModel):
    transaction_id: str = Field(..., min_length=1, max_length=50)
    amount: float = Field(..., gt=0, le=1_000_000)
    currency: str = Field(..., pattern=r'^[A-Z]{3}$')
    customer_id: str
    timestamp: datetime

    @field_validator('timestamp')
    @classmethod
    def timestamp_not_future(cls, v: datetime) -> datetime:
        if v > datetime.now():
            raise ValueError('미래 시점의 거래는 허용되지 않음')
        return v

    class Config:
        str_strip_whitespace = True

Great Expectations 스위트 템플릿

def create_sales_expectations(context):
    suite = context.add_expectation_suite("sales_validation")

    suite.add_expectation(
        expect_column_values_to_not_be_null("transaction_id")
    )
    suite.add_expectation(
        expect_column_values_to_be_between("amount", min_value=0, max_value=1000000)
    )
    suite.add_expectation(
        expect_column_values_to_be_in_set("currency", ["USD", "EUR", "KRW", "JPY"])
    )
    suite.add_expectation(
        expect_column_pair_values_to_be_unique(["transaction_id", "timestamp"])
    )

    return suite

검증 삽입 위치

Extract → [스키마 검증] → Transform → [비즈니스 규칙 검증] → Load → [적재 후 확인]

---

## 템플릿 활용법

세 가지 템플릿은 각 도메인의 시작점이다. 활용할 때의 원칙:

  1. 복사 후 커스터마이징
    • 템플릿을 그대로 쓰지 않는다
    • 프로젝트의 기술 스택, 규모, 팀 구성에 맞게 수정한다
    • 사용하지 않는 에이전트나 스킬은 제거한다
  2. 점진적 확장
    • CLAUDE.md만 먼저 적용하고 효과를 확인한다
    • 필요에 따라 에이전트 정의를 추가한다
    • 스킬은 반복되는 작업 패턴이 보일 때 추가한다
  3. 팀에 맞게 진화
    • 에이전트가 반복적으로 실수하는 영역에 규칙을 추가한다
    • 사용하지 않는 규칙은 정리한다
    • feature-list.json은 프로젝트 초기에 가장 유용하다
  4. 도메인을 혼합할 수 있다
    • 풀스택 프로젝트: 백엔드 + 프론트엔드 템플릿 조합
    • 데이터 기반 서비스: API + 데이터 파이프라인 조합
    • 모노레포: 디렉토리별로 다른 CLAUDE.md 적용

하네스는 프레임워크가 아니라 패턴이다. 정해진 형태가 있는 것이 아니라, 프로젝트의 맥락에 맞게 구성하는 것이다. 이 템플릿들이 그 출발점이 되기를 바란다.




댓글 남기기