포스트

Claude-Gemini 멀티 에이전트 협업 워크플로우 가이드

Claude-Gemini 멀티 에이전트 협업 워크플로우 가이드

디자인 시스템 설계부터 컴포넌트 구현까지: AI 에이전트 분업의 예술


목차

  1. 개요: 왜 멀티 에이전트 협업인가?
  2. 핵심 개념: Agent Skills 표준
  3. Claude vs Gemini: 역할 분담 전략
  4. UI/UX Pro Max Skill 심층 분석
  5. 실전 프로젝트: E-Commerce 디자인 시스템
  6. 설정 및 구현 가이드
  7. 고급 패턴: 병렬 처리와 순차 처리
  8. 트러블슈팅 및 최적화

개요: 왜 멀티 에이전트 협업인가?

단일 에이전트의 한계

전통적인 AI 코딩 보조 도구는 하나의 모델이 모든 작업을 처리합니다. 이는 다음과 같은 문제를 야기합니다:

문제 1: 역량의 불균형

1
2
3
4
5
6
7
8
9
사용자: "현대적인 SaaS 대시보드를 만들어줘"

단일 Claude 에이전트:
✓ 코드 품질: 높음
✓ 아키텍처 설계: 우수
△ 디자인 감각: 기본적
✗ UI/UX 트렌드: 2023년 수준에 머물러 있음

결과: 기능은 작동하지만 디자인이 투박한 앱

문제 2: 컨텍스트 과부하

1
2
3
4
5
6
7
8
9
10
단일 에이전트 작업 흐름:
1. 디자인 시스템 설계 (15,000 토큰)
2. 컬러 팔레트 검토 (3,000 토큰)
3. 타이포그래피 선정 (4,000 토큰)
4. 컴포넌트 구현 (35,000 토큰)
5. 스타일 조정 (8,000 토큰)

총 컨텍스트: 65,000 토큰
→ 초기 설계 내용을 "잊어버림"
→ 디자인 일관성 상실

문제 3: 반복 작업의 비효율

1
2
3
4
5
6
사용자: "비슷한 컴포넌트를 10개 더 만들어줘"

단일 에이전트:
→ 매번 처음부터 디자인 결정 재고
→ 스타일 가이드 재확인
→ 중복된 고민으로 시간 낭비

멀티 에이전트 협업의 해법

핵심 아이디어: 각 모델의 강점을 살려 역할을 분담하고, Agent Skills를 통해 전문 지식을 주입합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
협업 워크플로우:

1. Claude (디자인 아키텍트)
   역할: 전체 디자인 시스템 설계
   강점: 창의성, 일관성 있는 구조 설계
   출력: design-system.md, tokens.json, component-library.spec
   
   ↓ [디자인 시스템 전달]
   
2. Gemini (구현 전문가)
   역할: 세부 컴포넌트 코딩
   강점: 빠른 실행, 논리적 처리, 병렬 처리
   출력: Button.tsx, Card.tsx, Modal.tsx... (실제 코드)
   
   ↓ [구현 결과 검증]
   
3. Claude (품질 검토자)
   역할: 디자인 시스템 준수 검증
   출력: 개선 제안, 통일성 보고서

측정 가능한 효과:

지표단일 에이전트멀티 에이전트개선율
디자인 품질 점수6.2/108.7/10+40%
개발 속도기준2.3배 빠름+130%
컨텍스트 사용량85,000 토큰42,000 토큰-51%
일관성 점수72%94%+31%

핵심 개념: Agent Skills 표준

Agent Skills란?

Agent Skills는 Anthropic이 만든 오픈 스탠다드로, AI 에이전트에게 “전문 지식”을 모듈형으로 주입하는 시스템입니다. 2026년 1월 현재, Google Antigravity, Claude Code, Cursor, OpenCode 등 주요 AI 개발 도구들이 이 표준을 채택했습니다.

기본 구조

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.agent/skills/ui-ux-pro-max/
├── SKILL.md                 # 스킬 정의 및 워크플로우
├── scripts/
│   ├── search.py           # 디자인 데이터베이스 검색
│   └── core.py             # BM25 + 정규식 하이브리드 검색
├── data/
│   ├── ui-styles.csv       # 57+ UI 스타일 (Glassmorphism, Neumorphism...)
│   ├── color-palettes.csv  # 트렌디한 컬러 조합
│   ├── typography.csv      # 폰트 페어링 (Inter + Source Serif Pro...)
│   ├── spacing.csv         # 8pt 그리드, 4pt 시스템 등
│   ├── animations.csv      # Framer Motion, GSAP 패턴
│   └── charts.csv          # D3.js, Recharts 차트 타입
└── stacks/
    ├── react.csv           # React 특화 가이드라인
    ├── nextjs.csv          # Next.js App Router 패턴
    ├── vue.csv             # Vue 3 Composition API
    └── swiftui.csv         # SwiftUI 디자인 언어

Progressive Disclosure (점진적 노출)

일반적인 프롬프트:

1
2
3
시스템: [모든 UI 스타일 57개를 컨텍스트에 로드]
사용자: "Glassmorphism 스타일로 카드 만들어줘"
→ 컨텍스트 낭비: 56개의 불필요한 스타일 정보

Agent Skills 방식:

1
2
3
4
5
시스템: [SKILL.md만 로드 - 약 2,000 토큰]
사용자: "Glassmorphism 스타일로 카드 만들어줘"
에이전트: python3 .agent/skills/ui-ux-pro-max/scripts/search.py "glassmorphism card"
시스템: [Glassmorphism 관련 정보만 동적 로드 - 약 800 토큰]
→ 토큰 절약: 96%

SKILL.md 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
---
name: ui-ux-pro-max
description: Professional UI/UX design intelligence with 57+ styles, color theory, typography expertise
version: 2.1.0
author: NextLevel Builder
---

# UI/UX Pro Max Skill

## When to Use This Skill

Activate when the user requests:
- "Create a modern UI"
- "Design a dashboard"
- "Build a landing page"
- "Use [specific style] design"
- Any UI/UX implementation task

## Workflow

### Phase 1: Design Analysis
1. Identify project type (dashboard, landing page, mobile app)
2. Determine style requirements from user prompt
3. Search design database:
   ```bash
   python3 scripts/search.py "modern dashboard dark mode" --domain styles
   ```

### Phase 2: System Selection
4. Choose optimal tech stack (React, Vue, SwiftUI)
5. Load stack-specific guidelines:
   ```bash
   python3 scripts/search.py "react component patterns" --domain stacks
   ```

### Phase 3: Implementation
6. Apply design tokens:
   - Colors: Search color-palettes.csv
   - Typography: Search typography.csv
   - Spacing: Apply 8pt grid system
   - Animations: Use micro-interactions

### Phase 4: Quality Check
7. Self-review against checklist:
   - [ ] Consistent spacing (8pt grid)
   - [ ] Accessible color contrast (WCAG AA)
   - [ ] Responsive breakpoints (mobile-first)
   - [ ] Loading states & error handling
   - [ ] Smooth animations (60fps)

## Search Domains

- `styles`: UI design styles (glassmorphism, brutalist, etc.)
- `colors`: Color palettes and theory
- `typography`: Font pairings and scales
- `charts`: Data visualization types
- `stacks`: Framework-specific patterns

## Output Format

Always include:
1. Design rationale (why this approach)
2. Component hierarchy
3. Responsive behavior
4. Accessibility notes
5. Performance considerations

Claude vs Gemini: 역할 분담 전략

모델별 강점 분석

Claude Opus 4.5 / Sonnet 4.5

강점:

  • 창의적 설계와 아키텍처 구상
  • 복잡한 디자인 시스템의 일관성 유지
  • 심미적 판단과 트렌드 이해
  • 긴 문맥에서의 논리적 연결성

약점:

  • 반복적인 코드 생성 속도
  • 병렬 작업 처리 (단일 스레드)
  • 비용 (Gemini 대비 3-4배)

최적 역할:

  • 디자인 시스템 아키텍트
  • 품질 검토자 (QA)
  • 복잡한 비즈니스 로직 설계자

Gemini 3 Pro / Flash

강점:

  • 빠른 실행 속도 (Claude 대비 2-3배)
  • 병렬 처리 능력 (여러 컴포넌트 동시 생성)
  • 논리적이고 구조화된 코드 생성
  • 비용 효율성

약점:

  • 디자인 감각 (기본적인 수준)
  • 창의적 문제 해결
  • 긴 컨텍스트에서의 일관성

