[Harness Engineering] 단계별 구축 가이드




개인 프로젝트에서 조직 규모까지


하네스 엔지니어링의 네 가지 기둥 — 컨텍스트 엔지니어링, 아키텍처 제약, 엔트로피 관리, 피드백 루프 — 을 모두 살펴봤다. 이론과 코드 예시를 충분히 다뤘지만, 현실적인 질문이 남는다. “좋은 건 알겠는데, 어디서부터 시작하지?”

모든 것을 한꺼번에 도입하려고 하면 실패한다. 하네스 자체가 과도하게 복잡해져서, 정작 제품 개발에 집중하지 못하는 역설이 생긴다. 가장 효과적인 접근은 규모에 맞게 단계적으로 도입하는 것이다.


Level 1: 개인 프로젝트 — 30분이면 충분하다

가장 가성비 좋은 시작점이다. 최소한의 설정으로 에이전트의 결과물 품질이 체감될 정도로 달라진다.

해야 할 것: 3가지만

첫째, AGENTS.md(또는 CLAUDE.md)를 작성한다.

# CLAUDE.md

## 프로젝트 개요
Express + TypeScript + PostgreSQL 기반 REST API 서버.
사용자 인증, 상품 관리, 주문 처리 기능을 제공한다.

## 디렉토리 구조
src/
├── routes/       # Express 라우터
├── services/     # 비즈니스 로직
├── repositories/ # DB 접근
├── middlewares/   # 인증, 에러 핸들링
├── types/        # 타입 정의
└── utils/        # 공용 유틸리티

## 코딩 컨벤션
- 함수명: camelCase
- 파일명: kebab-case.ts
- 타입/인터페이스: PascalCase (I 접두사 사용하지 않음)
- any 금지 → unknown + 타입 가드
- console.log 금지 → src/utils/logger.ts 사용

## 에러 처리
- 커스텀 에러 클래스: src/utils/errors.ts의 AppError 상속
- routes에서 try-catch 하지 않음 → error middleware가 전역 처리
- DB 에러는 repository에서 AppError로 변환

## 테스트
- 테스트 파일: 대상 파일 옆에 *.test.ts
- jest 사용
- DB 테스트는 testcontainers로 실제 PostgreSQL 사용

## 현재 상태
- 인증(회원가입/로그인/JWT) 완료
- 상품 CRUD 완료
- 주문 처리 진행 중

처음부터 완벽할 필요 없다. 프로젝트의 현재 상태와 핵심 규칙만 적어도 에이전트의 결과물이 확연히 달라진다. 에이전트는 이 파일을 읽고 “아, Express 프로젝트구나, 에러는 AppError를 쓰고, console.log 대신 logger를 쓰는구나”를 파악한다.

둘째, pre-commit 훅을 설정한다.

# husky 설치 및 설정
npm install -D husky lint-staged
npx husky init

# pre-commit 훅 작성
cat > .husky/pre-commit << 'EOF'
npx lint-staged
npx tsc --noEmit
EOF
// package.json에 추가
{
  "lint-staged": {
    "src/**/*.ts": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}

이것만으로도 에이전트가 스타일 규칙을 위반하는 코드를 커밋할 수 없게 된다. 타입 에러도 커밋 단계에서 차단된다.

셋째, 기존 테스트를 유지한다.

새 테스트를 작성하라는 것이 아니다. 이미 있는 테스트가 에이전트의 코드 변경으로 깨지지 않게 CI에서 실행되도록 만들면 된다.

// package.json
{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch"
  }
}

Level 1 체크리스트

□ CLAUDE.md (또는 AGENTS.md) 작성 — 프로젝트 개요, 구조, 컨벤션
□ husky + lint-staged 설정 — 코드 스타일과 타입 검사 강제
□ npm test가 CI에서 실행되도록 설정

소요 시간: 약 30분
효과: 에이전트 결과물의 일관성이 즉시 개선됨

Level 2: 소규모 팀 (2~5명) — 경계를 세운다

개인 프로젝트에서 검증된 하네스를 팀 수준으로 확장한다. 핵심은 여러 에이전트(또는 여러 사람이 사용하는 에이전트)가 동시에 작업해도 일관성이 유지되는 구조를 만드는 것이다.

