장기 실행 AI 에이전트 개발 가이드 - k82022603/k82022603.github.io GitHub Wiki

장기 실행 AI 에이전트 개발 가이드 #

목차


개요

장기 실행 AI 에이전트는 수 시간에서 며칠에 걸쳐 복잡한 작업을 자율적으로 수행할 수 있는 시스템입니다. Anthropic의 Claude Agent SDK는 이러한 장기 실행 에이전트를 구축하기 위한 효과적인 하네스(harness)를 제공합니다. 하네스란 에이전트가 안정적으로 작동할 수 있도록 지원하는 외부 구조와 시스템을 의미합니다.

왜 중요한가?

전통적인 AI 에이전트는 단일 컨텍스트 윈도우 내에서만 작동하며, 세션이 종료되면 모든 작업 기록을 잃어버립니다. 이는 마치 매 교대마다 이전 작업 내용을 전혀 모르는 새로운 엔지니어가 투입되는 것과 같습니다. 장기 프로젝트를 완수하기 위해서는 세션 간 격차를 메울 수 있는 메커니즘이 필요합니다.


핵심 문제와 해결책

주요 실패 패턴

장기 실행 에이전트에서 흔히 발생하는 두 가지 치명적인 실패 패턴이 있습니다.

1. One-Shot 실패 에이전트가 한 번에 모든 것을 끝내려다 컨텍스트 윈도우를 초과하여 작업 중간에 멈추는 현상입니다. 이 경우 다음 세션의 에이전트는 이전에 무엇을 했는지 추측해야 하며, 명확한 인계가 불가능합니다.

2. Early Victory (조기 승리 선언) 일부 기능이 구현된 후 에이전트가 프로젝트를 완료했다고 잘못 판단하는 현상입니다. 실제로는 더 많은 작업이 남아있음에도 불구하고 작업을 중단합니다.

Anthropic의 해결책

Anthropic은 이러한 문제를 해결하기 위해 이중 에이전트 패턴을 개발했습니다.

  • Initializer Agent: 첫 세션에서 환경을 설정하고 작업 범위를 명확히 정의
  • Coding Agent: 이후 세션에서 점진적으로 기능을 구현하고 명확한 인계 자료 생성

이 패턴의 핵심은 작업을 작은 단위로 쪼개고, 구조화된 체크리스트를 통해 범위를 강제하는 것입니다.


아키텍처 패턴

프로젝트 구조

my_project/
├── feature_list.json          # 작업의 단일 진실 공급원(SSOT)
├── app_spec.txt               # 애플리케이션 사양
├── init.sh                    # 환경 설정 스크립트
├── claude-progress.txt        # 세션별 진행 로그
├── .claude_settings.json      # 보안 설정
├── .git/                      # Git 저장소
└── [application files]        # 생성된 애플리케이션 코드

핵심 구성 요소

1. feature_list.json 모든 작업의 기준이 되는 JSON 파일입니다. 각 기능은 다음과 같은 구조를 가집니다.

{
  "features": [
    {
      "id": 1,
      "title": "사용자 로그인 구현",
      "description": "이메일과 비밀번호를 사용한 기본 로그인 기능",
      "steps": [
        "로그인 폼 UI 생성",
        "백엔드 인증 API 구현",
        "세션 관리 추가",
        "에러 핸들링 구현"
      ],
      "passes": false,
      "priority": "high"
    }
  ]
}

2. claude-progress.txt 각 세션의 작업 내용을 기록하는 로그 파일입니다. 다음 세션의 에이전트가 이 파일을 읽어 이전 작업 내용을 파악합니다.

=== Session 2024-01-15 14:30 ===
Completed: Feature #1 - User Login
- Implemented login form with email/password validation
- Added JWT authentication to backend
- Tested with Puppeteer automation
- Committed: "feat: implement user login system"

Next: Feature #2 - User Registration

3. Git Repository Git은 에이전트의 영구 메모리 역할을 합니다. 각 기능 구현 후 상세한 커밋 메시지와 함께 변경사항을 저장합니다.


Initializer Agent

역할과 책임

Initializer Agent는 프로젝트의 첫 세션에서 실행되며, 이후 모든 Coding Agent가 효과적으로 작업할 수 있는 환경을 구축합니다.

주요 작업

1. 사양 분석

# app_spec.txt 파일을 읽고 요구사항 파악
cat app_spec.txt

2. feature_list.json 생성 애플리케이션 사양을 바탕으로 200개 이상의 상세한 테스트 케이스를 작성합니다. 이 과정은 10-20분 정도 소요될 수 있으며, 에이전트가 멈춘 것처럼 보일 수 있지만 정상적인 동작입니다.

{
  "features": [
    {
      "id": 1,
      "title": "홈페이지 레이아웃",
      "description": "반응형 홈페이지 레이아웃 구현",
      "steps": ["헤더 생성", "네비게이션 바", "푸터 구현"],
      "passes": false
    },
    // ... 199개 더
  ]
}

3. 프로젝트 구조 설정

# Git 저장소 초기화
git init

# 기본 디렉토리 구조 생성
mkdir -p src/{frontend,backend,tests}

# 환경 설정 스크립트 생성
cat > init.sh << 'EOF'
#!/bin/bash
npm install
npm run dev
EOF
chmod +x init.sh

# 첫 커밋
git add .
git commit -m "Initial setup: project structure and feature list"

4. 보안 설정

// .claude_settings.json
{
  "allowed_commands": [
    "npm", "node", "git", "cat", "ls",
    "mkdir", "cp", "mv", "rm"
  ],
  "blocked_patterns": [
    "rm -rf /",
    "sudo",
    "chmod 777"
  ]
}

Coding Agent

운영 원칙

Coding Agent는 매 세션마다 단 하나의 기능만 구현합니다. 이는 작업을 관리 가능한 크기로 유지하고, 각 세션이 명확한 결과물을 남기도록 보장합니다.

작업 흐름

1. 컨텍스트 수집

# 1. 현재 위치 확인
pwd

# 2. 프로젝트 구조 파악
ls -la

# 3. 사양 파일 읽기
cat app_spec.txt

# 4. 기능 목록 확인
cat feature_list.json | head -50

# 5. 이전 세션 진행 상황 확인
cat claude-progress.txt

# 6. Git 히스토리 확인
git log --oneline -20

# 7. 남은 작업 카운트
cat feature_list.json | grep '"passes": false' | wc -l

2. 기능 선택 feature_list.json에서 "passes": false인 최우선 순위 기능을 선택합니다.

3. 개발 서버 실행

# init.sh 스크립트로 자동 실행
./init.sh

# 또는 수동으로
npm install
npm run dev

4. 기능 구현 선택한 기능의 모든 단계를 완료합니다.

// 예: 로그인 기능 구현
// src/components/LoginForm.jsx
import React, { useState } from 'react';

export default function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const response = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email, password })
      });
      // 로그인 로직...
    } catch (err) {
      setError('로그인 실패');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* 폼 필드... */}
    </form>
  );
}

5. E2E 테스트 Puppeteer를 사용하여 실제 사용자 시나리오를 테스트합니다.

// tests/e2e/login.test.js
const puppeteer = require('puppeteer');

describe('Login Feature', () => {
  let browser;
  let page;

  beforeAll(async () => {
    browser = await puppeteer.launch();
    page = await browser.newPage();
  });

  test('사용자가 올바른 자격증명으로 로그인할 수 있어야 함', async () => {
    await page.goto('http://localhost:3000/login');
    
    // 이메일 입력
    await page.type('#email', '[email protected]');
    
    // 비밀번호 입력
    await page.type('#password', 'password123');
    
    // 로그인 버튼 클릭
    await page.click('button[type="submit"]');
    
    // 대시보드로 리다이렉트 확인
    await page.waitForNavigation();
    expect(page.url()).toContain('/dashboard');
    
    // 스크린샷 저장
    await page.screenshot({ 
      path: 'verification/login-success.png' 
    });
  });

  afterAll(async () => {
    await browser.close();
  });
});

6. Git 커밋

git add .
git commit -m "feat: implement user login feature (#1)

- Created LoginForm component with email/password inputs
- Implemented authentication API endpoint
- Added JWT session management
- Tested with E2E automation using Puppeteer
- Verified UI with screenshots in verification/ directory
- Updated feature_list.json: marked feature #1 as passing

Tests: All E2E tests passing
Screenshots: verification/login-*.png"

7. feature_list.json 업데이트 테스트가 완전히 통과한 후에만 passes 필드를 true로 변경합니다.

{
  "id": 1,
  "title": "사용자 로그인 구현",
  "passes": true,  // ← 테스트 통과 후에만 변경
  "completed_at": "2024-01-15T14:45:00Z"
}

8. 진행 상황 기록

# claude-progress.txt에 세션 요약 추가
echo "
=== Session $(date '+%Y-%m-%d %H:%M') ===
Completed: Feature #1 - User Login
Status: ✓ All tests passing
Time spent: ~45 minutes
Next: Feature #2 - User Registration
" >> claude-progress.txt

세션 시작 루틴

매 세션 시작 시 Coding Agent는 일관된 루틴을 따릅니다. 이는 "어제 뭐 했지?" 문제를 완전히 해결합니다.

표준 체크리스트

#!/bin/bash
# session-start.sh - 세션 시작 루틴

echo "=== 세션 시작 루틴 ==="

# 1. 위치 확인
echo "1. 작업 디렉토리 확인"
pwd

# 2. 프로젝트 현황 파악
echo "2. 프로젝트 구조 확인"
ls -la

# 3. 사양 리뷰
echo "3. 애플리케이션 사양 검토"
head -20 app_spec.txt

# 4. 진행 로그 확인
echo "4. 이전 세션 진행 상황"
tail -50 claude-progress.txt

# 5. Git 히스토리
echo "5. 최근 커밋 히스토리"
git log --oneline -10