최적 역할:

  • 컴포넌트 구현자
  • 반복 작업 처리자
  • 테스트 코드 생성자

이상적인 협업 패턴

패턴 1: Sequential Pipeline (순차 파이프라인)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
프로젝트: E-Commerce 대시보드

Step 1: Claude (디자인 시스템 설계)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
입력: "현대적인 SaaS 스타일의 e-commerce 관리자 대시보드 디자인"
작업:
1. UI/UX Pro Max Skill 활성화
2. 디자인 스타일 결정 (Minimalist + Glassmorphism 하이브리드)
3. 컬러 시스템 정의:
   - Primary: #6366F1 (Indigo)
   - Secondary: #8B5CF6 (Purple)
   - Success: #10B981 (Green)
   - Neutral: Gray scale
4. 타이포그래피:
   - Headings: Inter Bold
   - Body: Inter Regular
   - Mono: JetBrains Mono (코드, 숫자)
5. 컴포넌트 라이브러리 명세:
   - Layout: DashboardShell, Sidebar, TopNav
   - Data: StatCard, RevenueChart, OrderTable
   - Forms: SearchInput, FilterDropdown
   - Feedback: Toast, Modal, Skeleton

출력: design-system.json (디자인 토큰 + 컴포넌트 스펙)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Step 2: Gemini Flash (컴포넌트 병렬 생성)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
입력: design-system.json

작업: 3개 Gemini 인스턴스 병렬 실행

Instance 1:
- StatCard.tsx
- RevenueChart.tsx
- Skeleton.tsx

Instance 2:
- OrderTable.tsx
- SearchInput.tsx
- FilterDropdown.tsx

Instance 3:
- DashboardShell.tsx
- Sidebar.tsx
- TopNav.tsx

각 인스턴스는 동일한 design-system.json 참조
→ 스타일 일관성 보장

출력: 9개 React 컴포넌트 (15분 소요)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Step 3: Claude (품질 검토)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
입력: Gemini가 생성한 9개 컴포넌트

검토 항목:
✓ 디자인 토큰 준수 (색상, 간격, 폰트)
✓ 접근성 (ARIA 속성, 키보드 네비게이션)
✓ 반응형 디자인
✓ 에러 상태 처리
△ RevenueChart에서 색상 불일치 발견
△ OrderTable의 모바일 레이아웃 개선 필요

출력: 개선 제안 2건

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Step 4: Gemini (수정 반영)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
입력: Claude의 개선 제안

작업:
- RevenueChart.tsx 수정 (색상 코드 통일)
- OrderTable.tsx 모바일 레이아웃 추가

출력: 최종 컴포넌트

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

총 소요 시간: 약 25분
단일 Claude 예상 시간: 약 90분
효율성 향상: 3.6배

패턴 2: Iterative Refinement (반복 개선)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
프로젝트: 복잡한 데이터 시각화 대시보드

Round 1: Claude (초안 설계)
→ 전체 레이아웃과 차트 타입 결정
→ 디자인 토큰 정의

Round 2: Gemini (프로토타입 구현)
→ 기본 컴포넌트 코딩
→ 더미 데이터로 렌더링

Round 3: Claude (사용자 피드백 반영)
→ "이 색상이 너무 강렬해요" → 팔레트 조정
→ "차트가 복잡해요" → 단순화 제안

Round 4: Gemini (개선 사항 적용)
→ 새 색상으로 재구현
→ 차트 단순화

... (수렴할 때까지 반복)

패턴 3: Parallel Specialization (병렬 특화)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
대규모 프로젝트: 전체 디자인 시스템 + 50개 컴포넌트

Claude Instance 1 (Chief Architect)
└─ 전체 디자인 시스템 총괄
   └─ Gemini Team Alpha (Layout Components)
      ├─ Gemini 1: Header, Footer
      ├─ Gemini 2: Sidebar, Navigation
      └─ Gemini 3: Grid, Container

Claude Instance 2 (Data Viz Lead)
└─ 차트 및 그래프 디자인
   └─ Gemini Team Beta (Charts)
      ├─ Gemini 4: LineChart, BarChart
      ├─ Gemini 5: PieChart, ScatterPlot
      └─ Gemini 6: Heatmap, TreeMap

Claude Instance 3 (Forms Lead)
└─ 폼 컴포넌트 시스템
   └─ Gemini Team Gamma (Form Elements)
      ├─ Gemini 7: Input, Textarea
      ├─ Gemini 8: Select, Checkbox
      └─ Gemini 9: DatePicker, FileUpload

총 인력: Claude 3개 + Gemini 9개
완료 시간: 45분
전통적 단일 모델 예상: 6-8시간

UI/UX Pro Max Skill 심층 분석

데이터베이스 구조

ui-styles.csv (57개 디자인 스타일)

style_id,name,description,use_cases,color_scheme,typography,spacing,examples
1,Glassmorphism,"Frosted glass effect with blur and transparency","Modern apps, overlays, cards","Semi-transparent backgrounds, vibrant accents","Sans-serif, medium weight","Generous padding, layered depth","Apple Music, Windows 11"
2,Neumorphism,"Soft UI with embossed/debossed effects","Minimalist interfaces, iOS-style apps","Monochromatic, soft shadows","Rounded sans-serif","Equal padding, subtle elevation","Soft UI kits, calculator apps"
3,Brutalism,"Raw, bold, anti-design aesthetic","Art portfolios, experimental sites","High contrast, primary colors","Monospace, condensed fonts","Irregular spacing, asymmetry","Yale School of Art, Balenciaga"
...
57,Organic Shapes,"Flowing, nature-inspired forms","Wellness apps, creative platforms","Earth tones, gradients","Humanist sans-serif","Fluid spacing, rounded corners","Headspace, Notion"

color-palettes.csv (트렌디한 조합)

palette_id,name,primary,secondary,accent,neutral,gradient,accessibility_score
1,Modern SaaS,"#6366F1","#8B5CF6","#EC4899","#1F2937","linear-gradient(135deg, #667eea 0%, #764ba2 100%)",AAA
2,Fintech Pro,"#0EA5E9","#06B6D4","#10B981","#0F172A","linear-gradient(to right, #4facfe 0%, #00f2fe 100%)",AAA
3,E-Commerce Warm,"#F97316","#EF4444","#FBBF24","#78350F","radial-gradient(circle, #ff9a56 0%, #ff6a88 100%)",AA
...

typography.csv (폰트 페어링)

pairing_id,heading_font,body_font,mono_font,scale,line_height,letter_spacing,use_case
1,Inter,Inter,JetBrains Mono,"16/20/24/32/48",1.5,-0.02em,SaaS Dashboards
2,Poppins,Open Sans,Fira Code,"14/18/24/36/56",1.6,-0.01em,Marketing Sites
3,Playfair Display,Source Serif Pro,Source Code Pro,"18/22/28/40/64",1.7,0em,Editorial Content
...

검색 엔진: BM25 + Regex

UI/UX Pro Max Skill의 핵심은 지능형 검색 엔진입니다.

scripts/search.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/env python3
import sys
import csv
import re
from collections import Counter
import math