아키텍처 테스트 추가

Level 1에서는 컨벤션만 강제했다면, Level 2에서는 아키텍처 경계를 강제한다.

// tests/architecture.test.ts

const LAYER_RULES: Record<string, string[]> = {
  'src/routes':       ['src/services'],
  'src/services':     ['src/repositories', 'src/types', 'src/utils'],
  'src/repositories': ['src/types', 'src/utils'],
  'src/types':        [],
  'src/utils':        [],
};

describe('Architecture', () => {
  for (const [layer, allowedDeps] of Object.entries(LAYER_RULES)) {
    test(`${layer}는 허용된 의존성만 가져야 한다`, () => {
      const files = getTypeScriptFiles(layer);
      const allLayers = Object.keys(LAYER_RULES);
      const forbiddenLayers = allLayers.filter(
        l => l !== layer && !allowedDeps.includes(l)
      );

      for (const file of files) {
        const imports = extractImports(file);
        for (const forbidden of forbiddenLayers) {
          const violations = imports.filter(imp =>
            imp.includes(forbidden.replace('src/', ''))
          );
          expect(violations).toHaveLength(0);
        }
      }
    });
  }
});
# .husky/pre-commit — 아키텍처 검증 추가
npx lint-staged
npx tsc --noEmit
npm run test:architecture -- --silent

PR 검증 파이프라인

# .github/workflows/pr-check.yml

name: PR Check
on:
  pull_request:
    branches: [main, develop]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci

      - name: 타입 검사
        run: npx tsc --noEmit

      - name: 린터
        run: npx eslint src/ --max-warnings 0

      - name: 아키텍처 테스트
        run: npm run test:architecture

      - name: 단위 테스트 + 커버리지
        run: npm test -- --coverage --coverageThreshold='{"global":{"lines":70}}'

      - name: 빌드 확인
        run: npm run build

계층적 AGENTS.md

프로젝트 루트/
├── CLAUDE.md                     # 전체 프로젝트 규칙
├── src/
│   ├── routes/
│   │   └── CLAUDE.md             # "services만 import 가능, req/res 파싱은 여기서"
│   ├── services/
│   │   └── CLAUDE.md             # "비즈니스 로직만, HTTP 관련 코드 금지"
│   └── repositories/
│       └── CLAUDE.md             # "SQL 쿼리는 여기서만, ORM 엔티티 외부 노출 금지"

레이어별 CLAUDE.md 예시:

# src/services/CLAUDE.md

## 이 디렉토리의 역할
비즈니스 로직을 담당한다. HTTP 요청/응답과 무관하게 동작해야 한다.

## 규칙
- Request, Response 객체를 import하지 않는다
- HTTP 상태 코드를 반환하지 않는다
- repositories/만 import 가능하다 (routes, middlewares 금지)
- 반환 타입은 항상 도메인 객체 또는 DTO
- 외부 API 호출은 하지 않는다 (infrastructure 레이어가 담당)

## 에러 처리
- DomainError를 상속받는 구체적 에러 클래스 사용
- 예: OrderNotFoundError, InsufficientStockError
- HTTP 매핑은 error middleware가 담당하므로 여기서 하지 않는다

Level 2 체크리스트

□ 아키텍처 테스트 작성 — 레이어 간 의존성 방향 강제
□ pre-commit에 아키텍처 테스트 추가
□ GitHub Actions PR 검증 파이프라인 설정
□ 레이어별 CLAUDE.md 작성
□ 커버리지 임계값 설정 (최소 70%)

소요 시간: 반나절 ~ 하루
효과: 여러 에이전트가 동시에 작업해도 아키텍처 일관성 유지

Level 3: 조직 규모 (5명 이상) — 자동화를 자동화한다

팀이 커지면 에이전트 수도 늘어나고, 코드 변경 속도도 빨라진다. Level 2의 구조만으로는 엔트로피 증가를 감당하기 어렵다. 자동화된 관찰, 정리, 보고 체계가 필요하다.

엔트로피 모니터링

// scripts/entropy-score.ts — 코드베이스 건강도 점수 산출