# 6. 다음 작업 식별
echo "6. 다음 미완료 기능 찾기"
node -pe "
  const features = require('./feature_list.json').features;
  const next = features.find(f => !f.passes);
  next ? \`Feature #\${next.id}: \${next.title}\` : 'All done!'
"

# 7. 개발 환경 실행
echo "7. 개발 서버 시작"
./init.sh &

# 8. 기본 테스트 실행
echo "8. 기존 테스트 실행"
npm test

echo "=== 루틴 완료 ==="

품질 관리 시스템

3단계 검증 프로세스

1. Rule-Based 검증 코드가 기본 규칙을 준수하는지 자동으로 확인합니다.

// tools/validators.js
class FeatureValidator {
  validate(code, tests) {
    const checks = {
      hasTests: tests.length > 0,
      hasDocumentation: code.includes('/**'),
      hasErrorHandling: code.includes('try') || code.includes('catch'),
      followsNaming: this.checkNamingConvention(code),
      hasTypeChecks: code.includes('PropTypes') || code.includes('TypeScript')
    };
    
    return Object.values(checks).every(Boolean);
  }
}

2. Visual 검증 Puppeteer를 통해 실제 UI를 테스트하고 스크린샷을 저장합니다.

// tests/visual/feature-verification.js
async function verifyFeature(featureId, scenarios) {
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  const results = [];

  for (const scenario of scenarios) {
    try {
      // 시나리오 실행
      await scenario.run(page);
      
      // 스크린샷 저장
      const screenshotPath = `verification/feature-${featureId}-${scenario.name}.png`;
      await page.screenshot({ path: screenshotPath, fullPage: true });
      
      // 결과 기록
      results.push({
        scenario: scenario.name,
        passed: true,
        screenshot: screenshotPath
      });
    } catch (error) {
      results.push({
        scenario: scenario.name,
        passed: false,
        error: error.message
      });
    }
  }

  await browser.close();
  return results;
}

3. LLM 검증 (선택사항) 복잡한 경우 Claude를 사용하여 추가 검증을 수행할 수 있습니다.

# tools/llm_validator.py
from claude_agent_sdk import query, ClaudeAgentOptions

async def validate_implementation(feature_desc, code, test_results):
    prompt = f"""
    다음 기능 구현을 검증해주세요:
    
    기능 설명: {feature_desc}
    
    구현 코드:
    ```
    {code}
    ```
    
    테스트 결과:
    {test_results}
    
    다음 기준으로 평가해주세요:
    1. 기능이 사양을 완전히 충족하는가?
    2. 코드 품질이 프로덕션 수준인가?
    3. 엣지 케이스가 처리되었는가?
    4. 보안 문제가 없는가?
    
    JSON 형식으로 응답:
    {{
      "approved": true/false,
      "issues": ["문제1", "문제2"],
      "recommendations": ["권장사항1"]
    }}
    """
    
    async for message in query(prompt=prompt):
        if hasattr(message, 'result'):
            return json.loads(message.result)

커밋 메시지 템플릿

명확한 커밋 메시지는 미래 세션의 에이전트에게 중요한 컨텍스트를 제공합니다.

feat: implement [기능명] (#[기능번호])

[구현 세부사항]
- 변경사항 1
- 변경사항 2
- 변경사항 3

[테스트]
- 테스트 시나리오 1: 통과
- 테스트 시나리오 2: 통과
- E2E 테스트: verification/screenshots/

[업데이트]
- feature_list.json: #[번호] passes: true로 변경

[다음 단계]
Feature #[다음번호] - [다음 기능명]

실전 구현 가이드

Claude Agent SDK 설치

# Node.js 환경
npm install -g @anthropic-ai/claude-code

# Python 환경
pip install claude-agent-sdk

# 인증 설정
export ANTHROPIC_API_KEY="your-api-key"

기본 구조 구현

# autonomous_agent.py
import asyncio
from pathlib import Path
from claude_agent_sdk import query, ClaudeAgentOptions

async def run_initializer_agent(project_dir: Path):
    """첫 세션: 환경 설정 및 feature_list.json 생성"""
    
    # 프롬프트 로드
    with open('prompts/initializer_prompt.md') as f:
        system_prompt = f.read()
    
    options = ClaudeAgentOptions(
        cwd=str(project_dir),
        system_prompt=system_prompt,
        allowed_tools=["Read", "Write", "Bash", "Glob"],
        permission_mode='acceptEdits',
        model='claude-sonnet-4-20250514'
    )
    
    async for message in query(
        prompt="app_spec.txt를 읽고 프로젝트를 초기화하세요.",
        options=options
    ):
        if hasattr(message, 'result'):
            print(f"Initializer: {message.result}")

async def run_coding_agent(project_dir: Path):
    """이후 세션: 기능 구현"""
    
    with open('prompts/coding_prompt.md') as f:
        system_prompt = f.read()
    
    options = ClaudeAgentOptions(
        cwd=str(project_dir),
        system_prompt=system_prompt,
        allowed_tools=["Read", "Write", "Bash", "Glob", "WebSearch"],
        permission_mode='acceptEdits'
    )
    
    async for message in query(
        prompt="세션 시작 루틴을 실행하고 다음 기능을 구현하세요.",
        options=options
    ):
        if hasattr(message, 'result'):
            print(f"Coding Agent: {message.result}")

async def main():
    project_dir = Path("./generations/my_project")
    project_dir.mkdir(parents=True, exist_ok=True)
    
    feature_list = project_dir / "feature_list.json"
    
    if not feature_list.exists():
        print("첫 세션: Initializer Agent 실행")
        await run_initializer_agent(project_dir)
    else:
        print("계속 세션: Coding Agent 실행")
        await run_coding_agent(project_dir)

if __name__ == "__main__":
    asyncio.run(main())

프롬프트 템플릿

initializer_prompt.md

# Initializer Agent 시스템 프롬프트

당신은 장기 실행 자율 개발 프로세스의 **첫 번째 에이전트**입니다.
미래의 모든 Coding Agent가 효과적으로 작업할 수 있는 기반을 구축하는 것이 당신의 임무입니다.

## 작업 순서

1. **사양 읽기**: app_spec.txt를 신중히 읽어 요구사항을 완전히 이해하세요.

2. **feature_list.json 생성**: 200개의 상세한 E2E 테스트 케이스를 작성하세요.
   - 각 기능은 구체적이고 테스트 가능해야 합니다
   - 우선순위를 명확히 표시하세요
   - 모든 기능은 초기에 `"passes": false`로 설정

3. **프로젝트 구조 설정**:
   - 기술 스택에 맞는 디렉토리 구조 생성
   - package.json 또는 requirements.txt 설정
   - init.sh 스크립트 작성 (개발 서버 자동 실행)

4. **Git 초기화**:
   
   git init
   git add .
   git commit -m "Initial setup: complete feature list and project structure"
   

5. **진행 상황 기록**: claude-progress.txt에 초기 설정 내용 기록

## 중요 원칙

- 완벽주의보다 **완성도**를 추구하세요
- 다음 에이전트를 위한 명확한 **인계 자료**를 남기세요
- 시간이 남으면 최우선 순위 기능 구현을 시작할 수 있습니다

coding_prompt.md

# Coding Agent 시스템 프롬프트

당신은 장기 실행 프로젝트를 계속하는 Coding Agent입니다.
이것은 **새로운 컨텍스트 윈도우**이므로 이전 세션의 기억이 없습니다.

## 필수 시작 루틴

매 세션 시작 시 다음 명령을 **반드시** 실행하세요:


# 1. 위치 확인
pwd

# 2. 프로젝트 구조 파악
ls -la

# 3. 사양 확인
cat app_spec.txt

# 4. 기능 목록 (처음 50개)
cat feature_list.json | head -50

# 5. 이전 진행 상황
cat claude-progress.txt

# 6. Git 히스토리
git log --oneline -20

# 7. 남은 작업 확인
cat feature_list.json | grep '"passes": false' | wc -l


## 작업 흐름

1. **기능 선택**: feature_list.json에서 `"passes": false`인 최우선 순위 기능 선택

2. **환경 확인**: 

   ./init.sh  # 또는 npm run dev


3. **기존 테스트 실행**: 이전 세션이 버그를 남겼을 수 있습니다

4. **기능 구현**: 선택한 기능의 모든 단계 완료

5. **E2E 테스트**: Puppeteer로 실제 UI 테스트
   - 마우스와 키보드로 사람처럼 테스트
   - JavaScript eval 사용 금지
   - 스크린샷을 verification/ 디렉토리에 저장

6. **검증 후 업데이트**: 
   - 테스트가 **완전히 통과**한 후에만 `"passes": true`로 변경
   - 스크린샷 증거 없이는 passes 변경 금지

7. **Git 커밋**:

   git add .
   git commit -m "feat: [기능명] (#번호)
   
   - 구체적 변경사항
   - E2E 테스트: 통과
   - 스크린샷: verification/
   - feature_list.json 업데이트
   "


8. **진행 기록**: claude-progress.txt 업데이트

## 중요 제약사항

- **한 세션에 한 기능만**: 욕심내지 마세요
- **검증 없이 passes 변경 금지**: 반드시 스크린샷 증거 필요
- **명확한 커밋 메시지**: 다음 에이전트를 위해
- **프로덕션 품질**: 임시방편 금지

## 목표

200+ 테스트가 모두 통과하는 프로덕션 품질의 애플리케이션

모범 사례

1. 작업 범위 관리

좋은 예시:

{
  "id": 5,
  "title": "로그인 폼 유효성 검사",
  "description": "이메일 형식과 비밀번호 길이 검증",
  "steps": [
    "이메일 정규식 검증 구현",
    "비밀번호 8자 이상 확인",
    "에러 메시지 표시"
  ],
  "acceptance_criteria": [
    "잘못된 이메일 형식 입력 시 에러 표시",
    "8자 미만 비밀번호 입력 시 에러 표시",
    "올바른 입력 시 에러 없음"
  ]
}

나쁜 예시:

{
  "id": 5,
  "title": "사용자 관리 시스템",
  "description": "전체 사용자 관리 기능"
  // 너무 광범위함!
}

2. 테스트 자동화

// tests/e2e-automation.js
const { chromium } = require('playwright');

async function runFeatureTest(featureId, testScenarios) {
  const browser = await chromium.launch({ headless: false });
  const context = await browser.newContext({
    viewport: { width: 1280, height: 720 },
    recordVideo: { dir: `verification/videos/feature-${featureId}/` }
  });
  const page = await context.newPage();
  
  const results = [];
  
  for (const scenario of testScenarios) {
    console.log(`Testing: ${scenario.name}`);
    
    try {
      // 시나리오 실행
      await page.goto('http://localhost:3000');
      await scenario.execute(page);
      
      // 스크린샷 캡처
      const screenshotPath = `verification/feature-${featureId}-${scenario.name}.png`;
      await page.screenshot({ 
        path: screenshotPath,
        fullPage: true 
      });
      
      // 결과 저장
      results.push({
        feature: featureId,
        scenario: scenario.name,
        passed: true,
        screenshot: screenshotPath,
        timestamp: new Date().toISOString()
      });
      
      console.log(`✓ ${scenario.name} passed`);
      
    } catch (error) {
      results.push({
        feature: featureId,
        scenario: scenario.name,
        passed: false,
        error: error.message,
        timestamp: new Date().toISOString()
      });
      
      console.error(`✗ ${scenario.name} failed: ${error.message}`);
    }
  }
  
  await context.close();
  await browser.close();
  
  // 테스트 결과 저장
  const fs = require('fs');
  fs.writeFileSync(
    `verification/feature-${featureId}-results.json`,
    JSON.stringify(results, null, 2)
  );
  
  return results.every(r => r.passed);
}

module.exports = { runFeatureTest };

3. 진행 상황 시각화

// tools/progress-tracker.js
const fs = require('fs');

class ProgressTracker {
  constructor(featureListPath) {
    this.featureListPath = featureListPath;
    this.features = JSON.parse(fs.readFileSync(featureListPath, 'utf8')).features;
  }
  
  getStats() {
    const total = this.features.length;
    const completed = this.features.filter(f => f.passes).length;
    const percentage = ((completed / total) * 100).toFixed(1);
    
    return {
      total,
      completed,
      remaining: total - completed,
      percentage,
      estimatedHoursRemaining: (total - completed) * 0.75 // 기능당 45분 가정
    };
  }
  
  generateReport() {
    const stats = this.getStats();
    const byPriority = {
      high: this.features.filter(f => !f.passes && f.priority === 'high').length,
      medium: this.features.filter(f => !f.passes && f.priority === 'medium').length,
      low: this.features.filter(f => !f.passes && f.priority === 'low').length
    };
    
    return `
# 프로젝트 진행 상황

## 전체 현황
- 총 기능: ${stats.total}
- 완료: ${stats.completed} (${stats.percentage}%)
- 남은 작업: ${stats.remaining}
- 예상 소요 시간: ${stats.estimatedHoursRemaining.toFixed(1)}시간

## 우선순위별 남은 작업
- High: ${byPriority.high}
- Medium: ${byPriority.medium}
- Low: ${byPriority.low}

## 진행 바
${'█'.repeat(Math.floor(stats.percentage / 5))}${'░'.repeat(20 - Math.floor(stats.percentage / 5))} ${stats.percentage}%
    `;
  }
  
  getNextFeature() {
    // 우선순위 순으로 다음 미완료 기능 반환
    const priorities = ['high', 'medium', 'low'];
    
    for (const priority of priorities) {
      const next = this.features.find(
        f => !f.passes && f.priority === priority
      );
      if (next) return next;
    }
    
    return null;
  }
}

// 사용 예시
const tracker = new ProgressTracker('./feature_list.json');
console.log(tracker.generateReport());

const nextFeature = tracker.getNextFeature();
if (nextFeature) {
  console.log(`\n다음 작업: Feature #${nextFeature.id} - ${nextFeature.title}`);
} else {
  console.log('\n🎉 모든 기능이 완료되었습니다!');
}

4. 에러 복구 메커니즘

# tools/error_recovery.py
import json
import subprocess
from pathlib import Path