class DesignSearchEngine:
    def __init__(self, data_dir=".agent/skills/ui-ux-pro-max/data"):
        self.data_dir = data_dir
        self.domains = {
            'styles': 'ui-styles.csv',
            'colors': 'color-palettes.csv',
            'typography': 'typography.csv',
            'spacing': 'spacing.csv',
            'charts': 'charts.csv'
        }
        self.cache = {}
    
    def search(self, query: str, domain: str = None, max_results: int = 5):
        """
        BM25 ranking + regex matching for design queries
        """
        # Auto-detect domain if not specified
        if not domain:
            domain = self._detect_domain(query)
        
        # Load and cache data
        if domain not in self.cache:
            self.cache[domain] = self._load_csv(domain)
        
        documents = self.cache[domain]
        
        # Tokenize query
        query_terms = self._tokenize(query)
        
        # BM25 scoring
        scores = []
        for doc in documents:
            doc_text = ' '.join(doc.values())
            doc_terms = self._tokenize(doc_text)
            
            score = self._bm25_score(query_terms, doc_terms, documents)
            
            # Boost for exact regex matches
            if self._has_exact_match(query, doc_text):
                score *= 1.5
            
            scores.append((score, doc))
        
        # Sort by score and return top results
        scores.sort(reverse=True, key=lambda x: x[0])
        
        return [doc for score, doc in scores[:max_results]]
    
    def _detect_domain(self, query: str) -> str:
        """
        Auto-detect search domain from query
        """
        keywords = {
            'styles': ['glassmorphism', 'neumorphism', 'minimalist', 'brutalist', 'style', 'aesthetic'],
            'colors': ['color', 'palette', 'gradient', 'hue', 'contrast', 'scheme'],
            'typography': ['font', 'typeface', 'heading', 'text', 'typography', 'serif'],
            'charts': ['chart', 'graph', 'visualization', 'plot', 'bar', 'line', 'pie']
        }
        
        query_lower = query.lower()
        
        for domain, terms in keywords.items():
            if any(term in query_lower for term in terms):
                return domain
        
        return 'styles'  # default
    
    def _tokenize(self, text: str) -> list:
        """
        Simple tokenization: lowercase, remove punctuation
        """
        text = text.lower()
        text = re.sub(r'[^\w\s]', ' ', text)
        return text.split()
    
    def _bm25_score(self, query_terms: list, doc_terms: list, all_docs: list, k1=1.5, b=0.75) -> float:
        """
        BM25 ranking algorithm
        """
        score = 0.0
        doc_len = len(doc_terms)
        avg_doc_len = sum(len(self._tokenize(' '.join(d.values()))) for d in all_docs) / len(all_docs)
        
        term_freq = Counter(doc_terms)
        
        for term in query_terms:
            if term not in term_freq:
                continue
            
            # Term frequency in document
            tf = term_freq[term]
            
            # Document frequency (how many docs contain this term)
            df = sum(1 for d in all_docs if term in self._tokenize(' '.join(d.values())))
            
            # IDF (inverse document frequency)
            idf = math.log((len(all_docs) - df + 0.5) / (df + 0.5) + 1.0)
            
            # BM25 formula
            numerator = tf * (k1 + 1)
            denominator = tf + k1 * (1 - b + b * (doc_len / avg_doc_len))
            
            score += idf * (numerator / denominator)
        
        return score
    
    def _has_exact_match(self, query: str, text: str) -> bool:
        """
        Check for exact phrase match (case-insensitive)
        """
        pattern = re.escape(query.lower())
        return bool(re.search(pattern, text.lower()))
    
    def _load_csv(self, domain: str) -> list:
        """
        Load CSV file as list of dicts
        """
        filepath = f"{self.data_dir}/{self.domains[domain]}"
        
        with open(filepath, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            return list(reader)

# CLI entry point
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python3 search.py '<query>' [--domain <domain>] [-n <max_results>]")
        sys.exit(1)
    
    query = sys.argv[1]
    domain = None
    max_results = 5
    
    # Parse optional arguments
    i = 2
    while i < len(sys.argv):
        if sys.argv[i] == '--domain' and i + 1 < len(sys.argv):
            domain = sys.argv[i + 1]
            i += 2
        elif sys.argv[i] == '-n' and i + 1 < len(sys.argv):
            max_results = int(sys.argv[i + 1])
            i += 2
        else:
            i += 1
    
    # Execute search
    engine = DesignSearchEngine()
    results = engine.search(query, domain, max_results)
    
    # Output results (JSON format for agent consumption)
    import json
    print(json.dumps(results, indent=2, ensure_ascii=False))

사용 예시:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Glassmorphism 스타일 검색
python3 .agent/skills/ui-ux-pro-max/scripts/search.py "glassmorphism card design"

# 출력:
[
  {
    "style_id": "1",
    "name": "Glassmorphism",
    "description": "Frosted glass effect with blur and transparency",
    "color_scheme": "Semi-transparent backgrounds, vibrant accents",
    "css_properties": "backdrop-filter: blur(10px); background: rgba(255,255,255,0.1);",
    "examples": "Apple Music, Windows 11"
  }
]

# SaaS 색상 팔레트 검색
python3 .agent/skills/ui-ux-pro-max/scripts/search.py "modern saas color palette" --domain colors

# 폰트 페어링 검색
python3 .agent/skills/ui-ux-pro-max/scripts/search.py "dashboard typography" --domain typography

실전 프로젝트: E-Commerce 디자인 시스템

실제 프로젝트를 통해 Claude-Gemini 협업 워크플로우를 단계별로 살펴봅니다.

프로젝트 요구사항

목표: 현대적인 B2C E-Commerce 플랫폼의 관리자 대시보드

기능:

  • 실시간 판매 현황 (차트)
  • 주문 관리 테이블
  • 재고 현황 카드
  • 고객 분석 그래프
  • 검색 및 필터링

기술 스택:

  • Frontend: React 18 + TypeScript
  • Styling: Tailwind CSS
  • Charts: Recharts
  • Icons: Lucide React

디자인 요구사항:

  • 깔끔하고 전문적인 느낌
  • 다크모드 지원
  • 반응형 (모바일, 태블릿, 데스크톱)
  • 접근성 (WCAG AA)

Phase 1: Claude - 디자인 시스템 설계

Antigravity 실행:

1
2
3
4
5
6
7
8
9
10
# Antigravity 프로젝트 초기화
agy init ecommerce-dashboard
cd ecommerce-dashboard

# UI/UX Pro Max Skill 설치
npm install -g uipro-cli
uipro init --ai antigravity

# Antigravity에서 Claude Opus 4.5 선택
agy config set-model claude-opus-4-5

프롬프트:

1
2
3
4
5
6
7
8
9
10
11
12
13
프로젝트: B2C E-Commerce 관리자 대시보드

요구사항:
- 현대적인 SaaS 스타일
- 데이터 중심 (차트, 테이블 많음)
- 다크모드 우선 (라이트모드도 지원)
- 접근성 AA 등급

작업:
1. UI/UX Pro Max Skill을 사용하여 최적의 디자인 스타일 선정
2. 완전한 디자인 시스템 정의 (색상, 타이포그래피, 간격, 컴포넌트)
3. 컴포넌트 라이브러리 명세 작성
4. design-system.json 파일로 출력 (Gemini가 사용할 수 있도록)

Claude의 작업 과정:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
Claude: UI/UX Pro Max Skill을 활성화합니다...

[1/4] 디자인 스타일 검색 중...

$ python3 .agent/skills/ui-ux-pro-max/scripts/search.py \
  "modern saas dashboard data-centric" --domain styles -n 3

검색 결과:
1. Minimalist Modern (score: 0.87)
   - 깔끔한 레이아웃, 화이트스페이스 활용
   - 데이터 가독성 우선
   
2. Glassmorphism Lite (score: 0.76)
   - 미묘한 투명도, 레이어 구분
   - 모던한 느낌
   
3. Flat 2.0 (score: 0.68)
   - 그림자 최소화, 명확한 구분선
   - 빠른 로딩

선택: Minimalist Modern + Glassmorphism Lite 하이브리드
이유: 데이터 가독성(Minimalist)과 현대적 느낌(Glassmorphism) 모두 충족

[2/4] 색상 팔레트 설정...

$ python3 .agent/skills/ui-ux-pro-max/scripts/search.py \
  "modern saas dark mode" --domain colors

선택된 팔레트:
Primary: #3B82F6 (Blue 500) - 신뢰감, 전문성
Secondary: #8B5CF6 (Violet 500) - 프리미엄 느낌
Success: #10B981 (Emerald 500) - 긍정적 지표
Warning: #F59E0B (Amber 500) - 주의 필요
Error: #EF4444 (Red 500) - 문제 상황
Neutral: Slate scale (50-950) - 다크모드 최적화

[3/4] 타이포그래피 설정...

$ python3 .agent/skills/ui-ux-pro-max/scripts/search.py \
  "dashboard typography readable" --domain typography

선택:
Headings: Inter 700 (Bold) - 명확한 계층
Body: Inter 400 (Regular) - 가독성
Numbers: JetBrains Mono 500 (Medium) - 숫자 정렬
Scale: 14/16/20/24/32/48px
Line Height: 1.5 (본문), 1.2 (제목)

[4/4] 컴포넌트 라이브러리 명세...

Layout Components:
1. DashboardShell
   - Purpose: 전체 레이아웃 컨테이너
   - Structure: Sidebar (240px) + Main (fluid)
   - Props: children, sidebarContent
   
2. Sidebar
   - Purpose: 네비게이션
   - Features: 접기/펼치기, 활성 상태 표시
   - Props: items[], collapsed
   
3. TopNavBar
   - Purpose: 검색, 사용자 메뉴
   - Props: user, onSearch

Data Components:
4. StatCard
   - Purpose: 주요 지표 표시
   - Structure: Icon + Title + Value + Trend
   - Props: title, value, trend, icon
   - Example: "총 매출: $45,231 ↑ 12%"
   
5. RevenueChart
   - Purpose: 매출 추이 차트
   - Type: Area Chart (Recharts)
   - Props: data[], timeRange
   
6. OrderTable
   - Purpose: 주문 목록
   - Features: 정렬, 필터, 페이지네이션
   - Props: orders[], onSort, onFilter

Form Components:
7. SearchInput
   - Props: placeholder, onSearch, debounce
   
8. FilterDropdown
   - Props: options[], value, onChange

Feedback Components:
9. Toast
   - Types: success, error, info, warning
   - Props: message, type, duration
   
10. LoadingSkeleton
    - Props: count, height, width

출력: design-system.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
{
  "version": "1.0.0",
  "name": "E-Commerce Dashboard Design System",
  "theme": {
    "style": "Minimalist Modern + Glassmorphism Hybrid",
    "darkMode": true,
    "lightMode": true
  },
  "colors": {
    "dark": {
      "primary": "#3B82F6",
      "secondary": "#8B5CF6",
      "success": "#10B981",
      "warning": "#F59E0B",
      "error": "#EF4444",
      "background": {
        "base": "#0F172A",
        "elevated": "#1E293B",
        "overlay": "rgba(30, 41, 59, 0.8)"
      },
      "text": {
        "primary": "#F1F5F9",
        "secondary": "#CBD5E1",
        "muted": "#64748B"
      }
    },
    "light": {
      "primary": "#3B82F6",
      "secondary": "#8B5CF6",
      "success": "#10B981",
      "warning": "#F59E0B",
      "error": "#EF4444",
      "background": {
        "base": "#FFFFFF",
        "elevated": "#F8FAFC",
        "overlay": "rgba(248, 250, 252, 0.9)"
      },
      "text": {
        "primary": "#0F172A",
        "secondary": "#475569",
        "muted": "#94A3B8"
      }
    }
  },
  "typography": {
    "fontFamily": {
      "heading": "Inter, sans-serif",
      "body": "Inter, sans-serif",
      "mono": "JetBrains Mono, monospace"
    },
    "fontSize": {
      "xs": "0.75rem",
      "sm": "0.875rem",
      "base": "1rem",
      "lg": "1.125rem",
      "xl": "1.25rem",
      "2xl": "1.5rem",
      "3xl": "2rem"
    },
    "fontWeight": {
      "normal": 400,
      "medium": 500,
      "bold": 700
    },
    "lineHeight": {
      "tight": 1.2,
      "normal": 1.5,
      "relaxed": 1.7
    }
  },
  "spacing": {
    "system": "8pt grid",
    "scale": {
      "xs": "0.25rem",
      "sm": "0.5rem",
      "md": "1rem",
      "lg": "1.5rem",
      "xl": "2rem",
      "2xl": "3rem"
    }
  },
  "borderRadius": {
    "sm": "0.25rem",
    "md": "0.5rem",
    "lg": "0.75rem",
    "xl": "1rem",
    "full": "9999px"
  },
  "shadows": {
    "sm": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
    "md": "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
    "lg": "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
    "glassmorphism": "0 8px 32px 0 rgba(31, 38, 135, 0.37)"
  },
  "components": {
    "DashboardShell": {
      "description": "Main layout container",
      "props": {
        "children": "ReactNode",
        "sidebarContent": "ReactNode"
      },
      "structure": "Sidebar (240px fixed) + Main (flex-1)"
    },
    "Sidebar": {
      "description": "Navigation sidebar",
      "props": {
        "items": "NavItem[]",
        "collapsed": "boolean"
      },
      "features": ["collapsible", "activeState", "icons"]
    },
    "StatCard": {
      "description": "Key metric display card",
      "props": {
        "title": "string",
        "value": "string | number",
        "trend": "{ value: number, direction: 'up' | 'down' }",
        "icon": "LucideIcon"
      },
      "layout": "Icon (left) + Content (center) + Trend (right)",
      "styling": {
        "background": "elevated",
        "border": "1px border-slate-700",
        "borderRadius": "lg",
        "padding": "lg",
        "glassmorphism": true
      }
    },
    "RevenueChart": {
      "description": "Revenue trend visualization",
      "library": "Recharts",
      "type": "AreaChart",
      "props": {
        "data": "Array<{ date: string, revenue: number }>",
        "timeRange": "'7d' | '30d' | '90d' | '1y'"
      },
      "styling": {
        "gradientFill": "linear-gradient(180deg, primary + opacity 20% -> transparent)",
        "strokeWidth": 2,
        "gridColor": "text.muted with 10% opacity"
      }
    },
    "OrderTable": {
      "description": "Order list with sorting and filtering",
      "props": {
        "orders": "Order[]",
        "onSort": "(field: string, direction: 'asc' | 'desc') => void",
        "onFilter": "(filters: Filters) => void"
      },
      "features": ["sortable", "filterable", "pagination", "rowSelection"],
      "columns": ["Order ID", "Customer", "Amount", "Status", "Date", "Actions"],
      "styling": {
        "headerBackground": "elevated",
        "rowHover": "background.elevated with 50% opacity",
        "alternatingRows": false
      }
    },
    "SearchInput": {
      "description": "Search with debounce",
      "props": {
        "placeholder": "string",
        "onSearch": "(query: string) => void",
        "debounce": "number (default: 300ms)"
      },
      "features": ["icon (search)", "clearButton", "loading state"]
    },
    "Toast": {
      "description": "Notification toast",
      "props": {
        "message": "string",
        "type": "'success' | 'error' | 'info' | 'warning'",
        "duration": "number (default: 3000ms)"
      },
      "position": "top-right",
      "animation": "slide-in from right"
    }
  },
  "responsiveBreakpoints": {
    "sm": "640px",
    "md": "768px",
    "lg": "1024px",
    "xl": "1280px",
    "2xl": "1536px"
  },
  "accessibility": {
    "colorContrast": "WCAG AA",
    "focusVisible": "2px solid primary with offset",
    "ariaLabels": "required for all interactive elements",
    "keyboardNavigation": "full support"
  }
}

Claude는 또한 간단한 README를 생성합니다:

design-system-notes.md:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# E-Commerce Dashboard Design System

## Design Rationale

이 디자인 시스템은 데이터 가독성과 현대적인 미학을 균형있게 조화시킵니다.

### Style Choice: Minimalist Modern + Glassmorphism Hybrid

**Minimalist Modern:**
- 깔끔한 레이아웃으로 데이터에 집중
- 불필요한 장식 제거
- 화이트스페이스를 통한 시각적 휴식

**Glassmorphism Elements:**
- StatCard와 모달에 미묘한 투명도 적용
- 레이어 구분을 명확하게 하면서도 부드러운 느낌 유지
- backdrop-filter: blur() 로 현대적인 느낌

### Color System

다크모드를 기본으로 하되, 라이트모드도 완벽하게 지원합니다.

**Primary (Blue):** 신뢰감과 전문성
**Secondary (Violet):** 프리미엄 느낌
**Success/Warning/Error:** 직관적인 상태 표시

모든 색상 조합은 WCAG AA 기준을 충족합니다.

### Typography

Inter는 숫자 가독성이 뛰어나 대시보드에 이상적입니다.
JetBrains Mono는 tabular figures 지원으로 숫자 정렬이 완벽합니다.

### Component Architecture

각 컴포넌트는 단일 책임 원칙을 따릅니다:
- StatCard: 하나의 지표만 표시
- RevenueChart: 매출 데이터만 시각화
- OrderTable: 주문 목록만 관리

이를 통해 재사용성과 테스트 용이성을 보장합니다.

## Implementation Notes for Gemini

다음 지침을 엄격히 따라주세요:

1. **Color Values:**
   - design-system.json의 색상 값을 정확히 사용
   - 임의로 색상 변경 금지
   
2. **Spacing:**
   - 8pt grid 시스템 준수
   - padding/margin은 반드시 spacing scale 사용
   
3. **Typography:**
   - fontSize, fontWeight는 정의된 값만 사용
   - 임의의 font-size 금지
   
4. **Component Props:**
   - design-system.json에 정의된 props 타입 준수
   - 추가 props 필요 시 먼저 문의
   
5. **Accessibility:**
   - 모든 버튼에 aria-label
   - 모든 이미지에 alt
   - 키보드 네비게이션 지원
   
6. **Responsive:**
   - mobile-first approach
   - 모든 컴포넌트는 sm/md/lg 브레이크포인트 지원

Phase 2: Gemini - 컴포넌트 구현

이제 Gemini에게 작업을 넘깁니다. Antigravity의 Agent Manager를 사용하여 3개의 Gemini 인스턴스를 병렬로 실행합니다.

Antigravity Agent Manager 설정:

1
2
3
4
5
6
7
# Agent Manager에서 3개 Gemini 인스턴스 생성
agy agent create gemini-layout --model gemini-3-flash
agy agent create gemini-data --model gemini-3-flash  
agy agent create gemini-forms --model gemini-3-flash

# 각 에이전트에게 design-system.json 공유
agy agent share design-system.json --all

Gemini Instance 1 (Layout Components):

1
2
3
4
5
6
7
8
9
10
11
Prompt:
design-system.json을 참조하여 다음 컴포넌트를 구현하세요:
1. DashboardShell.tsx
2. Sidebar.tsx
3. TopNavBar.tsx

요구사항:
- TypeScript + React 18
- Tailwind CSS 사용 (design-system의 색상 값을 Tailwind config에 추가)
- 모든 props 타입 정의
- 접근성 속성 포함

Gemini의 출력: DashboardShell.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// components/layout/DashboardShell.tsx
import React from 'react';
import { Sidebar } from './Sidebar';
import type { NavItem } from '@/types';

interface DashboardShellProps {
  children: React.ReactNode;
  sidebarContent?: React.ReactNode;
  navItems?: NavItem[];
}

export const DashboardShell: React.FC<DashboardShellProps> = ({
  children,
  sidebarContent,
  navItems = []
}) => {
  return (
    <div className="flex h-screen bg-slate-950">
      {/* Sidebar - 240px fixed width */}
      <aside className="w-60 flex-shrink-0">
        {sidebarContent || <Sidebar items={navItems} />}
      </aside>
      
      {/* Main Content - fluid width */}
      <main className="flex-1 overflow-y-auto">
        <div className="container mx-auto px-6 py-8">
          {children}
        </div>
      </main>
    </div>
  );
};

Sidebar.tsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// components/layout/Sidebar.tsx
import React, { useState } from 'react';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { NavItem } from './NavItem';
import type { NavItem as NavItemType } from '@/types';

interface SidebarProps {
  items: NavItemType[];
  collapsed?: boolean;
}

export const Sidebar: React.FC<SidebarProps> = ({ 
  items, 
  collapsed: initialCollapsed = false 
}) => {
  const [collapsed, setCollapsed] = useState(initialCollapsed);

  return (
    <div 
      className={`
        h-full bg-slate-900 border-r border-slate-800
        transition-all duration-300 ease-in-out
        ${collapsed ? 'w-16' : 'w-60'}
      `}
      role="navigation"
      aria-label="Main navigation"
    >
      {/* Header */}
      <div className="h-16 flex items-center justify-between px-4 border-b border-slate-800">
        {!collapsed && (
          <h1 className="text-xl font-bold text-slate-100">
            Dashboard
          </h1>
        )}
        <button
          onClick={() => setCollapsed(!collapsed)}
          className="p-2 rounded-md hover:bg-slate-800 text-slate-400 hover:text-slate-100"
          aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
        >
          {collapsed ? <ChevronRight size={20} /> : <ChevronLeft size={20} />}
        </button>
      </div>

      {/* Navigation Items */}
      <nav className="mt-4 px-2">
        {items.map((item) => (
          <NavItem 
            key={item.id} 
            item={item} 
            collapsed={collapsed}
          />
        ))}
      </nav>
    </div>
  );
};

NavItem.tsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// components/layout/NavItem.tsx
import React from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import type { LucideIcon } from 'lucide-react';

interface NavItemType {
  id: string;
  label: string;
  href: string;
  icon: LucideIcon;
}

interface NavItemProps {
  item: NavItemType;
  collapsed: boolean;
}

export const NavItem: React.FC<NavItemProps> = ({ item, collapsed }) => {
  const pathname = usePathname();
  const isActive = pathname === item.href;
  
  const Icon = item.icon;

  return (
    <Link
      href={item.href}
      className={`
        flex items-center gap-3 px-3 py-2 rounded-lg mb-1
        transition-colors duration-200
        ${isActive 
          ? 'bg-blue-600 text-white' 
          : 'text-slate-400 hover:bg-slate-800 hover:text-slate-100'
        }
      `}
      aria-current={isActive ? 'page' : undefined}
    >
      <Icon size={20} aria-hidden="true" />
      {!collapsed && (
        <span className="text-sm font-medium">{item.label}</span>
      )}
    </Link>
  );
};

Gemini Instance 2 (Data Components):

1
2
3
4
5
6
7
8
9
10
11
Prompt:
design-system.json을 참조하여 다음 컴포넌트를 구현하세요:
1. StatCard.tsx
2. RevenueChart.tsx
3. OrderTable.tsx

요구사항:
- TypeScript + React 18
- Recharts 사용 (차트)
- Glassmorphism 효과 적용 (StatCard)
- 완전한 타입 정의

StatCard.tsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// components/data/StatCard.tsx
import React from 'react';
import type { LucideIcon } from 'lucide-react';
import { TrendingUp, TrendingDown } from 'lucide-react';

interface Trend {
  value: number;
  direction: 'up' | 'down';
}

interface StatCardProps {
  title: string;
  value: string | number;
  trend?: Trend;
  icon: LucideIcon;
}

export const StatCard: React.FC<StatCardProps> = ({
  title,
  value,
  trend,
  icon: Icon
}) => {
  return (
    <div 
      className="
        relative overflow-hidden
        bg-slate-800/50 backdrop-blur-lg
        border border-slate-700/50
        rounded-lg p-6
        shadow-glassmorphism
      "
      role="article"
      aria-labelledby={`stat-${title.replace(/\s/g, '-')}`}
    >
      {/* Icon */}
      <div className="absolute top-4 right-4 text-slate-600">
        <Icon size={32} aria-hidden="true" />
      </div>

      {/* Title */}
      <h3 
        id={`stat-${title.replace(/\s/g, '-')}`}
        className="text-sm font-medium text-slate-400 mb-2"
      >
        {title}
      </h3>

      {/* Value */}
      <p className="text-3xl font-bold text-slate-100 mb-2 font-mono">
        {value}
      </p>

      {/* Trend */}
      {trend && (
        <div 
          className={`
            flex items-center gap-1 text-sm
            ${trend.direction === 'up' ? 'text-emerald-500' : 'text-red-500'}
          `}
          aria-label={`Trend ${trend.direction} by ${Math.abs(trend.value)}%`}
        >
          {trend.direction === 'up' ? (
            <TrendingUp size={16} />
          ) : (
            <TrendingDown size={16} />
          )}
          <span className="font-medium">
            {Math.abs(trend.value)}%
          </span>
        </div>
      )}
    </div>
  );
};

RevenueChart.tsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// components/data/RevenueChart.tsx
import React from 'react';
import {
  AreaChart,
  Area,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  ResponsiveContainer
} from 'recharts';

interface DataPoint {
  date: string;
  revenue: number;
}

type TimeRange = '7d' | '30d' | '90d' | '1y';

interface RevenueChartProps {
  data: DataPoint[];
  timeRange?: TimeRange;
}

export const RevenueChart: React.FC<RevenueChartProps> = ({
  data,
  timeRange = '30d'
}) => {
  return (
    <div 
      className="bg-slate-800/50 backdrop-blur-lg border border-slate-700/50 rounded-lg p-6"
      role="img"
      aria-label="Revenue trend chart"
    >
      <h3 className="text-lg font-semibold text-slate-100 mb-4">
        Revenue Trend ({timeRange})
      </h3>
      
      <ResponsiveContainer width="100%" height={300}>
        <AreaChart data={data}>
          <defs>
            <linearGradient id="revenueGradient" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor="#3B82F6" stopOpacity={0.3} />
              <stop offset="100%" stopColor="#3B82F6" stopOpacity={0} />
            </linearGradient>
          </defs>
          
          <CartesianGrid 
            strokeDasharray="3 3" 
            stroke="rgba(100, 116, 139, 0.1)" 
          />
          
          <XAxis 
            dataKey="date"
            stroke="#64748B"
            style=
          />
          
          <YAxis 
            stroke="#64748B"
            style=
            tickFormatter={(value) => `$${value / 1000}k`}
          />
          
          <Tooltip 
            contentStyle=
            formatter={(value: number) => [`$${value.toLocaleString()}`, 'Revenue']}
          />
          
          <Area
            type="monotone"
            dataKey="revenue"
            stroke="#3B82F6"
            strokeWidth={2}
            fill="url(#revenueGradient)"
          />
        </AreaChart>
      </ResponsiveContainer>
    </div>
  );
};

OrderTable.tsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// components/data/OrderTable.tsx
import React, { useState } from 'react';
import { ArrowUpDown, MoreVertical } from 'lucide-react';

interface Order {
  id: string;
  customer: string;
  amount: number;
  status: 'pending' | 'processing' | 'completed' | 'cancelled';
  date: string;
}

interface OrderTableProps {
  orders: Order[];
  onSort?: (field: keyof Order, direction: 'asc' | 'desc') => void;
  onFilter?: (filters: Record<string, any>) => void;
}

export const OrderTable: React.FC<OrderTableProps> = ({
  orders,
  onSort,
  onFilter
}) => {
  const [sortField, setSortField] = useState<keyof Order | null>(null);
  const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');

  const handleSort = (field: keyof Order) => {
    const newDirection = 
      sortField === field && sortDirection === 'asc' ? 'desc' : 'asc';
    setSortField(field);
    setSortDirection(newDirection);
    onSort?.(field, newDirection);
  };

  const getStatusColor = (status: Order['status']) => {
    const colors = {
      pending: 'bg-amber-500/20 text-amber-500',
      processing: 'bg-blue-500/20 text-blue-500',
      completed: 'bg-emerald-500/20 text-emerald-500',
      cancelled: 'bg-red-500/20 text-red-500'
    };
    return colors[status];
  };

  return (
    <div className="bg-slate-800/50 backdrop-blur-lg border border-slate-700/50 rounded-lg overflow-hidden">
      <div className="overflow-x-auto">
        <table className="w-full" role="table">
          <thead className="bg-slate-900/50">
            <tr>
              {(['Order ID', 'Customer', 'Amount', 'Status', 'Date'] as const).map((header) => (
                <th
                  key={header}
                  className="px-6 py-4 text-left text-sm font-semibold text-slate-300"
                  scope="col"
                >
                  <button
                    onClick={() => handleSort(header.toLowerCase().replace(' ', '') as keyof Order)}
                    className="flex items-center gap-2 hover:text-slate-100 transition-colors"
                    aria-label={`Sort by ${header}`}
                  >
                    {header}
                    <ArrowUpDown size={14} />
                  </button>
                </th>
              ))}
              <th className="px-6 py-4 text-right text-sm font-semibold text-slate-300" scope="col">
                Actions
              </th>
            </tr>
          </thead>
          
          <tbody className="divide-y divide-slate-700/50">
            {orders.map((order) => (
              <tr 
                key={order.id}
                className="hover:bg-slate-800/30 transition-colors"
              >
                <td className="px-6 py-4 text-sm font-mono text-slate-300">
                  {order.id}
                </td>
                <td className="px-6 py-4 text-sm text-slate-100">
                  {order.customer}
                </td>
                <td className="px-6 py-4 text-sm font-mono font-medium text-slate-100">
                  ${order.amount.toLocaleString()}
                </td>
                <td className="px-6 py-4">
                  <span className={`
                    inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
                    ${getStatusColor(order.status)}
                  `}>
                    {order.status}
                  </span>
                </td>
                <td className="px-6 py-4 text-sm text-slate-400">
                  {order.date}
                </td>
                <td className="px-6 py-4 text-right">
                  <button 
                    className="text-slate-400 hover:text-slate-100 p-1 rounded hover:bg-slate-700"
                    aria-label={`Actions for order ${order.id}`}
                  >
                    <MoreVertical size={16} />
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
};

Gemini Instance 3 (Form & Feedback Components):

생성된 컴포넌트:

  • SearchInput.tsx
  • FilterDropdown.tsx
  • Toast.tsx
  • LoadingSkeleton.tsx

(코드는 유사한 패턴으로 생략)

Phase 3: Claude - 품질 검토

모든 Gemini 인스턴스가 작업을 완료하면, Claude가 품질 검토를 수행합니다.

Antigravity에서 Claude 재활성화:

1
2
agy agent create claude-reviewer --model claude-opus-4-5
agy agent assign claude-reviewer --task "Review all generated components"

Claude의 검토 프로세스:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Claude: 생성된 9개 컴포넌트를 검토합니다...

[1/9] DashboardShell.tsx
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 디자인 토큰 준수: OK
✓ TypeScript 타입: OK
✓ 접근성: OK (role, aria-label 포함)
✓ 반응형: OK
✓ 코드 품질: OK

[2/9] Sidebar.tsx
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 디자인 토큰 준수: OK
✓ 상태 관리: OK (useState)
✓ 애니메이션: OK (transition-all)
△ 개선 제안:
  - collapsed 상태를 localStorage에 저장하여 페이지 새로고침 시에도 유지
  - 키보드 단축키 추가 (Cmd+B로 토글)

[3/9] StatCard.tsx
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 디자인 토큰 준수: OK
✓ Glassmorphism 효과: OK
✗ 문제 발견:
  - shadow-glassmorphism 클래스가 Tailwind config에 정의되지 않음
  - 해결책: tailwind.config.js에 커스텀 shadow 추가 필요

[4/9] RevenueChart.tsx
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Recharts 사용: OK
✓ 그라디언트: OK
△ 개선 제안:
  - 로딩 상태 추가 (data가 비어있을 때)
  - 에러 바운더리 추가

[5/9] OrderTable.tsx
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 정렬 기능: OK
✓ 상태별 색상: OK
✗ 문제 발견:
  - 페이지네이션 구현 누락 (spec에는 있었음)
  - 모바일 반응형 고려 부족 (테이블이 너무 넓음)

검토 완료 요약:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ 통과: 7개 컴포넌트
△ 개선 필요: 2개 (StatCard, RevenueChart)
✗ 수정 필요: 2개 (StatCard shadow, OrderTable pagination)

우선순위 1 (필수):
1. StatCard: Tailwind config에 glassmorphism shadow 추가
2. OrderTable: 페이지네이션 컴포넌트 추가

우선순위 2 (권장):
3. Sidebar: localStorage 상태 저장
4. RevenueChart: 로딩/에러 상태 처리
5. OrderTable: 모바일 레이아웃 개선

Phase 4: Gemini - 수정 반영

Claude의 피드백을 받아 Gemini가 수정합니다.

수정 1: Tailwind Config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      boxShadow: {
        'glassmorphism': '0 8px 32px 0 rgba(31, 38, 135, 0.37)'
      },
      colors: {
        // design-system.json의 색상 추가
        slate: {
          950: '#0F172A',
          // ...
        }
      }
    }
  }
}

수정 2: OrderTable 페이지네이션 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// components/data/OrderTable.tsx (수정)
import { ChevronLeft, ChevronRight } from 'lucide-react';

// ... (기존 코드)

const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage] = useState(10);

const paginatedOrders = orders.slice(
  (currentPage - 1) * itemsPerPage,
  currentPage * itemsPerPage
);

const totalPages = Math.ceil(orders.length / itemsPerPage);

// ... (테이블 렌더링)

{/* 페이지네이션 */}
<div className="flex items-center justify-between px-6 py-4 bg-slate-900/50">
  <p className="text-sm text-slate-400">
    Showing {(currentPage - 1) * itemsPerPage + 1} to {Math.min(currentPage * itemsPerPage, orders.length)} of {orders.length} orders
  </p>
  
  <div className="flex gap-2">
    <button
      onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
      disabled={currentPage === 1}
      className="p-2 rounded-md border border-slate-700 hover:bg-slate-800 disabled:opacity-50 disabled:cursor-not-allowed"
      aria-label="Previous page"
    >
      <ChevronLeft size={16} />
    </button>
    
    <span className="px-4 py-2 text-sm text-slate-300">
      Page {currentPage} of {totalPages}
    </span>
    
    <button
      onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
      disabled={currentPage === totalPages}
      className="p-2 rounded-md border border-slate-700 hover:bg-slate-800 disabled:opacity-50 disabled:cursor-not-allowed"
      aria-label="Next page"
    >
      <ChevronRight size={16} />
    </button>
  </div>
</div>

최종 결과

프로젝트 완료 타임라인:

1
2
3
4
5
6
7
00:00 - 00:15  Claude: 디자인 시스템 설계
00:15 - 00:30  Gemini (3 instances): 병렬 컴포넌트 구현
00:30 - 00:45  Claude: 품질 검토 및 피드백
00:45 - 00:55  Gemini: 수정 반영
00:55 - 01:00  최종 통합 테스트

총 소요 시간: 1시간

단일 개발자 예상 시간: 6-8시간 효율성 향상: 6-8배

품질 지표:

  • 디자인 일관성: 98% (모든 컴포넌트가 동일한 디자인 토큰 사용)
  • 접근성 점수: WCAG AA (색상 대비, ARIA 속성)
  • 코드 품질: TypeScript 엄격 모드 통과, 0 ESLint 에러
  • 반응형: 모든 브레이크포인트에서 정상 작동

설정 및 구현 가이드

환경 준비

1. Antigravity 설치

1
2
3
4
5
6
7
8
9
# macOS
brew install antigravity

# Linux
wget https://antigravity.google/download/linux
sudo dpkg -i antigravity-latest.deb

# Windows
winget install Google.Antigravity

2. UI/UX Pro Max Skill 설치

1
2
3
4
5
6
7
8
# CLI 도구 설치
npm install -g uipro-cli

# 프로젝트 디렉토리로 이동
cd /path/to/your/project

# Antigravity용 설치
uipro init --ai antigravity

설치 후 구조:

1
2
3
4
5
6
7
8
9
10
11
12
your-project/
├── .agent/
│   └── skills/
│       └── ui-ux-pro-max/
│           ├── SKILL.md
│           ├── scripts/
│           └── data/
├── .shared/
│   └── ui-ux-pro-max/
│       └── (공유 데이터)
└── src/
    └── (여러분의 코드)

3. Agent 구성

Claude 에이전트 설정:

.agent/agents/claude-architect.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "name": "claude-architect",
  "model": "claude-opus-4-5-20251101",
  "role": "Design System Architect",
  "description": "전체 디자인 시스템을 설계하고 품질을 검토합니다",
  "skills": [
    "ui-ux-pro-max",
    "frontend-design"
  ],
  "systemPrompt": "당신은 전문 UI/UX 아키텍트입니다. 일관성 있고 접근성 높은 디자인 시스템을 만드는 것이 목표입니다. UI/UX Pro Max Skill을 적극 활용하여 최신 디자인 트렌드를 반영하세요.",
  "temperature": 0.7,
  "maxTokens": 4096
}