interface EntropyReport {
  timestamp: string;
  scores: {
    duplication: number;
    deadCode: number;
    docConsistency: number;
    testCoverage: number;
    circularDeps: number;
  };
  overallHealth: 'healthy' | 'warning' | 'critical';
}

function generateReport(): EntropyReport {
  const scores = {
    duplication: calculateDuplicationScore(),
    deadCode: calculateDeadCodeScore(),
    docConsistency: runDocCodeConsistency(),
    testCoverage: calculateTestCoverage(),
    circularDeps: countCircularDeps(),
  };

  let health: EntropyReport['overallHealth'] = 'healthy';
  if (scores.duplication > 10 || scores.deadCode > 15 ||
      scores.testCoverage < 60 || scores.circularDeps > 0) {
    health = 'critical';
  } else if (scores.duplication > 5 || scores.deadCode > 8 ||
             scores.testCoverage < 75) {
    health = 'warning';
  }

  return { timestamp: new Date().toISOString(), scores, overallHealth: health };
}

건강도 게이트

# CI에 추가 — critical이면 머지 차단

  entropy-gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20', cache: 'npm' }
      - run: npm ci
      - name: 건강도 확인
        run: |
          HEALTH=$(npx ts-node scripts/entropy-score.ts | jq -r '.overallHealth')
          if [ "$HEALTH" = "critical" ]; then
            echo "❌ 코드베이스 건강도: critical — 머지 차단"
            exit 1
          fi

E2E 테스트 및 성능 게이트

  e2e-and-performance:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_DB: testdb
          POSTGRES_PASSWORD: testpass
        ports:
          - 5432:5432
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20', cache: 'npm' }
      - run: npm ci
      - run: npm run dev &
        env:
          DATABASE_URL: postgres://postgres:testpass@localhost:5432/testdb
      - run: npx wait-on http://localhost:3000/health -t 30000
      - name: E2E 테스트
        run: npm run test:e2e
      - name: 성능 게이트
        run: |
          RESPONSE_TIME=$(curl -s -o /dev/null -w '%{time_total}' http://localhost:3000/health)
          if (( $(echo "$RESPONSE_TIME > 0.8" | bc -l) )); then
            echo "❌ 응답 시간 ${RESPONSE_TIME}s — 임계값 0.8s 초과"
            exit 1
          fi

Level 3 체크리스트

□ Level 1 + Level 2의 모든 항목
□ 엔트로피 점수 산출 스크립트
□ 엔트로피 히스토리 추적 (트렌드 확인)
□ E2E 테스트 파이프라인
□ 성능 게이트 (응답 시간 임계값)
□ 건강도 게이트 (critical이면 머지 차단)
□ 주간 엔트로피 리포트 자동 생성
□ 관찰 가능성 스택 (선택: Prometheus + Loki)

소요 시간: 2~3일
효과: 코드베이스의 건강 상태를 정량적으로 추적하고 자동 정리

단계 전환의 신호

Level을 올려야 할 때를 어떻게 아는가?

Level 1 → Level 2:
  - 2명 이상이 같은 저장소에서 에이전트를 사용하기 시작할 때
  - 에이전트가 만든 코드끼리 스타일이 달라지기 시작할 때
  - "이 규칙 CLAUDE.md에 적혀있는데 왜 안 지키지?" 하는 순간

Level 2 → Level 3:
  - PR이 하루에 10개 이상 머지될 때
  - "이 함수 어디서 본 것 같은데" 하는 빈도가 늘어날 때
  - 코드 리뷰에서 아키텍처 관련 코멘트가 반복될 때
  - 테스트는 통과하는데 실제 서비스에서 문제가 발생할 때

핵심은 과도하게 앞서가지 않는 것이다. 혼자 사이드 프로젝트를 하면서 Level 3을 구축하는 것은 오버엔지니어링이다. 반대로, 10명이 동시에 에이전트를 돌리면서 Level 1에 머물러 있으면 카오스가 된다. 현재 규모에서 가장 아픈 문제를 해결하는 수준으로 도입하고, 문제가 바뀌면 하네스도 진화시킨다.




댓글 남기기