class ErrorRecovery:
    def __init__(self, project_dir: Path):
        self.project_dir = project_dir
        self.feature_list_path = project_dir / "feature_list.json"
        self.backup_dir = project_dir / ".backups"
        self.backup_dir.mkdir(exist_ok=True)
    
    def create_checkpoint(self, feature_id: int):
        """기능 구현 전 체크포인트 생성"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        # Git 스태시 생성
        subprocess.run(
            ["git", "stash", "push", "-m", f"checkpoint_feature_{feature_id}"],
            cwd=self.project_dir
        )
        
        # feature_list.json 백업
        backup_path = self.backup_dir / f"feature_list_{timestamp}.json"
        shutil.copy(self.feature_list_path, backup_path)
        
        return timestamp
    
    def rollback_to_checkpoint(self, timestamp: str):
        """체크포인트로 롤백"""
        # Git 스태시 복원
        subprocess.run(["git", "stash", "pop"], cwd=self.project_dir)
        
        # feature_list.json 복원
        backup_path = self.backup_dir / f"feature_list_{timestamp}.json"
        if backup_path.exists():
            shutil.copy(backup_path, self.feature_list_path)
    
    def validate_repository_state(self):
        """저장소 상태 검증"""
        issues = []
        
        # 1. Git 저장소 확인
        if not (self.project_dir / ".git").exists():
            issues.append("Git 저장소가 초기화되지 않았습니다")
        
        # 2. feature_list.json 존재 확인
        if not self.feature_list_path.exists():
            issues.append("feature_list.json이 없습니다")
        
        # 3. feature_list.json 유효성 검증
        try:
            with open(self.feature_list_path) as f:
                data = json.load(f)
                if "features" not in data:
                    issues.append("feature_list.json에 features 키가 없습니다")
        except json.JSONDecodeError:
            issues.append("feature_list.json이 유효한 JSON이 아닙니다")
        
        # 4. 변경사항 미커밋 확인
        result = subprocess.run(
            ["git", "status", "--porcelain"],
            cwd=self.project_dir,
            capture_output=True,
            text=True
        )
        if result.stdout.strip():
            issues.append("커밋되지 않은 변경사항이 있습니다")
        
        return issues
    
    def auto_fix(self):
        """자동 수정 시도"""
        issues = self.validate_repository_state()
        
        for issue in issues:
            if "Git 저장소" in issue:
                subprocess.run(["git", "init"], cwd=self.project_dir)
                print("✓ Git 저장소 초기화 완료")
            
            elif "커밋되지 않은" in issue:
                subprocess.run(["git", "add", "."], cwd=self.project_dir)
                subprocess.run(
                    ["git", "commit", "-m", "Auto-commit: recovery"],
                    cwd=self.project_dir
                )
                print("✓ 미커밋 변경사항 자동 커밋 완료")
        
        remaining = self.validate_repository_state()
        return len(remaining) == 0

5. 세션 간 일관성 보장

// tools/session-validator.js
const fs = require('fs');
const path = require('path');

class SessionValidator {
  constructor(projectDir) {
    this.projectDir = projectDir;
    this.requiredFiles = [
      'feature_list.json',
      'app_spec.txt',
      'claude-progress.txt',
      'init.sh'
    ];
  }
  
  validateSessionStart() {
    const errors = [];
    const warnings = [];
    
    // 1. 필수 파일 존재 확인
    for (const file of this.requiredFiles) {
      const filePath = path.join(this.projectDir, file);
      if (!fs.existsSync(filePath)) {
        errors.push(`필수 파일 누락: ${file}`);
      }
    }
    
    // 2. feature_list.json 무결성 검사
    try {
      const featureList = JSON.parse(
        fs.readFileSync(path.join(this.projectDir, 'feature_list.json'))
      );
      
      if (!Array.isArray(featureList.features)) {
        errors.push('feature_list.json의 features가 배열이 아닙니다');
      }
      
      // 중복 ID 확인
      const ids = featureList.features.map(f => f.id);
      const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index);
      if (duplicates.length > 0) {
        errors.push(`중복된 feature ID: ${duplicates.join(', ')}`);
      }
      
      // passes:true인데 커밋이 없는 경우
      const { execSync } = require('child_process');
      const gitLog = execSync('git log --oneline', { 
        cwd: this.projectDir 
      }).toString();
      
      for (const feature of featureList.features) {
        if (feature.passes && !gitLog.includes(`#${feature.id}`)) {
          warnings.push(
            `Feature #${feature.id}가 passes:true이지만 Git 커밋이 없습니다`
          );
        }
      }
      
    } catch (error) {
      errors.push(`feature_list.json 파싱 실패: ${error.message}`);
    }
    
    // 3. Git 상태 확인
    try {
      const { execSync } = require('child_process');
      const status = execSync('git status --porcelain', {
        cwd: this.projectDir
      }).toString();
      
      if (status.trim()) {
        warnings.push('이전 세션의 미커밋 변경사항이 있습니다');
      }
    } catch (error) {
      errors.push('Git 저장소 확인 실패');
    }
    
    return { errors, warnings, valid: errors.length === 0 };
  }
  
  generateSessionReport() {
    const validation = this.validateSessionStart();
    
    let report = '# 세션 검증 보고서\n\n';
    
    if (validation.valid) {
      report += '✅ 모든 검증 통과\n\n';
    } else {
      report += '❌ 검증 실패\n\n';
      report += '## 에러\n';
      validation.errors.forEach(error => {
        report += `- ${error}\n`;
      });
      report += '\n';
    }
    
    if (validation.warnings.length > 0) {
      report += '## 경고\n';
      validation.warnings.forEach(warning => {
        report += `- ${warning}\n`;
      });
    }
    
    return report;
  }
}

module.exports = SessionValidator;

고급 패턴

1. 멀티 에이전트 협업

여러 전문화된 에이전트를 동시에 실행할 수 있습니다.

# multi_agent_system.py
from claude_agent_sdk import query, ClaudeAgentOptions
import asyncio

class MultiAgentOrchestrator:
    def __init__(self, project_dir):
        self.project_dir = project_dir
        
    async def run_frontend_agent(self, feature):
        """프론트엔드 전문 에이전트"""
        system_prompt = """
        당신은 React/Vue 프론트엔드 전문 에이전트입니다.
        UI/UX, 반응형 디자인, 접근성에 집중하세요.
        """
        
        options = ClaudeAgentOptions(
            cwd=str(self.project_dir),
            system_prompt=system_prompt,
            allowed_tools=["Read", "Write", "Bash"]
        )
        
        async for msg in query(
            prompt=f"다음 UI 기능을 구현하세요: {feature['title']}",
            options=options
        ):
            yield msg
    
    async def run_backend_agent(self, feature):
        """백엔드 전문 에이전트"""
        system_prompt = """
        당신은 API/데이터베이스 백엔드 전문 에이전트입니다.
        성능, 보안, 확장성에 집중하세요.
        """
        
        options = ClaudeAgentOptions(
            cwd=str(self.project_dir),
            system_prompt=system_prompt,
            allowed_tools=["Read", "Write", "Bash"]
        )
        
        async for msg in query(
            prompt=f"다음 API를 구현하세요: {feature['title']}",
            options=options
        ):
            yield msg
    
    async def run_qa_agent(self, feature):
        """QA/테스트 전문 에이전트"""
        system_prompt = """
        당신은 품질 보증 전문 에이전트입니다.
        E2E 테스트, 통합 테스트, 성능 테스트를 작성하세요.
        """
        
        options = ClaudeAgentOptions(
            cwd=str(self.project_dir),
            system_prompt=system_prompt,
            allowed_tools=["Read", "Write", "Bash"]
        )
        
        async for msg in query(
            prompt=f"다음 기능을 테스트하세요: {feature['title']}",
            options=options
        ):
            yield msg
    
    async def orchestrate_feature(self, feature):
        """기능 구현을 위해 여러 에이전트 조율"""
        
        # 1단계: 백엔드 구현
        print("🔧 백엔드 구현 중...")
        async for msg in self.run_backend_agent(feature):
            pass
        
        # 2단계: 프론트엔드 구현
        print("🎨 프론트엔드 구현 중...")
        async for msg in self.run_frontend_agent(feature):
            pass
        
        # 3단계: QA 테스트
        print("🧪 테스트 실행 중...")
        async for msg in self.run_qa_agent(feature):
            pass
        
        print(f"✅ Feature #{feature['id']} 완료")

2. 지속적 학습 시스템

# learning_system.py
import json
from pathlib import Path
from collections import defaultdict

class LearningSystem:
    def __init__(self, project_dir):
        self.project_dir = Path(project_dir)
        self.knowledge_base = self.project_dir / "agent_knowledge.json"
        self.load_knowledge()
    
    def load_knowledge(self):
        """축적된 지식 로드"""
        if self.knowledge_base.exists():
            with open(self.knowledge_base) as f:
                self.knowledge = json.load(f)
        else:
            self.knowledge = {
                "common_patterns": {},
                "error_solutions": {},
                "best_practices": {},
                "time_estimates": defaultdict(list)
            }
    
    def record_feature_completion(self, feature_id, time_spent, issues_encountered):
        """기능 완료 후 학습"""
        feature_type = self.classify_feature(feature_id)
        
        # 시간 예측 개선
        self.knowledge["time_estimates"][feature_type].append(time_spent)
        
        # 발생한 문제와 해결책 기록
        for issue in issues_encountered:
            key = issue['error_type']
            if key not in self.knowledge["error_solutions"]:
                self.knowledge["error_solutions"][key] = []
            
            self.knowledge["error_solutions"][key].append({
                "solution": issue['solution'],
                "context": issue['context'],
                "feature_id": feature_id
            })
        
        self.save_knowledge()
    
    def get_time_estimate(self, feature_type):
        """과거 데이터 기반 시간 예측"""
        if feature_type in self.knowledge["time_estimates"]:
            times = self.knowledge["time_estimates"][feature_type]
            return sum(times) / len(times)
        return 45  # 기본값: 45분
    
    def get_relevant_solutions(self, error_message):
        """에러 메시지에 대한 과거 해결책 검색"""
        relevant = []
        for error_type, solutions in self.knowledge["error_solutions"].items():
            if error_type.lower() in error_message.lower():
                relevant.extend(solutions)
        return relevant
    
    def save_knowledge(self):
        """지식 베이스 저장"""
        with open(self.knowledge_base, 'w') as f:
            json.dump(self.knowledge, f, indent=2)

3. 동적 우선순위 조정

// tools/priority-adjuster.js
class DynamicPriorityAdjuster {
  constructor(featureListPath) {
    this.featureListPath = featureListPath;
    this.features = this.loadFeatures();
  }
  
  loadFeatures() {
    const fs = require('fs');
    const data = JSON.parse(fs.readFileSync(this.featureListPath, 'utf8'));
    return data.features;
  }
  
  adjustPriorities() {
    // 1. 의존성 분석
    const dependencyGraph = this.buildDependencyGraph();
    
    // 2. 차단된 기능 우선순위 상승
    for (const feature of this.features) {
      if (!feature.passes) {
        const blockingCount = this.countBlockedFeatures(feature.id, dependencyGraph);
        
        if (blockingCount > 5) {
          feature.priority = 'high';
          feature.adjustmentReason = `${blockingCount}개 기능을 차단 중`;
        }
      }
    }
    
    // 3. 빠른 승리(Quick Wins) 식별
    for (const feature of this.features) {
      if (!feature.passes && feature.estimatedMinutes < 30) {
        if (feature.priority === 'low') {
          feature.priority = 'medium';
          feature.adjustmentReason = '빠른 승리 가능';
        }
      }
    }
    
    // 4. 장기 미완료 기능 우선순위 상승
    const now = new Date();
    for (const feature of this.features) {
      if (feature.createdAt) {
        const daysSinceCreation = (now - new Date(feature.createdAt)) / (1000 * 60 * 60 * 24);
        
        if (daysSinceCreation > 7 && !feature.passes) {
          feature.priority = 'high';
          feature.adjustmentReason = `${Math.floor(daysSinceCreation)}일째 미완료`;
        }
      }
    }
    
    this.saveFeatures();
  }
  
  buildDependencyGraph() {
    const graph = {};
    
    for (const feature of this.features) {
      graph[feature.id] = {
        dependsOn: feature.dependsOn || [],
        blockedBy: []
      };
    }
    
    // 역방향 의존성 계산
    for (const feature of this.features) {
      if (feature.dependsOn) {
        for (const depId of feature.dependsOn) {
          if (graph[depId]) {
            graph[depId].blockedBy.push(feature.id);
          }
        }
      }
    }
    
    return graph;
  }
  
  countBlockedFeatures(featureId, graph) {
    if (!graph[featureId]) return 0;
    
    let count = graph[featureId].blockedBy.length;
    
    // 재귀적으로 간접 차단된 기능도 카운트
    for (const blockedId of graph[featureId].blockedBy) {
      count += this.countBlockedFeatures(blockedId, graph);
    }
    
    return count;
  }
  
  saveFeatures() {
    const fs = require('fs');
    const data = { features: this.features };
    fs.writeFileSync(
      this.featureListPath,
      JSON.stringify(data, null, 2)
    );
  }
}

// 사용 예시
const adjuster = new DynamicPriorityAdjuster('./feature_list.json');
adjuster.adjustPriorities();
console.log('우선순위 조정 완료');

문제 해결 가이드

일반적인 문제와 해결책

문제 1: 에이전트가 이전 작업을 기억하지 못함

증상: 매 세션마다 같은 작업을 반복하거나 이전에 완료한 기능을 다시 시도합니다.

해결책:

  1. claude-progress.txt가 제대로 업데이트되고 있는지 확인
  2. 세션 시작 루틴이 실행되는지 확인
  3. Git 커밋 메시지가 충분히 상세한지 검토
# 진단 스크립트
echo "=== 메모리 시스템 진단 ==="

# 1. 진행 로그 확인
echo "진행 로그 최신 항목:"
tail -20 claude-progress.txt

# 2. Git 히스토리 확인
echo -e "\nGit 커밋 히스토리:"
git log --oneline -10

# 3. feature_list.json 상태
echo -e "\n완료된 기능 수:"
cat feature_list.json | grep '"passes": true' | wc -l

echo -e "\n미완료 기능 수:"
cat feature_list.json | grep '"passes": false' | wc -l

문제 2: 테스트가 통과하지 않음

증상: E2E 테스트가 계속 실패하거나 타임아웃이 발생합니다.

해결책:

  1. 개발 서버가 제대로 실행 중인지 확인
  2. 테스트 대기 시간을 충분히 설정
  3. 헤드리스 모드를 끄고 실제 브라우저에서 확인
// tests/debug-test.js
const puppeteer = require('puppeteer');

async function debugTest() {
  // 헤드리스 모드 끄기
  const browser = await puppeteer.launch({ 
    headless: false,
    slowMo: 250,  // 동작을 천천히
    devtools: true  // DevTools 자동 열기
  });
  
  const page = await browser.newPage();
  
  // 콘솔 메시지 캡처
  page.on('console', msg => console.log('PAGE LOG:', msg.text()));
  
  // 에러 캡처
  page.on('pageerror', error => console.log('PAGE ERROR:', error.message));
  
  try {
    await page.goto('http://localhost:3000', {
      waitUntil: 'networkidle2',
      timeout: 30000
    });
    
    // 단계별 스크린샷
    await page.screenshot({ path: 'debug-1-initial.png' });
    
    // 요소가 나타날 때까지 대기
    await page.waitForSelector('#login-form', { timeout: 10000 });
    await page.screenshot({ path: 'debug-2-form-visible.png' });
    
    // 입력
    await page.type('#email', '[email protected]');
    await page.screenshot({ path: 'debug-3-email-entered.png' });
    
    await page.type('#password', 'password123');
    await page.screenshot({ path: 'debug-4-password-entered.png' });
    
    // 제출
    await page.click('button[type="submit"]');
    await page.waitForNavigation({ timeout: 10000 });
    await page.screenshot({ path: 'debug-5-after-submit.png' });
    
    console.log('✓ 테스트 성공');
    
  } catch (error) {
    console.error('✗ 테스트 실패:', error.message);
    await page.screenshot({ path: 'debug-error.png' });
  }
  
  // 브라우저를 자동으로 닫지 않음
  // await browser.close();
}

debugTest();

문제 3: 에이전트가 feature_list.json을 임의로 수정함

증상: 테스트 없이 passes: true로 변경하거나 기능을 임의로 추가/삭제합니다.

해결책:

  1. 시스템 프롬프트에 명확한 제약사항 추가
  2. Git 훅으로 무단 수정 감지
  3. 검증 스크립트 자동 실행
#!/bin/bash
# .git/hooks/pre-commit

echo "=== feature_list.json 검증 중 ==="

# Python으로 검증 스크립트 실행
python3 << 'EOF'
import json
import sys
import subprocess

# feature_list.json 로드
with open('feature_list.json') as f:
    data = json.load(f)

# passes: true인 기능 확인
for feature in data['features']:
    if feature.get('passes'):
        feature_id = feature['id']
        
        # 해당 기능의 커밋이 있는지 확인
        result = subprocess.run(
            ['git', 'log', '--all', '--grep', f'#{feature_id}'],
            capture_output=True,
            text=True
        )
        
        if not result.stdout.strip():
            print(f"❌ Feature #{feature_id}가 passes:true이지만 커밋이 없습니다!")
            sys.exit(1)
        
        # 스크린샷이 있는지 확인
        screenshot_path = f'verification/feature-{feature_id}-*.png'
        result = subprocess.run(
            ['ls', screenshot_path],
            capture_output=True,
            shell=True
        )
        
        if result.returncode != 0:
            print(f"❌ Feature #{feature_id}의 스크린샷이 없습니다!")
            sys.exit(1)

print("✓ feature_list.json 검증 통과")
EOF

if [ $? -ne 0 ]; then
    echo "검증 실패: 커밋을 중단합니다"
    exit 1
fi

echo "✓ 사전 커밋 검증 완료"

문제 4: 세션 타임아웃

증상: 긴 작업 중 세션이 만료됩니다.

해결책:

  1. 작업을 더 작은 단위로 쪼개기
  2. 주기적으로 중간 커밋 생성
  3. 체크포인트 시스템 구현
# tools/checkpoint_system.py
import time
from datetime import datetime

class CheckpointSystem:
    def __init__(self, project_dir):
        self.project_dir = project_dir
        self.start_time = time.time()
        self.checkpoint_interval = 20 * 60  # 20분마다
        self.last_checkpoint = self.start_time
    
    def should_checkpoint(self):
        """체크포인트 생성 시점인지 확인"""
        elapsed = time.time() - self.last_checkpoint
        return elapsed >= self.checkpoint_interval
    
    def create_checkpoint(self, message=""):
        """중간 체크포인트 생성"""
        if not self.should_checkpoint():
            return False
        
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        # Git 스태시 생성
        subprocess.run(
            ["git", "add", "."],
            cwd=self.project_dir
        )
        
        subprocess.run(
            ["git", "commit", "-m", f"checkpoint: {timestamp} - {message}"],
            cwd=self.project_dir
        )
        
        self.last_checkpoint = time.time()
        print(f"✓ 체크포인트 생성: {timestamp}")
        return True
    
    def get_session_duration(self):
        """세션 지속 시간 반환"""
        elapsed = time.time() - self.start_time
        return {
            'minutes': elapsed / 60,
            'remaining': (60 - (elapsed / 60))  # 60분 세션 가정
        }

성능 최적화

1. 병렬 작업 처리

독립적인 기능들을 동시에 처리하여 전체 완료 시간을 단축할 수 있습니다.

# parallel_execution.py
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

class ParallelExecutor:
    def __init__(self, project_dir):
        self.project_dir = project_dir
        self.max_concurrent = 3  # 동시 실행 최대 수
    
    def identify_independent_features(self, features):
        """의존성이 없는 독립적인 기능 식별"""
        independent = []
        
        for feature in features:
            if not feature.get('passes'):
                # 의존성 확인
                depends_on = feature.get('dependsOn', [])
                all_deps_complete = all(
                    self.is_feature_complete(dep_id, features)
                    for dep_id in depends_on
                )
                
                if all_deps_complete:
                    independent.append(feature)
        
        return independent
    
    def is_feature_complete(self, feature_id, features):
        """기능이 완료되었는지 확인"""
        for f in features:
            if f['id'] == feature_id:
                return f.get('passes', False)
        return False
    
    async def execute_feature(self, feature):
        """단일 기능 실행"""
        options = ClaudeAgentOptions(
            cwd=str(self.project_dir),
            system_prompt=self.get_coding_prompt(),
            allowed_tools=["Read", "Write", "Bash", "Glob"]
        )
        
        print(f"🔄 시작: Feature #{feature['id']} - {feature['title']}")
        
        async for message in query(
            prompt=f"Feature #{feature['id']}를 구현하세요: {feature['description']}",
            options=options
        ):
            if hasattr(message, 'result'):
                print(f"✓ 완료: Feature #{feature['id']}")
                return True
        
        return False
    
    async def execute_parallel(self, features):
        """여러 기능을 병렬로 실행"""
        independent = self.identify_independent_features(features)
        
        # 배치 단위로 처리
        for i in range(0, len(independent), self.max_concurrent):
            batch = independent[i:i + self.max_concurrent]
            
            print(f"\n=== 배치 {i // self.max_concurrent + 1} 실행 ===")
            print(f"동시 처리: {[f['id'] for f in batch]}")
            
            # 병렬 실행
            tasks = [self.execute_feature(f) for f in batch]
            results = await asyncio.gather(*tasks, return_exceptions=True)
            
            # 결과 확인
            for feature, result in zip(batch, results):
                if isinstance(result, Exception):
                    print(f"✗ 실패: Feature #{feature['id']} - {result}")
                elif result:
                    print(f"✓ 성공: Feature #{feature['id']}")

2. 캐싱 시스템

반복적인 작업을 캐시하여 속도를 향상시킵니다.

// tools/cache-system.js
const fs = require('fs');
const crypto = require('crypto');
const path = require('path');

class CacheSystem {
  constructor(cacheDir = '.cache') {
    this.cacheDir = cacheDir;
    this.ensureCacheDir();
  }
  
  ensureCacheDir() {
    if (!fs.existsSync(this.cacheDir)) {
      fs.mkdirSync(this.cacheDir, { recursive: true });
    }
  }
  
  generateKey(input) {
    // 입력값으로 캐시 키 생성
    return crypto
      .createHash('sha256')
      .update(JSON.stringify(input))
      .digest('hex');
  }
  
  get(key) {
    const cachePath = path.join(this.cacheDir, `${key}.json`);
    
    if (fs.existsSync(cachePath)) {
      const cached = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
      
      // 만료 시간 확인 (24시간)
      const age = Date.now() - cached.timestamp;
      if (age < 24 * 60 * 60 * 1000) {
        console.log(`✓ 캐시 히트: ${key}`);
        return cached.data;
      }
    }
    
    console.log(`✗ 캐시 미스: ${key}`);
    return null;
  }
  
  set(key, data) {
    const cachePath = path.join(this.cacheDir, `${key}.json`);
    const cached = {
      timestamp: Date.now(),
      data: data
    };
    
    fs.writeFileSync(cachePath, JSON.stringify(cached, null, 2));
    console.log(`✓ 캐시 저장: ${key}`);
  }
  
  // 특정 작업을 캐시와 함께 실행
  async cached(key, operation) {
    const cacheKey = this.generateKey(key);
    const cached = this.get(cacheKey);
    
    if (cached !== null) {
      return cached;
    }
    
    const result = await operation();
    this.set(cacheKey, result);
    return result;
  }
  
  clear() {
    // 캐시 디렉토리 전체 삭제
    if (fs.existsSync(this.cacheDir)) {
      fs.rmSync(this.cacheDir, { recursive: true });
      this.ensureCacheDir();
      console.log('✓ 캐시 클리어 완료');
    }
  }
}

// 사용 예시
const cache = new CacheSystem();

// 무거운 테스트를 캐시
async function runHeavyTest(featureId) {
  return await cache.cached(
    { type: 'test', featureId },
    async () => {
      console.log('실제 테스트 실행 중...');
      // 실제 테스트 로직
      return { passed: true, duration: 45 };
    }
  );
}

module.exports = CacheSystem;

3. 점진적 테스트

모든 테스트를 매번 실행하는 대신 변경된 부분만 테스트합니다.

// tools/incremental-testing.js
const { execSync } = require('child_process');
const fs = require('fs');

class IncrementalTesting {
  constructor(projectDir) {
    this.projectDir = projectDir;
  }
  
  getChangedFiles() {
    // Git으로 변경된 파일 목록 가져오기
    try {
      const output = execSync(
        'git diff --name-only HEAD',
        { cwd: this.projectDir }
      ).toString();
      
      return output.split('\n').filter(Boolean);
    } catch (error) {
      return [];
    }
  }
  
  identifyAffectedTests(changedFiles) {
    const affectedTests = new Set();
    
    // 파일 변경에 따른 테스트 매핑
    const testMapping = {
      'src/components/Login': ['tests/e2e/login.test.js', 'tests/unit/login.test.js'],
      'src/api/auth': ['tests/api/auth.test.js'],
      'src/components/': ['tests/e2e/ui.test.js']
    };
    
    for (const file of changedFiles) {
      for (const [pattern, tests] of Object.entries(testMapping)) {
        if (file.includes(pattern)) {
          tests.forEach(test => affectedTests.add(test));
        }
      }
    }
    
    return Array.from(affectedTests);
  }
  
  runIncrementalTests() {
    const changedFiles = this.getChangedFiles();
    
    if (changedFiles.length === 0) {
      console.log('변경사항 없음 - 모든 테스트 스킵');
      return true;
    }
    
    console.log(`변경된 파일: ${changedFiles.length}개`);
    
    const affectedTests = this.identifyAffectedTests(changedFiles);
    
    if (affectedTests.length === 0) {
      console.log('영향받는 테스트 없음');
      return true;
    }
    
    console.log(`실행할 테스트: ${affectedTests.length}개`);
    
    for (const test of affectedTests) {
      console.log(`실행 중: ${test}`);
      try {
        execSync(`npm test ${test}`, {
          cwd: this.projectDir,
          stdio: 'inherit'
        });
      } catch (error) {
        console.error(`✗ 실패: ${test}`);
        return false;
      }
    }
    
    console.log('✓ 모든 증분 테스트 통과');
    return true;
  }
}

module.exports = IncrementalTesting;

보안 및 권한 관리

1. 안전한 명령어 실행

// tools/safe-executor.js
class SafeCommandExecutor {
  constructor() {
    // 허용된 명령어 화이트리스트
    this.allowedCommands = [
      'npm', 'node', 'git', 'cat', 'ls', 'pwd',
      'mkdir', 'touch', 'cp', 'mv', 'rm'
    ];
    
    // 금지된 패턴
    this.blockedPatterns = [
      /rm\s+-rf\s+\//, // rm -rf /
      /sudo/, // sudo 명령
      /chmod\s+777/, // 과도한 권한
      /eval\(/, // eval 함수
      />\s*\/dev\//, // 시스템 디바이스 접근
      /curl.*\|\s*bash/, // 파이프라인 실행
    ];
  }
  
  isCommandSafe(command) {
    // 기본 명령어 추출
    const baseCommand = command.trim().split(/\s+/)[0];
    
    // 1. 화이트리스트 확인
    if (!this.allowedCommands.includes(baseCommand)) {
      return {
        safe: false,
        reason: `명령어 '${baseCommand}'는 허용되지 않습니다`
      };
    }
    
    // 2. 위험한 패턴 확인
    for (const pattern of this.blockedPatterns) {
      if (pattern.test(command)) {
        return {
          safe: false,
          reason: `위험한 패턴 감지: ${pattern}`
        };
      }
    }
    
    // 3. 파일 시스템 접근 범위 확인
    if (command.includes('..')) {
      return {
        safe: false,
        reason: '상위 디렉토리 접근 금지'
      };
    }
    
    return { safe: true };
  }
  
  execute(command, options = {}) {
    const safety = this.isCommandSafe(command);
    
    if (!safety.safe) {
      console.error(`❌ 명령어 차단: ${safety.reason}`);
      console.error(`차단된 명령어: ${command}`);
      throw new Error(safety.reason);
    }
    
    console.log(`✓ 안전한 명령어 실행: ${command}`);
    
    const { execSync } = require('child_process');
    return execSync(command, {
      ...options,
      timeout: 30000, // 30초 타임아웃
      maxBuffer: 10 * 1024 * 1024 // 10MB 버퍼
    });
  }
}

module.exports = SafeCommandExecutor;

2. 파일 접근 제어

# tools/file_access_control.py
import os
from pathlib import Path

class FileAccessControl:
    def __init__(self, project_root):
        self.project_root = Path(project_root).resolve()
        
        # 읽기 전용 파일/디렉토리
        self.readonly_paths = {
            '.git/config',
            '.claude_settings.json',
            'app_spec.txt'
        }
        
        # 접근 금지 디렉토리
        self.forbidden_dirs = {
            '/etc',
            '/usr',
            '/bin',
            os.path.expanduser('~/.ssh')
        }
    
    def is_path_safe(self, file_path):
        """경로가 안전한지 확인"""
        try:
            resolved = Path(file_path).resolve()
            
            # 1. 프로젝트 디렉토리 내부인지 확인
            if not str(resolved).startswith(str(self.project_root)):
                return False, "프로젝트 디렉토리 외부 접근 금지"
            
            # 2. 금지된 디렉토리 확인
            for forbidden in self.forbidden_dirs:
                if str(resolved).startswith(forbidden):
                    return False, f"접근 금지 디렉토리: {forbidden}"
            
            return True, "안전한 경로"
            
        except Exception as e:
            return False, f"경로 검증 실패: {str(e)}"
    
    def can_write(self, file_path):
        """쓰기 가능한지 확인"""
        rel_path = os.path.relpath(file_path, self.project_root)
        
        if rel_path in self.readonly_paths:
            return False, f"읽기 전용 파일: {rel_path}"
        
        safe, message = self.is_path_safe(file_path)
        return safe, message
    
    def can_read(self, file_path):
        """읽기 가능한지 확인"""
        return self.is_path_safe(file_path)
    
    def safe_read(self, file_path):
        """안전하게 파일 읽기"""
        can_read, message = self.can_read(file_path)
        
        if not can_read:
            raise PermissionError(message)
        
        with open(file_path, 'r') as f:
            return f.read()
    
    def safe_write(self, file_path, content):
        """안전하게 파일 쓰기"""
        can_write, message = self.can_write(file_path)
        
        if not can_write:
            raise PermissionError(message)
        
        # 백업 생성
        if os.path.exists(file_path):
            backup_path = f"{file_path}.backup"
            import shutil
            shutil.copy2(file_path, backup_path)
        
        with open(file_path, 'w') as f:
            f.write(content)

모니터링 및 디버깅

1. 실시간 진행 상황 대시보드

// tools/dashboard.js
const blessed = require('blessed');
const contrib = require('blessed-contrib');

class ProgressDashboard {
  constructor(featureListPath) {
    this.featureListPath = featureListPath;
    this.screen = blessed.screen();
    this.setupUI();
    this.startUpdates();
  }
  
  setupUI() {
    const grid = new contrib.grid({ rows: 12, cols: 12, screen: this.screen });
    
    // 전체 진행률 게이지
    this.progressGauge = grid.set(0, 0, 2, 6, contrib.gauge, {
      label: '전체 진행률',
      stroke: 'green',
      fill: 'white'
    });
    
    // 우선순위별 현황
    this.priorityDonut = grid.set(0, 6, 4, 6, contrib.donut, {
      label: '우선순위별 남은 작업',
      radius: 8,
      arcWidth: 3,
      remainColor: 'black',
      yPadding: 2
    });
    
    // 최근 완료 기능 로그
    this.logBox = grid.set(4, 0, 8, 12, contrib.log, {
      fg: 'green',
      selectedFg: 'green',
      label: '최근 활동'
    });
    
    // 남은 작업 목록
    this.todoTable = grid.set(2, 0, 2, 6, contrib.table, {
      keys: true,
      fg: 'white',
      selectedFg: 'white',
      selectedBg: 'blue',
      interactive: false,
      label: '다음 작업',
      width: '50%',
      height: '30%',
      columnSpacing: 3,
      columnWidth: [4, 30, 10]
    });
    
    this.screen.key(['escape', 'q', 'C-c'], () => {
      return process.exit(0);
    });
    
    this.screen.render();
  }
  
  update() {
    const fs = require('fs');
    const data = JSON.parse(fs.readFileSync(this.featureListPath, 'utf8'));
    const features = data.features;
    
    // 진행률 계산
    const total = features.length;
    const completed = features.filter(f => f.passes).length;
    const percentage = Math.round((completed / total) * 100);
    
    this.progressGauge.setPercent(percentage);
    
    // 우선순위별 현황
    const remaining = features.filter(f => !f.passes);
    const byPriority = {
      high: remaining.filter(f => f.priority === 'high').length,
      medium: remaining.filter(f => f.priority === 'medium').length,
      low: remaining.filter(f => f.priority === 'low').length
    };
    
    this.priorityDonut.setData([
      { percent: byPriority.high / remaining.length, label: 'High', color: 'red' },
      { percent: byPriority.medium / remaining.length, label: 'Medium', color: 'yellow' },
      { percent: byPriority.low / remaining.length, label: 'Low', color: 'green' }
    ]);
    
    // 다음 작업 목록
    const nextFeatures = remaining.slice(0, 5);
    this.todoTable.setData({
      headers: ['ID', 'Title', 'Priority'],
      data: nextFeatures.map(f => [
        f.id.toString(),
        f.title.substring(0, 28),
        f.priority
      ])
    });
    
    // Git 로그 읽기
    const { execSync } = require('child_process');
    try {
      const gitLog = execSync('git log --oneline -10', { encoding: 'utf8' });
      const lines = gitLog.split('\n').filter(Boolean);
      lines.forEach(line => this.logBox.log(line));
    } catch (error) {
      // Git 히스토리가 없을 수 있음
    }
    
    this.screen.render();
  }
  
  startUpdates() {
    this.update();
    setInterval(() => this.update(), 5000); // 5초마다 업데이트
  }
}

// 사용 예시
const dashboard = new ProgressDashboard('./feature_list.json');

2. 상세 로깅 시스템

# tools/detailed_logger.py
import logging
import json
from datetime import datetime
from pathlib import Path

class DetailedLogger:
    def __init__(self, project_dir):
        self.project_dir = Path(project_dir)
        self.log_dir = self.project_dir / "logs"
        self.log_dir.mkdir(exist_ok=True)
        
        # 로그 파일 설정
        self.session_log = self.log_dir / f"session_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
        
        # 로거 설정
        self.logger = logging.getLogger('AgentLogger')
        self.logger.setLevel(logging.DEBUG)
        
        # 파일 핸들러
        fh = logging.FileHandler(self.session_log)
        fh.setLevel(logging.DEBUG)
        
        # 콘솔 핸들러
        ch = logging.StreamHandler()
        ch.setLevel(logging.INFO)
        
        # 포맷터
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        fh.setFormatter(formatter)
        ch.setFormatter(formatter)
        
        self.logger.addHandler(fh)
        self.logger.addHandler(ch)
    
    def log_feature_start(self, feature):
        """기능 시작 로그"""
        self.logger.info(f"========== Feature #{feature['id']} 시작 ==========")
        self.logger.info(f"Title: {feature['title']}")
        self.logger.info(f"Description: {feature['description']}")
        self.logger.info(f"Priority: {feature.get('priority', 'medium')}")
    
    def log_feature_complete(self, feature, duration_minutes):
        """기능 완료 로그"""
        self.logger.info(f"========== Feature #{feature['id']} 완료 ==========")
        self.logger.info(f"Duration: {duration_minutes:.1f} minutes")
        
        # JSON 로그도 저장
        completion_data = {
            'feature_id': feature['id'],
            'title': feature['title'],
            'completed_at': datetime.now().isoformat(),
            'duration_minutes': duration_minutes
        }
        
        json_log = self.log_dir / "completions.jsonl"
        with open(json_log, 'a') as f:
            f.write(json.dumps(completion_data) + '\n')
    
    def log_test_result(self, test_name, passed, error=None):
        """테스트 결과 로그"""
        if passed:
            self.logger.info(f"✓ Test passed: {test_name}")
        else:
            self.logger.error(f"✗ Test failed: {test_name}")
            if error:
                self.logger.error(f"Error: {error}")
    
    def log_command(self, command, output, return_code):
        """명령어 실행 로그"""
        self.logger.debug(f"Command: {command}")
        self.logger.debug(f"Return code: {return_code}")
        if output:
            self.logger.debug(f"Output: {output[:500]}")  # 처음 500자만
    
    def log_error(self, error_message, context=None):
        """에러 로그"""
        self.logger.error(f"ERROR: {error_message}")
        if context:
            self.logger.error(f"Context: {json.dumps(context, indent=2)}")
    
    def generate_session_summary(self):
        """세션 요약 생성"""
        summary = {
            'session_start': self.session_log.stat().st_ctime,
            'session_end': datetime.now().timestamp(),
            'log_file': str(self.session_log)
        }
        
        summary_file = self.log_dir / f"summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
        with open(summary_file, 'w') as f:
            json.dump(summary, f, indent=2)
        
        return summary

배포 및 프로덕션

1. 최종 검증 체크리스트

#!/bin/bash
# final-validation.sh

echo "========== 최종 배포 전 검증 =========="

# 색상 정의
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

PASSED=0
FAILED=0

check_pass() {
    echo -e "${GREEN}${NC} $1"
    ((PASSED++))
}

check_fail() {
    echo -e "${RED}${NC} $1"
    ((FAILED++))
}

check_warn() {
    echo -e "${YELLOW}${NC} $1"
}

# 1. 모든 기능 완료 확인
echo -e "\n1. 기능 완료 상태 확인"
INCOMPLETE=$(cat feature_list.json | grep '"passes": false' | wc -l)
if [ $INCOMPLETE -eq 0 ]; then
    check_pass "모든 기능 완료 ($INCOMPLETE개 미완료)"
else
    check_fail "$INCOMPLETE개 기능 미완료"
fi

# 2. 테스트 실행
echo -e "\n2. 전체 테스트 실행"
if npm test > /dev/null 2>&1; then
    check_pass "모든 테스트 통과"
else
    check_fail "일부 테스트 실패"
fi

# 3. 코드 품질 검사
echo -e "\n3. 코드 품질 검사"
if command -v eslint &> /dev/null; then
    if npm run lint > /dev/null 2>&1; then
        check_pass "Lint 검사 통과"
    else
        check_warn "Lint 경고 있음"
    fi
else
    check_warn "ESLint 미설치"
fi

# 4. 보안 취약점 검사
echo -e "\n4. 보안 취약점 검사"
if npm audit --audit-level=high 2>&1 | grep -q "found 0 vulnerabilities"; then
    check_pass "심각한 보안 취약점 없음"
else
    check_fail "보안 취약점 발견"
fi

# 5. 빌드 테스트
echo -e "\n5. 프로덕션 빌드"
if npm run build > /dev/null 2>&1; then
    check_pass "빌드 성공"
else
    check_fail "빌드 실패"
fi

# 6. Git 상태 확인
echo -e "\n6. Git 상태 확인"
if [ -z "$(git status --porcelain)" ]; then
    check_pass "모든 변경사항 커밋됨"
else
    check_fail "미커밋 변경사항 있음"
fi

# 7. 문서 확인
echo -e "\n7. 문서 완성도 확인"
if [ -f "README.md" ] && [ -s "README.md" ]; then
    check_pass "README.md 존재"
else
    check_warn "README.md 업데이트 필요"
fi

# 최종 결과
echo -e "\n========== 검증 결과 =========="
echo -e "${GREEN}통과: $PASSED${NC}"
echo -e "${RED}실패: $FAILED${NC}"

if [ $FAILED -eq 0 ]; then
    echo -e "\n${GREEN}🎉 배포 가능!${NC}"
    exit 0
else
    echo -e "\n${RED}❌ 문제 해결 후 재시도${NC}"
    exit 1
fi

2. 프로덕션 배포 가이드

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [ main ]
  workflow_dispatch:

jobs:
  validate:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test
    
    - name: Validate feature list
      run: |
        INCOMPLETE=$(cat feature_list.json | grep '"passes": false' | wc -l)
        if [ $INCOMPLETE -ne 0 ]; then
          echo "❌ $INCOMPLETE features incomplete"
          exit 1
        fi
    
    - name: Build
      run: npm run build
    
    - name: Run final validation
      run: bash final-validation.sh
  
  deploy:
    needs: validate
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Deploy to production
      run: |
        # 배포 스크립트 실행
        echo "Deploying to production..."
        # 실제 배포 명령어

결론

장기 실행 AI 에이전트의 성공은 프롬프트 엔지니어링이 아니라 하네스 설계에 달려 있습니다. 이 가이드에서 다룬 핵심 원칙을 정리하면:

핵심 원칙

  1. Git은 메모리: 상세한 커밋 메시지로 세션 간 컨텍스트 연결
  2. JSON은 구조: feature_list.json으로 범위를 명확히 정의하고 임의 수정 방지
  3. 테스트는 검증: Puppeteer E2E 테스트와 스크린샷으로 실제 작동 확인
  4. 한 세션 = 한 기능: 작업을 관리 가능한 단위로 분할
  5. 명확한 인계: 다음 에이전트가 쉽게 이해할 수 있도록 문서화

성공을 위한 체크리스트

프로젝트 시작 시

  • app_spec.txt 작성 (명확한 요구사항)
  • Initializer Agent로 feature_list.json 생성
  • Git 저장소 초기화
  • init.sh 스크립트 설정
  • 보안 설정 (.claude_settings.json)

매 세션 시작 시

  • 세션 시작 루틴 실행 (위치 확인, 로그 읽기, Git 히스토리)
  • 다음 미완료 기능 식별
  • 개발 서버 실행 및 기존 테스트 확인

기능 구현 중

  • 단계별 구현 (feature의 steps 따르기)
  • 주기적 중간 커밋 (긴 작업 시)
  • 실시간 테스트로 문제 조기 발견

기능 완료 시

  • E2E 테스트 작성 및 실행
  • 스크린샷 저장 (verification/ 디렉토리)
  • 모든 테스트 통과 확인
  • feature_list.json 업데이트 (passes: true)
  • 상세한 Git 커밋
  • claude-progress.txt 업데이트

프로젝트 완료 시

  • 모든 기능 passes: true 확인
  • 전체 테스트 스위트 실행
  • 최종 검증 스크립트 실행
  • 프로덕션 빌드 테스트
  • 문서 완성도 확인

확장 가능성

이 패턴은 웹 애플리케이션뿐만 아니라 다음 분야에도 적용 가능합니다:

연구 프로젝트

  • 문헌 조사 → feature_list.json의 "논문"으로 매핑
  • 실험 실행 → Git으로 실험 결과 추적
  • 데이터 분석 → 단계별 분석 파이프라인

자동화 시스템

  • 작업 체크리스트 → feature_list.json
  • 실행 로그 → claude-progress.txt
  • 성공 검증 → 자동화 테스트

데이터 파이프라인

  • ETL 단계 → 개별 기능으로 분할
  • 데이터 품질 검사 → 테스트로 구현
  • 버전 관리 → Git으로 데이터 스키마 추적

추가 리소스

공식 문서

도구 및 라이브러리

커뮤니티


부록 A: 전체 프로젝트 템플릿

디렉토리 구조

my_autonomous_project/
├── .git/                          # Git 저장소
├── .cache/                        # 캐시 디렉토리
├── .backups/                      # 백업 파일
├── logs/                          # 상세 로그
│   ├── session_20240115_140000.log
│   ├── completions.jsonl
│   └── summary_20240115_180000.json
├── verification/                  # 테스트 증거
│   ├── feature-1-login.png
│   ├── feature-2-signup.png
│   └── videos/
├── tests/                         # 테스트 스위트
│   ├── e2e/
│   │   ├── login.test.js
│   │   └── signup.test.js
│   ├── unit/
│   └── integration/
├── tools/                         # 유틸리티 스크립트
│   ├── progress-tracker.js
│   ├── session-validator.js
│   ├── cache-system.js
│   └── safe-executor.js
├── prompts/                       # 에이전트 프롬프트
│   ├── initializer_prompt.md
│   └── coding_prompt.md
├── src/                           # 애플리케이션 코드
│   ├── components/
│   ├── api/
│   └── utils/
├── app_spec.txt                   # 애플리케이션 사양
├── feature_list.json              # 기능 체크리스트
├── claude-progress.txt            # 진행 로그
├── init.sh                        # 환경 설정 스크립트
├── .claude_settings.json          # 보안 설정
├── package.json                   # 의존성
└── README.md                      # 프로젝트 문서

package.json 템플릿

{
  "name": "autonomous-project",
  "version": "1.0.0",
  "description": "AI-built application",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "test": "jest",
    "test:e2e": "jest --testPathPattern=e2e",
    "test:unit": "jest --testPathPattern=unit",
    "lint": "eslint src/",
    "validate": "bash tools/final-validation.sh",
    "progress": "node tools/progress-tracker.js",
    "dashboard": "node tools/dashboard.js"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/jest": "^29.5.0",
    "eslint": "^8.40.0",
    "jest": "^29.5.0",
    "puppeteer": "^21.0.0",
    "playwright": "^1.40.0",
    "vite": "^5.0.0",
    "blessed": "^0.1.81",
    "blessed-contrib": "^4.11.0"
  }
}

.claude_settings.json 템플릿

{
  "version": "1.0",
  "security": {
    "allowed_commands": [
      "npm", "node", "git", "cat", "ls", "pwd",
      "mkdir", "touch", "cp", "mv", "rm", "echo"
    ],
    "allowed_directories": [
      "src/", "tests/", "public/", "verification/"
    ],
    "readonly_files": [
      "app_spec.txt",
      ".claude_settings.json"
    ],
    "max_file_size_mb": 10,
    "command_timeout_seconds": 30
  },
  "testing": {
    "required_before_passes": true,
    "screenshot_required": true,
    "screenshot_directory": "verification/"
  },
  "git": {
    "auto_commit": false,
    "require_commit_message": true,
    "min_commit_message_length": 20
  },
  "features": {
    "max_per_session": 1,
    "require_priority": true,
    "allowed_priorities": ["high", "medium", "low"]
  }
}

app_spec.txt 템플릿

# 애플리케이션 사양

## 프로젝트 개요
프로젝트명: [프로젝트 이름]
목적: [프로젝트의 목적과 목표]
대상 사용자: [타겟 사용자]

## 기술 스택
- 프론트엔드: React + Vite
- 스타일링: Tailwind CSS
- 백엔드: Node.js + Express
- 데이터베이스: PostgreSQL
- 테스팅: Jest + Puppeteer

## 핵심 기능

### 1. 사용자 인증
- 이메일/비밀번호 로그인
- 회원가입 (이메일 검증 포함)
- 비밀번호 재설정
- 소셜 로그인 (Google, GitHub)

### 2. 사용자 대시보드
- 개인 프로필 관리
- 활동 히스토리 조회
- 설정 페이지

### 3. [추가 기능들...]

## 비기능적 요구사항

### 성능
- 페이지 로드 시간 < 2초
- API 응답 시간 < 500ms
- 동시 사용자 1000명 지원

### 보안
- HTTPS 필수
- JWT 인증
- XSS/CSRF 방어
- Rate limiting

### 접근성
- WCAG 2.1 AA 준수
- 키보드 네비게이션 지원
- 스크린 리더 호환

### 반응형 디자인
- 모바일 (320px~)
- 태블릿 (768px~)
- 데스크톱 (1024px~)

## 제약사항
- 예산: [예산]
- 기간: [기간]
- 팀 규모: AI 에이전트 단독

## 성공 지표
- [ ] 모든 E2E 테스트 통과
- [ ] 200+ 기능 구현 완료
- [ ] 코드 커버리지 > 80%
- [ ] 성능 벤치마크 달성
- [ ] 보안 취약점 0건

부록 B: 문제 해결 플로우차트

세션 시작
    │
    ├─> 세션 시작 루틴 실행
    │   ├─> pwd, ls 실행
    │   ├─> app_spec.txt 읽기
    │   ├─> feature_list.json 읽기
    │   ├─> claude-progress.txt 읽기
    │   └─> git log 확인
    │
    ├─> 다음 기능 선택
    │   ├─> passes: false 찾기
    │   └─> 우선순위 확인
    │
    ├─> 개발 환경 실행
    │   ├─> ./init.sh 또는 npm run dev
    │   └─> 기존 테스트 실행
    │       ├─> 통과? → 계속
    │       └─> 실패? → 이전 버그 수정
    │
    ├─> 기능 구현
    │   ├─> 단계별 구현
    │   ├─> 중간 테스트
    │   └─> 코드 리뷰
    │
    ├─> E2E 테스트
    │   ├─> Puppeteer 테스트 작성
    │   ├─> 테스트 실행
    │   └─> 스크린샷 저장
    │       ├─> 통과? → 다음 단계
    │       └─> 실패? → 디버깅
    │
    ├─> 검증 및 커밋
    │   ├─> 모든 테스트 통과 확인
    │   ├─> feature_list.json 업데이트
    │   ├─> Git 커밋 (상세 메시지)
    │   └─> claude-progress.txt 업데이트
    │
    └─> 세션 종료
        ├─> 다음 기능 남음? → 새 세션 시작
        └─> 모든 기능 완료? → 최종 검증

부록 C: 에이전트 프롬프트 라이브러리

초기화 전용 프롬프트

# Initializer Agent - 고급 버전

당신은 장기 실행 자율 개발 시스템의 설계자입니다.
미래의 Coding Agent들이 효율적으로 작업할 수 있는 완벽한 기반을 구축하세요.

## Phase 1: 요구사항 분석 (10분)

1. app_spec.txt를 3번 읽으세요:
   - 1번째: 전체 개요 파악
   - 2번째: 핵심 기능 식별
   - 3번째: 비기능적 요구사항 확인

2. 불명확한 사항 질문:
   - 기술 스택 불명확?
   - 기능 우선순위 미정의?
   - 성능/보안 요구사항 누락?

## Phase 2: 아키텍처 설계 (15분)

1. 기술 스택 결정:
   
   Frontend: [선택 + 이유]
   Backend: [선택 + 이유]
   Database: [선택 + 이유]
   Testing: [선택 + 이유]
   

2. 디렉토리 구조 설계:
   - 확장 가능한 구조
   - 테스트 용이성
   - 명확한 책임 분리

## Phase 3: Feature Breakdown (30-45분)

200+ 상세한 E2E 테스트 케이스 작성.
각 기능은 반드시 포함:


{
  "id": 1,
  "title": "명확하고 구체적인 제목",
  "description": "구현해야 할 내용의 상세 설명",
  "steps": [
    "단계 1: 구체적 작업",
    "단계 2: 구체적 작업"
  ],
  "acceptance_criteria": [
    "테스트 가능한 조건 1",
    "테스트 가능한 조건 2"
  ],
  "priority": "high|medium|low",
  "estimatedMinutes": 45,
  "dependsOn": [/* 의존 기능 ID */],
  "tags": ["auth", "ui", "api"],
  "passes": false
}


**중요한 분할 원칙:**
- 각 기능은 45분 이내 완료 가능해야 함
- 기능 간 의존성 명시
- 우선순위 명확히 (의존성 고려)
- 테스트 가능한 acceptance criteria

## Phase 4: 환경 설정 (10분)

1. Git 저장소:

   git init
   git config user.name "Claude Agent"
   git config user.email "[email protected]"


2. init.sh 스크립트:

   #!/bin/bash
   set -e
   
   echo "🚀 개발 환경 시작"
   
   # 의존성 설치 (첫 실행 시)
   if [ ! -d "node_modules" ]; then
     npm install
   fi
   
   # 데이터베이스 마이그레이션
   npm run db:migrate
   
   # 개발 서버 시작
   npm run dev &
   
   # 서버 준비 대기
   sleep 5
   
   echo "✓ 개발 서버 실행 중: http://localhost:3000"


3. 첫 커밋:

   git add .
   git commit -m "Initial setup: complete project structure

   - Created 200+ feature test cases in feature_list.json
   - Set up development environment with [기술스택]
   - Configured testing framework
   - Added init.sh for automated environment setup
   
   Project structure:
   - src/: Application source code
   - tests/: E2E, unit, and integration tests
   - tools/: Utility scripts for validation and monitoring
   - verification/: Test evidence (screenshots, videos)
   
   Next: Feature #1 - [첫 번째 기능명]"


## Phase 5: 문서화 (5분)

README.md 생성:

# [프로젝트명]

## 현재 상태
- 총 기능: 200+
- 완료: 0
- 진행률: 0%

## 개발 시작하기
\`\`\`bash
./init.sh
\`\`\`

## 다음 작업
Feature #1: [제목]

## 에이전트 노트
- 모든 기능은 feature_list.json에 정의됨
- 각 세션은 한 기능만 구현
- 테스트 없이 passes:true 금지


## Phase 6: 검증 및 인계

1. 자체 검증:
   - [ ] feature_list.json 유효성
   - [ ] Git 저장소 초기화
   - [ ] init.sh 실행 가능
   - [ ] 모든 필수 파일 존재

2. 인계 메시지:

   ✓ 초기화 완료
   
   생성된 파일:
   - feature_list.json (200개 기능)
   - init.sh (환경 설정)
   - README.md
   - .claude_settings.json
   
   다음 Coding Agent를 위한 노트:
   - Feature #1부터 시작
   - 반드시 세션 시작 루틴 실행
   - 테스트 통과 후에만 passes:true
   
   첫 번째 기능: [상세 설명]

코딩 전용 프롬프트 (고급)

# Coding Agent - Production Grade

당신은 이전 세션의 작업을 이어받는 프로페셔널 개발자입니다.

## 필수 시작 루틴 (5분, 절대 스킵 불가)


# 1. 컨텍스트 수집
pwd
ls -la
cat app_spec.txt | head -30
cat feature_list.json | head -100
tail -100 claude-progress.txt
git log --oneline -20
git diff HEAD~1

# 2. 상태 확인
node << 'EOF'
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('feature_list.json'));
const total = data.features.length;
const done = data.features.filter(f => f.passes).length;
const next = data.features.find(f => !f.passes);

console.log(`진행률: ${done}/${total} (${(done/total*100).toFixed(1)}%)`);
console.log(`다음 작업: Feature #${next.id} - ${next.title}`);
console.log(`우선순위: ${next.priority}`);
console.log(`예상 시간: ${next.estimatedMinutes}분`);
EOF

# 3. 환경 실행
./init.sh

# 4. 기존 테스트 (회귀 방지)
npm test


## 기능 구현 프로세스

### Step 1: 계획 (5분)

선택한 기능을 분석하고 구현 계획 수립:


Feature #[ID]: [제목]

구현 전 체크리스트:
- [ ] Description 이해
- [ ] Steps 검토
- [ ] Acceptance criteria 확인
- [ ] 의존성 확인 (dependsOn)
- [ ] 영향받는 파일 식별

구현 계획:
1. [파일명]: [변경사항]
2. [파일명]: [변경사항]
3. 테스트: [테스트 시나리오]


### Step 2: 구현 (20-30분)

**TDD 방식 권장:**
1. 실패하는 테스트 먼저 작성
2. 테스트를 통과하는 최소 코드 작성
3. 리팩토링

**코드 품질 기준:**
- 명확한 변수/함수명
- 적절한 주석
- 에러 처리
- 타입 안전성 (TypeScript/PropTypes)
- 접근성 (a11y)


// 나쁜 예
function f(x) {
  return x.map(i => i.n);
}

// 좋은 예
/**
 * 사용자 목록에서 이름만 추출
 * @param {Array<User>} users - 사용자 객체 배열
 * @returns {Array<string>} 사용자 이름 배열
 */
function extractUserNames(users) {
  return users.map(user => user.name);
}


### Step 3: E2E 테스트 (10-15분)


// tests/e2e/feature-[ID].test.js
const puppeteer = require('puppeteer');

describe(`Feature #[ID]: [제목]`, () => {
  let browser, page;
  
  beforeAll(async () => {
    browser = await puppeteer.launch({
      headless: process.env.CI === 'true',
      slowMo: 50
    });
    page = await browser.newPage();
    await page.setViewport({ width: 1280, height: 720 });
  });
  
  afterAll(async () => {
    await browser.close();
  });
  
  test('[Acceptance Criteria 1]', async () => {
    // 1. 설정
    await page.goto('http://localhost:3000');
    
    // 2. 실행
    await page.click('#some-button');
    
    // 3. 검증
    const result = await page.$eval('#result', el => el.textContent);
    expect(result).toBe('Expected Value');
    
    // 4. 스크린샷 증거
    await page.screenshot({
      path: `verification/feature-[ID]-scenario-1.png`,
      fullPage: true
    });
  });
  
  // 모든 acceptance criteria에 대한 테스트...
});


### Step 4: 검증 (5분)


# 전체 테스트 스위트 실행
npm test

# 변경된 파일 확인
git status

# 코드 품질
npm run lint


### Step 5: 커밋 (5분)


git add .
git commit -m "feat: implement [기능명] (#[ID])

[구현 내용 상세 설명]

Changes:
- Created/Modified: [파일 목록]
- Added E2E tests: [테스트 파일]
- Test coverage: [커버리지]%

Testing:
- All acceptance criteria verified
- Screenshots: verification/feature-[ID]-*.png
- Manual testing: [수동 테스트 내용]

Performance:
- Page load: [시간]ms
- API response: [시간]ms

Next Feature: #[다음ID] - [다음 기능명]
"

# feature_list.json 업데이트
node << 'EOF'
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('feature_list.json'));
const feature = data.features.find(f => f.id === [현재ID]);
feature.passes = true;
feature.completed_at = new Date().toISOString();
fs.writeFileSync('feature_list.json', JSON.stringify(data, null, 2));
console.log(`✓ Feature #[현재ID] marked as complete`);
EOF

git add feature_list.json
git commit --amend --no-edit


### Step 6: 진행 로그 (2분)


echo "
=== Session $(date '+%Y-%m-%d %H:%M:%S') ===
Completed: Feature #[ID] - [제목]
Duration: ~[시간]분
Status: ✓ All tests passing
Screenshots: verification/feature-[ID]-*.png

Summary:
[구현 내용 요약]

Issues encountered:
[발생한 문제와 해결책]

Next: Feature #[다음ID] - [다음 기능명]
Priority: [우선순위]
Estimated: [예상시간]========================================
" >> claude-progress.txt


## 엣지 케이스 처리

### 테스트 실패 시

1. 헤드리스 모드 끄고 재시도
2. 단계별 스크린샷으로 디버깅
3. 콘솔 로그 확인
4. 타임아웃 증가
5. 필요시 이전 커밋으로 롤백

### 세션 시간 부족 시

1. 중간 체크포인트 커밋:

   git add .
   git commit -m "wip: partial implementation of feature #[ID]
   
   Progress:
   - [완료한 부분]
   
   Remaining:
   - [남은 작업]
   
   Note: 다음 세션에서 계속"


2. claude-progress.txt에 상세 메모

### 복잡한 기능 발견 시

1. feature_list.json에서 기능 분할:

   // Feature #15를 #15a, #15b, #15c로 분할
   {
     "id": "15a",
     "title": "[원제목] - Part 1: [세부사항]",
     "description": "...",
     "steps": ["..."],
     "dependsOn": [/* 이전 의존성 */],
     "passes": false
   },
   {
     "id": "15b",
     "title": "[원제목] - Part 2: [세부사항]",
     "dependsOn": ["15a"],
     ...
   }


## 세션 종료 전 체크리스트

- [ ] 모든 테스트 통과
- [ ] 스크린샷 저장 완료
- [ ] feature_list.json 업데이트
- [ ] Git 커밋 완료
- [ ] claude-progress.txt 업데이트
- [ ] 다음 기능 식별 완료
- [ ] 변경사항 리뷰 (git diff)

## 품질 기준

이 중 하나라도 미달이면 passes:true 금지:
- 모든 acceptance criteria 충족
- E2E 테스트 100% 통과
- 스크린샷 증거 존재
- 코드 리뷰 통과 (자체 검토)
- 성능 기준 충족
- 접근성 기준 충족
- 보안 체크 통과

맺음말

장기 실행 AI 에이전트 시스템은 단순히 AI를 오래 실행하는 것이 아니라, 구조화된 하네스를 통해 세션 간 연속성을 보장하는 것입니다. 이 가이드에서 제시한 패턴들은 Anthropic의 실전 경험에서 검증된 것이며, 며칠에서 몇 주에 걸친 복잡한 프로젝트도 안정적으로 완수할 수 있게 해줍니다.

핵심은 프롬프트가 아니라 시스템입니다. Git, JSON, 테스트라는 3가지 기둥 위에 에이전트가 작동하면, 컨텍스트 윈도우의 한계를 극복하고 진정한 자율성을 달성할 수 있습니다.

이 가이드가 여러분의 장기 실행 AI 에이전트 프로젝트 성공에 도움이 되기를 바랍니다.


문서 버전: 1.0.0
최종 업데이트: 2024-01-15
작성자: Claude (Anthropic)
라이선스: MIT


부록 D: FAQ (자주 묻는 질문)

Q1: 왜 한 세션에 한 기능만 구현해야 하나요?

A: 여러 가지 이유가 있습니다:

  1. 컨텍스트 윈도우 관리: 한 기능에 집중하면 관련 코드와 테스트만 로드하여 컨텍스트를 효율적으로 사용할 수 있습니다.

  2. 명확한 인계: 다음 세션의 에이전트가 정확히 무엇이 완료되었는지 알 수 있습니다.

  3. 롤백 용이성: 문제 발생 시 한 기능만 되돌리면 됩니다.

  4. 테스트 격리: 각 기능이 독립적으로 테스트되어 버그 추적이 쉽습니다.

단, 매우 작은 기능들(5-10분 소요)이 여러 개 있다면 관련된 기능들을 묶어서 처리할 수 있습니다.

Q2: feature_list.json의 적정 기능 개수는?

A: Anthropic의 권장사항은 200개 이상입니다. 이는 다음과 같은 이유에서입니다:

  • 각 기능이 충분히 작고 구체적이어야 함 (30-60분 소요)
  • 세밀한 진행 상황 추적 가능
  • 테스트 커버리지 향상

그러나 프로젝트 규모에 따라 조정 가능합니다:

  • 소규모 프로젝트: 50-100개
  • 중규모 프로젝트: 100-200개
  • 대규모 프로젝트: 200-500개

중요한 것은 개수가 아니라 각 기능의 크기와 명확성입니다.

Q3: 테스트가 계속 실패하면 어떻게 하나요?

A: 단계적 디버깅 접근:

  1. 헤드리스 모드 끄기
const browser = await puppeteer.launch({ 
  headless: false,  // 브라우저를 볼 수 있게
  slowMo: 250       // 동작을 천천히
});
  1. 단계별 스크린샷
await page.screenshot({ path: 'debug-step-1.png' });
// 각 단계마다 스크린샷
  1. 콘솔 로그 캡처
page.on('console', msg => console.log('PAGE:', msg.text()));
  1. 대기 시간 증가
await page.waitForSelector('#element', { timeout: 30000 });
  1. 최후의 수단: 기능 분할
  • 복잡한 기능을 더 작은 단위로 쪼개기

Q4: 이전 세션이 버그를 남겼을 때는?

A: 세션 시작 루틴에서 기존 테스트를 실행하는 이유가 바로 이것입니다:

# 세션 시작 시
npm test  # 기존 테스트 실행

# 실패한 테스트 발견 시
git log --oneline -20  # 최근 커밋 확인
git show HEAD          # 마지막 변경사항 확인
git diff HEAD~1        # 이전 버전과 비교

# 필요시 롤백
git revert HEAD
# 또는
git reset --hard HEAD~1

버그 수정 후:

git commit -m "fix: resolve regression in feature #X

Previous session introduced bug in [기능].
Root cause: [원인]
Solution: [해결책]

Tests: All passing now"

Q5: 세션이 중간에 끊기면?

A: 이것이 바로 하네스 시스템의 진가가 드러나는 순간입니다:

  1. Git이 메모리 역할: 마지막 커밋까지의 작업은 안전하게 보존됩니다.

  2. 다음 세션 시작 시:

git status  # 커밋되지 않은 변경사항 확인

# WIP(Work In Progress) 커밋이 있다면
git log -1  # 마지막 커밋 메시지 읽기
# "wip: partial implementation..." 메시지에서 진행 상황 파악
  1. 예방책: 주기적 체크포인트
# 20분마다 자동 체크포인트
if time_elapsed > 20 * 60:
    subprocess.run(['git', 'add', '.'])
    subprocess.run(['git', 'commit', '-m', f'checkpoint: {timestamp}'])

Q6: 여러 에이전트를 동시에 실행할 수 있나요?

A: 가능하지만 주의가 필요합니다:

안전한 병렬 실행 조건:

  • 기능 간 의존성이 없음
  • 서로 다른 파일을 수정함
  • 각자 독립된 작업 디렉토리 사용

구현 예시:

# 3개 기능을 병렬 처리
async def parallel_execution():
    features = [
        feature_15,  # 프론트엔드
        feature_16,  # 백엔드 API
        feature_17   # 문서화
    ]
    
    # 각 에이전트에 별도 디렉토리 할당
    tasks = [
        run_agent(f, f'./workspace/agent-{f.id}')
        for f in features
    ]
    
    results = await asyncio.gather(*tasks)

주의사항:

  • feature_list.json 동시 수정 방지 (락 메커니즘 필요)
  • Git 충돌 가능성
  • 비용 증가 (여러 API 호출)

대부분의 경우 순차 실행이 더 안전하고 효율적입니다.

Q7: 프라이빗 API 키나 시크릿은 어떻게 관리하나요?

A: 절대 코드에 하드코딩하지 마세요:

// ❌ 나쁜 예
const API_KEY = "sk-1234567890abcdef";

// ✅ 좋은 예
const API_KEY = process.env.API_KEY;

권장 방법:

  1. .env 파일 사용
# .env (Git에 커밋하지 않음)
API_KEY=sk-1234567890abcdef
DATABASE_URL=postgresql://user:pass@localhost/db
  1. .gitignore에 추가
.env
.env.local
*.key
*.pem
secrets/
  1. .env.example 제공
# .env.example (Git에 커밋)
API_KEY=your_api_key_here
DATABASE_URL=your_database_url_here
  1. 에이전트에게 명시적 지시
## 보안 규칙

절대 금지:
- API 키를 코드에 하드코딩
- .env 파일을 Git에 커밋
- 로그에 시크릿 출력

필수:
- 환경 변수 사용
- .gitignore에 시크릿 파일 추가
- .env.example 제공

Q8: 데이터베이스 마이그레이션은 어떻게 관리하나요?

A: 데이터베이스 스키마도 Git으로 버전 관리:

migrations/
├── 001_initial_schema.sql
├── 002_add_users_table.sql
├── 003_add_email_column.sql
└── 004_create_sessions_table.sql

마이그레이션 스크립트:

// tools/migrate.js
const fs = require('fs');
const path = require('path');
const { Pool } = require('pg');

async function runMigrations() {
  const pool = new Pool({ connectionString: process.env.DATABASE_URL });
  
  // 마이그레이션 테이블 생성
  await pool.query(`
    CREATE TABLE IF NOT EXISTS migrations (
      id SERIAL PRIMARY KEY,
      filename VARCHAR(255) UNIQUE,
      applied_at TIMESTAMP DEFAULT NOW()
    )
  `);
  
  // 실행된 마이그레이션 조회
  const { rows } = await pool.query(
    'SELECT filename FROM migrations'
  );
  const applied = new Set(rows.map(r => r.filename));
  
  // 미실행 마이그레이션 찾기
  const migrationDir = path.join(__dirname, '../migrations');
  const files = fs.readdirSync(migrationDir)
    .filter(f => f.endsWith('.sql'))
    .sort();
  
  for (const file of files) {
    if (applied.has(file)) continue;
    
    console.log(`Running migration: ${file}`);
    const sql = fs.readFileSync(
      path.join(migrationDir, file),
      'utf8'
    );
    
    await pool.query('BEGIN');
    try {
      await pool.query(sql);
      await pool.query(
        'INSERT INTO migrations (filename) VALUES ($1)',
        [file]
      );
      await pool.query('COMMIT');
      console.log(`✓ ${file} applied`);
    } catch (error) {
      await pool.query('ROLLBACK');
      console.error(`✗ ${file} failed:`, error.message);
      throw error;
    }
  }
  
  await pool.end();
}

runMigrations().catch(console.error);

feature_list.json에 포함:

{
  "id": 25,
  "title": "데이터베이스 마이그레이션: 사용자 테이블",
  "steps": [
    "migrations/002_add_users_table.sql 생성",
    "마이그레이션 스크립트 실행",
    "테이블 생성 확인"
  ]
}

Q9: 에이전트가 무한 루프에 빠지면?

A: 방지 메커니즘 구현:

# tools/safety_monitor.py
import time
from collections import defaultdict

class SafetyMonitor:
    def __init__(self):
        self.action_history = []
        self.action_counts = defaultdict(int)
        self.start_time = time.time()
    
    def record_action(self, action):
        """액션 기록"""
        self.action_history.append({
            'action': action,
            'timestamp': time.time()
        })
        self.action_counts[action] += 1
    
    def detect_loop(self, threshold=5):
        """무한 루프 감지"""
        # 같은 액션이 5번 이상 반복
        for action, count in self.action_counts.items():
            if count >= threshold:
                return True, f"Action '{action}' repeated {count} times"
        
        # 최근 10개 액션이 모두 동일
        if len(self.action_history) >= 10:
            recent = self.action_history[-10:]
            if len(set(a['action'] for a in recent)) == 1:
                return True, "Same action repeated 10 times in a row"
        
        return False, None
    
    def check_timeout(self, max_minutes=60):
        """타임아웃 확인"""
        elapsed = (time.time() - self.start_time) / 60
        if elapsed > max_minutes:
            return True, f"Session exceeded {max_minutes} minutes"
        return False, None
    
    def should_stop(self):
        """중단해야 하는지 확인"""
        loop_detected, loop_msg = self.detect_loop()
        if loop_detected:
            print(f"⚠️  무한 루프 감지: {loop_msg}")
            return True
        
        timeout, timeout_msg = self.check_timeout()
        if timeout:
            print(f"⚠️  타임아웃: {timeout_msg}")
            return True
        
        return False

# 사용 예시
monitor = SafetyMonitor()

while not monitor.should_stop():
    action = agent.get_next_action()
    monitor.record_action(action)
    agent.execute(action)

Q10: 프로젝트가 완료되었는지 어떻게 확인하나요?

A: 다단계 검증:

1. feature_list.json 확인

node << 'EOF'
const data = require('./feature_list.json');
const incomplete = data.features.filter(f => !f.passes);

if (incomplete.length === 0) {
  console.log('✅ 모든 기능 완료!');
} else {
  console.log(`❌ ${incomplete.length}개 기능 미완료:`);
  incomplete.forEach(f => {
    console.log(`  - #${f.id}: ${f.title}`);
  });
}
EOF

2. 전체 테스트 스위트

npm test -- --coverage

3. 최종 검증 스크립트

bash tools/final-validation.sh

4. 수동 검수

  • 모든 주요 사용자 시나리오 테스트
  • 다양한 브라우저에서 확인
  • 모바일 반응형 확인
  • 성능 벤치마크
  • 보안 체크리스트

5. 프로덕션 빌드

npm run build
npm run preview  # 빌드된 버전 테스트

부록 E: 실전 사례 연구

사례 1: E-커머스 웹사이트 (3일 프로젝트)

프로젝트 개요:

  • 목표: 완전한 기능의 온라인 쇼핑몰
  • 기간: 3일 (72시간)
  • 기능 수: 250개
  • 최종 결과: 248개 완료 (99.2%)

타임라인:

Day 1 (8시간)

  • 0-2시간: Initializer Agent (환경 설정)
  • 2-8시간: 12개 기능 완료
    • 기본 레이아웃
    • 네비게이션
    • 홈페이지
    • 제품 목록 페이지

Day 2 (10시간)

  • 28개 기능 완료
    • 사용자 인증
    • 제품 상세 페이지
    • 장바구니
    • 검색 기능

Day 3 (12시간)

  • 208개 기능 완료 (가속화)
    • 결제 시스템
    • 주문 관리
    • 리뷰 시스템
    • 관리자 대시보드
    • 이메일 알림
    • 성능 최적화

주요 학습:

  1. 초반이 느림: 첫날은 구조를 잡는 시간. 정상입니다.
  2. 가속 효과: 구조가 잡히면 속도가 급격히 증가
  3. 재사용 패턴: 비슷한 기능(CRUD)은 복사-수정으로 빠르게 처리
  4. 테스트 자동화: E2E 테스트 템플릿 만들어두니 반복 작업 감소

성공 요인:

  • 명확한 feature_list.json (250개 상세 기능)
  • 일관된 커밋 패턴
  • 철저한 테스트 (스크린샷 500장+)

사례 2: 데이터 분석 대시보드 (5일 프로젝트)

프로젝트 개요:

  • 목표: 실시간 데이터 시각화 대시보드
  • 기간: 5일
  • 기능 수: 180개
  • 도전: 복잡한 데이터 처리 로직

특이사항:

이 프로젝트는 반복적 실패를 경험했습니다:

첫 번째 시도 (실패)

  • 문제: feature_list.json이 너무 추상적
    {
      "id": 1,
      "title": "데이터 처리 시스템",  // 너무 광범위!
      "description": "모든 데이터 처리"
    }
  • 결과: 에이전트가 범위를 정하지 못하고 방황

두 번째 시도 (부분 성공)

  • 개선: 기능을 더 작게 쪼갬
  • 문제: 의존성 관리 실패
    • Feature #50이 #30에 의존하는데 먼저 구현됨
  • 결과: 40% 지점에서 리팩토링 필요

세 번째 시도 (성공)

  • 핵심 변경:
    1. 명확한 의존성 그래프
    2. 우선순위 기반 정렬
    3. 각 기능 30분 이내로 제한

최종 feature_list.json 구조:

{
  "id": 15,
  "title": "CSV 파싱 - 기본 구조",
  "estimatedMinutes": 25,
  "dependsOn": [],
  "passes": false
},
{
  "id": 16,
  "title": "CSV 파싱 - 타입 변환",
  "estimatedMinutes": 30,
  "dependsOn": [15],
  "passes": false
},
{
  "id": 17,
  "title": "차트 컴포넌트 - 라인 그래프",
  "estimatedMinutes": 35,
  "dependsOn": [16],
  "passes": false
}

학습:

  • 초기 설계가 중요 (Initializer Agent에 시간 투자)
  • 의존성 명시가 필수
  • 실패도 학습 과정

사례 3: API 통합 서비스 (연구용, 2주)

프로젝트 개요:

  • 목표: 10개 외부 API 통합
  • 기간: 2주
  • 기능 수: 420개
  • 특징: 많은 외부 의존성

도전 과제:

  1. API 키 관리

    • 해결: .env 파일 + 시크릿 관리 시스템
    • Git 훅으로 실수 방지
  2. Rate Limiting

    • 해결: 캐싱 시스템 구현
    • 테스트 시 Mock API 사용
  3. 외부 서비스 다운타임

    • 해결: 재시도 로직 + 폴백 메커니즘
    async function callAPIWithRetry(url, maxRetries = 3) {
      for (let i = 0; i < maxRetries; i++) {
        try {
          return await fetch(url);
        } catch (error) {
          if (i === maxRetries - 1) throw error;
          await sleep(1000 * (i + 1)); // 지수 백오프
        }
      }
    }

성공 전략:

  • 각 API를 별도 기능으로 분리
  • Mock 데이터로 오프라인 개발 가능
  • 통합 테스트에 실제 API 사용

부록 F: 도구 및 스크립트 모음

완전 자동화 스크립트

#!/bin/bash
# auto-agent.sh - 완전 자동화된 에이전트 실행

set -e

PROJECT_DIR="$1"
SESSION_LIMIT="${2:-10}"  # 최대 10세션

if [ -z "$PROJECT_DIR" ]; then
  echo "사용법: $0 <프로젝트_디렉토리> [세션_제한]"
  exit 1
fi

cd "$PROJECT_DIR"

echo "🤖 자율 에이전트 시작"
echo "프로젝트: $PROJECT_DIR"
echo "최대 세션: $SESSION_LIMIT"
echo ""

# 초기화 확인
if [ ! -f "feature_list.json" ]; then
  echo "📋 Initializer Agent 실행..."
  python3 autonomous_agent.py --mode initialize
fi

# 메인 루프
for session in $(seq 1 $SESSION_LIMIT); do
  echo ""
  echo "========================================"
  echo "세션 #$session 시작"
  echo "========================================"
  
  # 남은 작업 확인
  REMAINING=$(cat feature_list.json | grep '"passes": false' | wc -l)
  
  if [ $REMAINING -eq 0 ]; then
    echo "🎉 모든 기능 완료!"
    break
  fi
  
  echo "남은 기능: $REMAINING"
  
  # Coding Agent 실행
  python3 autonomous_agent.py --mode code
  
  # 검증
  if npm test > /dev/null 2>&1; then
    echo "✅ 테스트 통과"
  else
    echo "⚠️  테스트 실패 - 중단"
    break
  fi
  
  # 진행 상황 출력
  COMPLETED=$(cat feature_list.json | grep '"passes": true' | wc -l)
  TOTAL=$(cat feature_list.json | grep '"id"' | wc -l)
  PERCENTAGE=$(echo "scale=1; $COMPLETED * 100 / $TOTAL" | bc)
  
  echo "진행률: $COMPLETED/$TOTAL ($PERCENTAGE%)"
  
  # 쿨다운 (Rate limiting 방지)
  sleep 2
done

echo ""
echo "========================================"
echo "자동 실행 완료"
echo "========================================"

# 최종 보고서 생성
node tools/progress-tracker.js

Git 훅 설정

#!/bin/bash
# setup-git-hooks.sh - Git 훅 자동 설정

HOOKS_DIR=".git/hooks"

# pre-commit 훅
cat > "$HOOKS_DIR/pre-commit" << 'EOF'
#!/bin/bash

echo "🔍 사전 커밋 검증..."

# 1. 금지된 파일 확인
FORBIDDEN_FILES=(".env" "*.key" "*.pem" "secrets/*")

for pattern in "${FORBIDDEN_FILES[@]}"; do
  if git diff --cached --name-only | grep -q "$pattern"; then
    echo "❌ 금지된 파일 커밋 시도: $pattern"
    exit 1
  fi
done

# 2. feature_list.json 검증
if git diff --cached --name-only | grep -q "feature_list.json"; then
  python3 << 'PYTHON'
import json
import sys

try:
    with open('feature_list.json') as f:
        data = json.load(f)
    
    # passes:true인데 커밋이 없는 경우 확인
    import subprocess
    git_log = subprocess.check_output(['git', 'log', '--all']).decode()
    
    for feature in data['features']:
        if feature.get('passes') and f"#{feature['id']}" not in git_log:
            print(f"❌ Feature #{feature['id']} passes:true but no commit found")
            sys.exit(1)
    
    print("✅ feature_list.json 검증 통과")
except Exception as e:
    print(f"❌ 검증 실패: {e}")
    sys.exit(1)
PYTHON

  if [ $? -ne 0 ]; then
    exit 1
  fi
fi

# 3. 린트 검사 (있는 경우)
if command -v npm &> /dev/null && [ -f "package.json" ]; then
  if npm run lint --if-present 2>&1 | grep -q "error"; then
    echo "❌ 린트 에러 발견"
    exit 1
  fi
fi

echo "✅ 사전 커밋 검증 통과"
EOF

chmod +x "$HOOKS_DIR/pre-commit"

# commit-msg 훅
cat > "$HOOKS_DIR/commit-msg" << 'EOF'
#!/bin/bash

COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")

# 최소 길이 확인
if [ ${#COMMIT_MSG} -lt 20 ]; then
  echo "❌ 커밋 메시지가 너무 짧습니다 (최소 20자)"
  exit 1
fi

# 패턴 확인 (feat:, fix:, docs: 등)
if ! echo "$COMMIT_MSG" | grep -qE "^(feat|fix|docs|style|refactor|test|chore):"; then
  echo "⚠️  권장 형식: feat|fix|docs|style|refactor|test|chore: 메시지"
fi

echo "✅ 커밋 메시지 검증 통과"
EOF

chmod +x "$HOOKS_DIR/commit-msg"

echo "✅ Git 훅 설정 완료"

장기 실행 AI 에이전트 만들 때 제일 큰 문제?

👉 컨텍스트 윈도우 한계로 “기억 상실” 😵
Anthropic의 해법: Initializer + Coding Agent 하네스
며칠짜리 웹앱 빌드도 안정적으로 가능
AIAgents #Claude

2/7
대표 실패 패턴 ❌
• 한 번에 다 끝내려다(one-shot) 망함
• 중간에 “완료!” 선언(early victory)
해결 👉 작업을 작은 기능 단위로 쪼개고
JSON 체크리스트로 범위 강제

3/7
Initializer Agent 역할 🧠
• 첫 세션에서 feature_list.json 생성
• 각 기능에 description / steps / passes:false
이 JSON이 에이전트의 To-Do 리스트
구조화 덕분에 임의 수정도 방지됨

4/7
운영 방식 💻
• Git repo 초기화
• claude-progress.txt에 세션 로그 기록
• https://init.sh로 dev 서버 자동 실행
Coding Agent는 한 세션에 한 기능만 구현
→ 세션 종료 시 완전한 핸드오버

5/7
세션 시작 루틴 🔄

현재 디렉토리 확인
진행 로그 + git log 읽기
JSON에서 다음 미완료 기능 선택
dev 서버 + 기본 E2E 테스트 “어제 뭐 했지?” 문제 완전 해결

6/7
품질 관리 핵심 ⚠️
• 구현 후 반드시 Git 커밋 (상세 메시지)
• 테스트 통과 전엔 passes:true 금지
• Puppeteer로 실제 사용자 시나리오 테스트
→ 사람 엔지니어처럼 클린 핸드오버

7/7
결론 🧩
장기 AI 에이전트의 핵심은
👉 프롬프트가 아니라 하네스
(Git = 메모리, JSON = 구조, 테스트 = 검증)
웹·연구·자동화 전부 재사용 가능

Anthropic 원문

Quickstart

Context Engineering: AI 시대의 새로운 핵심 역량

⚠️ **GitHub.com Fallback** ⚠️