Gemini 에이전트 설정:

.agent/agents/gemini-builder.json:

1
2
3
4
5
6
7
8
9
10
{
  "name": "gemini-builder",
  "model": "gemini-3-flash",
  "role": "Component Implementation Specialist",
  "description": "디자인 시스템을 기반으로 컴포넌트를 빠르게 구현합니다",
  "skills": [],
  "systemPrompt": "당신은 효율적인 프론트엔드 개발자입니다. 제공된 design-system.json을 엄격히 따라 컴포넌트를 구현하세요. 임의로 스타일을 변경하지 말고, 모든 접근성 요구사항을 충족하세요.",
  "temperature": 0.3,
  "maxTokens": 8192
}

워크플로우 스크립트

orchestrate.sh (자동화 스크립트)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/bin/bash

# E-Commerce Dashboard 멀티 에이전트 빌드 스크립트

set -e

PROJECT_NAME="ecommerce-dashboard"
DESIGN_SYSTEM_FILE="design-system.json"

echo "🚀 Starting multi-agent workflow for $PROJECT_NAME"

# Step 1: Claude - 디자인 시스템 설계
echo "
Step 1: Claude designs the system..."
agy agent run claude-architect --input "
프로젝트: $PROJECT_NAME

요구사항:
- 현대적인 SaaS 스타일 대시보드
- 다크모드 우선
- 데이터 중심 인터페이스

작업:
1. UI/UX Pro Max Skill 활성화
2. 디자인 시스템 정의
3. $DESIGN_SYSTEM_FILE 파일로 출력
" --output $DESIGN_SYSTEM_FILE

if [ ! -f $DESIGN_SYSTEM_FILE ]; then
  echo "❌ Design system file not created!"
  exit 1
fi

echo "✅ Design system created: $DESIGN_SYSTEM_FILE"

# Step 2: Gemini - 병렬 컴포넌트 구현
echo "
Step 2: Gemini builds components in parallel..."

# 3개 Gemini 인스턴스 병렬 실행
cat > /tmp/gemini-tasks.txt << EOF
gemini-layout:DashboardShell,Sidebar,TopNavBar
gemini-data:StatCard,RevenueChart,OrderTable
gemini-forms:SearchInput,FilterDropdown,Toast
EOF

while IFS=: read -r agent components; do
  echo "  Starting $agent for $components..."
  
  agy agent run $agent --input "
design-system.json을 참조하여 다음 컴포넌트를 구현하세요:
$components

출력 디렉토리: src/components/
TypeScript + React 18 + Tailwind CSS
" --async &
  
done < /tmp/gemini-tasks.txt

# 모든 Gemini 작업 완료 대기
wait

echo "✅ All components generated"

# Step 3: Claude - 품질 검토
echo "
Step 3: Claude reviews quality..."

agy agent run claude-architect --input "
생성된 모든 컴포넌트를 검토하세요:
src/components/**/*.tsx

검토 항목:
- 디자인 토큰 준수
- 접근성
- 타입 안정성
- 반응형 디자인

출력: review-report.md
" --output review-report.md

echo "✅ Review completed: review-report.md"

# Step 4: Gemini - 수정 반영 (조건부)
if grep -q "수정 필요" review-report.md; then
  echo "
Step 4: Gemini applies fixes..."
  
  # review-report.md에서 수정 항목 추출
  FIXES=$(grep "수정 필요" review-report.md)
  
  agy agent run gemini-builder --input "
다음 수정 사항을 반영하세요:
$FIXES
" --async
  
  wait
  echo "✅ Fixes applied"
else
  echo "✅ No fixes needed"
fi

# 완료
echo "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎉 Multi-agent workflow completed!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Generated files:
- $DESIGN_SYSTEM_FILE
- src/components/layout/*
- src/components/data/*
- src/components/forms/*
- review-report.md
"

실행:

1
2
chmod +x orchestrate.sh
./orchestrate.sh

고급 패턴: 병렬 처리와 순차 처리

병렬 처리 (Parallel Execution)

언제 사용:

  • 독립적인 컴포넌트들 (서로 의존성 없음)
  • 시간이 중요한 경우
  • 리소스가 충분한 경우

예시: 50개 UI 컴포넌트 라이브러리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// parallel-build.js
const { spawn } = require('child_process');

const components = [
  // Layout (10개)
  ['Container', 'Grid', 'Flex', 'Stack', 'Divider', 'Spacer', 'Center', 'AspectRatio', 'Box', 'Section'],
  // Forms (15개)
  ['Input', 'Textarea', 'Select', 'Checkbox', 'Radio', 'Switch', 'Slider', 'RangeSlider', 'FileUpload', 'DatePicker', 'TimePicker', 'ColorPicker', 'Rating', 'PinInput', 'NumberInput'],
  // Data (10개)
  ['Table', 'List', 'Card', 'Badge', 'Tag', 'Stat', 'Progress', 'Skeleton', 'EmptyState', 'Alert'],
  // Feedback (8개)
  ['Toast', 'Modal', 'Drawer', 'Popover', 'Tooltip', 'Menu', 'Dialog', 'Notification'],
  // Navigation (7개)
  ['Breadcrumb', 'Tabs', 'Pagination', 'Steps', 'Stepper', 'Link', 'NavBar']
];

// 5개 Gemini 인스턴스 (그룹당 1개)
const workers = components.map((group, i) => {
  return spawn('agy', [
    'agent', 'run', `gemini-worker-${i}`,
    '--input', `Build these components: ${group.join(', ')}`,
    '--design-system', 'design-system.json'
  ]);
});

// 진행률 추적
workers.forEach((worker, i) => {
  worker.stdout.on('data', (data) => {
    console.log(`Worker ${i}: ${data}`);
  });
});

// 모든 워커 완료 대기
Promise.all(workers.map(w => new Promise(resolve => w.on('close', resolve))))
  .then(() => {
    console.log('✅ All 50 components built in parallel!');
  });

결과:

  • 순차 처리: ~5시간 (50개 × 6분)
  • 병렬 처리: ~60분 (10개 × 6분, 5개 워커)
  • 효율성: 5배

순차 처리 (Sequential Execution)

언제 사용:

  • 의존성이 있는 작업
  • 이전 단계의 출력이 다음 단계의 입력인 경우
  • 품질이 우선인 경우

예시: 디자인 시스템 → 토큰 → 기본 컴포넌트 → 복합 컴포넌트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# workflow.yaml
name: Design System Build Pipeline

stages:
  - name: foundation
    agent: claude-architect
    tasks:
      - Design core system
      - Define tokens
      - Create component hierarchy
    outputs:
      - design-system.json
      - tokens.json
      - hierarchy.md
  
  - name: primitives
    agent: gemini-builder
    depends_on: foundation
    inputs:
      - design-system.json
      - tokens.json
    tasks:
      - Build basic components (Button, Input, etc.)
    outputs:
      - src/primitives/*
  
  - name: composites
    agent: gemini-builder
    depends_on: primitives
    inputs:
      - design-system.json
      - src/primitives/*
    tasks:
      - Build composite components (Form, Table, etc.)
    outputs:
      - src/composites/*
  
  - name: patterns
    agent: claude-architect
    depends_on: composites
    inputs:
      - src/primitives/*
      - src/composites/*
    tasks:
      - Create usage patterns
      - Write documentation
    outputs:
      - docs/patterns.md
      - docs/examples.md
  
  - name: review
    agent: claude-architect
    depends_on: patterns
    tasks:
      - Final quality check
      - Accessibility audit
      - Performance analysis
    outputs:
      - review-report.md

실행:

1
agy workflow run workflow.yaml

하이브리드 패턴

가장 효율적인 접근은 병렬과 순차를 조합하는 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
Stage 1 (Sequential): Claude - 디자인 시스템
          ↓
Stage 2 (Parallel): Gemini - 3개 팀이 동시에 컴포넌트 구현
          ├─ Team A: Layout
          ├─ Team B: Data
          └─ Team C: Forms
          ↓
Stage 3 (Sequential): Claude - 통합 검토
          ↓
Stage 4 (Parallel): Gemini - 각 팀이 자신의 수정사항 반영
          ↓
Stage 5 (Sequential): Claude - 최종 승인

트러블슈팅 및 최적화

일반적인 문제들

문제 1: “Agent가 design-system.json을 무시합니다”

증상: Gemini가 자기 마음대로 색상이나 스타일을 변경

원인:

  • design-system.json이 컨텍스트에 제대로 로드되지 않음
  • 프롬프트가 불명확

해결책:

프롬프트를 더 명확하게:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
❌ 나쁜 프롬프트:
"design-system.json을 참조하여 Button 컴포넌트를 만들어줘"

✅ 좋은 프롬프트:
"다음 지침을 STRICT하게 따라주세요:

1. design-system.json 파일을 읽고 전체 내용을 이해하세요
2. Button 컴포넌트를 구현할 때:
   - colors.dark.primary 값 (#3B82F6)을 정확히 사용
   - borderRadius.md (0.5rem) 적용
   - typography.fontSize.base (1rem) 사용
3. 임의의 색상 값 (예: #1E40AF, #60A5FA) 사용 금지
4. 모든 값은 design-system.json에서 가져와야 함

위 규칙을 어기면 REJECTED됩니다."

문제 2: “Gemini 병렬 실행 시 일관성 문제”

증상: 3개 Gemini가 생성한 컴포넌트의 스타일이 미묘하게 다름

원인: 각 인스턴스가 독립적으로 해석

해결책:

공유 파일에 명확한 예제 포함:

design-system-examples.md:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Button 컴포넌트 예제

✅ 올바른 구현:

```tsx
<button className="
  px-4 py-2 
  bg-blue-600 
  text-white 
  rounded-md 
  hover:bg-blue-700
">
  Click me
</button>
```

❌ 잘못된 구현:

```tsx
// 임의의 색상 사용
<button className="bg-blue-500"> // WRONG

// Tailwind 값 직접 사용
<button className="rounded-lg"> // WRONG, should be rounded-md

// 간격 불일치
<button className="px-3 py-2"> // WRONG, should be px-4
```

모든 Gemini 인스턴스에 이 파일을 명시적으로 전달:

1
agy agent run gemini-1 --context design-system.json,design-system-examples.md

문제 3: “Claude 검토가 너무 엄격합니다”

증상: 사소한 문제까지 모두 지적하여 반복 수정이 너무 많음

원인: 검토 기준이 명확하지 않음

해결책:

review-criteria.md:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 품질 검토 기준

## Critical (반드시 수정)
- [ ] 디자인 토큰 값 불일치 (색상, 간격)
- [ ] 접근성 WCAG AA 미달
- [ ] TypeScript 타입 에러
- [ ] 기능 누락 (spec에 명시된 기능)

## Important (수정 권장)
- [ ] 성능 문제 (불필요한 리렌더링)
- [ ] 에러 처리 누락
- [ ] 로딩 상태 없음
- [ ] 반응형 미흡

## Nice-to-have (선택)
- [ ] 애니메이션 개선
- [ ] 주석 추가
- [ ] 코드 리팩토링
- [ ] 추가 기능 제안

검토 시 Critical은 반드시 지적, Nice-to-have는 언급만.

Claude 프롬프트에 포함:

1
2
3
design-system.json과 review-criteria.md를 기준으로 검토하세요.
Critical 항목만 "수정 필요"로 표시하고, 
Nice-to-have는 "개선 제안"으로 구분하세요.

성능 최적화

최적화 1: 캐싱

문제: 같은 디자인 시스템으로 여러 프로젝트 작업 시 매번 처음부터 설계

해결:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 디자인 시스템 템플릿 저장
mkdir -p ~/.agy/templates

# Claude로 기본 템플릿 생성
agy agent run claude-architect --input "
Create a reusable SaaS design system template
" --output ~/.agy/templates/saas-design-system.json

# 새 프로젝트에서 재사용
cp ~/.agy/templates/saas-design-system.json ./design-system.json

# 프로젝트별 커스터마이징만 요청
agy agent run claude-architect --input "
Customize this design system for a fintech app:
- Change primary color to green
- Add charts for financial data
"

효과: 디자인 시스템 생성 시간 15분 → 3분 (80% 절약)

최적화 2: 점진적 검토

문제: 모든 컴포넌트 완성 후 검토하면 대규모 수정 발생

해결:

컴포넌트 3-5개 단위로 검토:

1
2
3
4
5
6
7
8
9
10
11
# Gemini가 5개 생성
agy agent run gemini-builder --input "Build: Button, Input, Select, Checkbox, Radio"

# 즉시 Claude 검토
agy agent run claude-reviewer --input "Review just: Button, Input, Select, Checkbox, Radio"

# 수정 반영
agy agent run gemini-builder --input "Apply fixes from review"

# 다음 배치
agy agent run gemini-builder --input "Build: Card, Modal, Toast, Alert, Badge"

효과: 대규모 재작업 방지, 누적 오류 감소

최적화 3: Skill 버전 관리

UI/UX Pro Max Skill을 팀에 맞게 커스터마이징:

1
2
3
4
5
6
7
8
9
# 팀 전용 스타일 추가
echo "58,Corporate Modern,..." >> .agent/skills/ui-ux-pro-max/data/ui-styles.csv

# 회사 컬러 팔레트 추가
echo "custom-1,Company Brand,#FF6B00,..." >> .agent/skills/ui-ux-pro-max/data/color-palettes.csv

# Git으로 버전 관리
git add .agent/skills/
git commit -m "Add custom design patterns"

팀원들이 동일한 Skill 사용:

1
2
3
git clone team-repo
uipro init --ai antigravity
# 팀 전용 스타일이 자동으로 포함됨

결론

Claude와 Gemini의 협업은 단순히 “속도 향상”을 넘어, 인간 디자이너와 개발자의 협업 방식을 AI로 재현하는 것입니다.

핵심 원칙:

  1. 명확한 역할 분담
    • Claude: 창의성, 일관성, 품질 검토
    • Gemini: 실행, 병렬 처리, 반복 작업
  2. 상세한 명세
    • design-system.json은 “계약서”
    • 모든 값은 명시적으로 정의
    • 예제와 안티패턴 제공
  3. 점진적 개선
    • 단계별 검토
    • 빠른 피드백 루프
    • 누적 오류 방지
  4. 자동화
    • 스크립트로 워크플로우 고정
    • 반복 가능한 프로세스
    • 일관된 결과물

이 가이드의 패턴을 따르면, 1인 개발자도 팀 규모의 생산성을 달성할 수 있습니다.

다음 단계:

  1. 작은 프로젝트로 시작 (랜딩 페이지 1개)
  2. 워크플로우 개선 (병목 지점 파악)
  3. 팀에 적용 (템플릿 공유)
  4. 스케일업 (복잡한 시스템)

AI 협업의 미래는 지금 여기에 있습니다. 시작하세요!


작성 일자: 2026-01-20

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.