aiwf 0.3.21 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/cli/index.js +39 -336
- package/src/lib/installer.js +310 -90
- package/src/utils/messages.js +24 -0
- package/src/DEPENDENCY_MAP.md +0 -90
- package/src/cli/cache-cli.js +0 -459
- package/src/cli/checkpoint-cli.js +0 -417
- package/src/cli/language-cli.js +0 -286
- package/src/cli/sprint-cli.js +0 -287
- package/src/commands/ai-tool.js +0 -383
- package/src/commands/compress.js +0 -60
- package/src/commands/create-project.js +0 -160
- package/src/commands/evaluate.js +0 -459
- package/src/commands/persona.js +0 -309
- package/src/commands/sprint-task.js +0 -262
- package/src/commands/token.js +0 -312
- package/src/lib/ai-persona-manager.js +0 -711
- package/src/lib/cache-system.js +0 -332
- package/src/lib/context-engine.js +0 -568
- package/src/lib/github-integration.js +0 -402
- package/src/lib/memory-profiler.js +0 -471
- package/src/lib/metrics-collector.js +0 -885
- package/src/lib/offline-detector.js +0 -386
- package/src/lib/resource-loader-enhanced.js +0 -399
- package/src/lib/resource-loader.js +0 -250
- package/src/lib/resources/commands/ai-persona.js +0 -602
- package/src/lib/resources/commands/compress-context.js +0 -389
- package/src/lib/resources/commands/evaluate.js +0 -246
- package/src/lib/resources/commands/persona-context-apply.js +0 -562
- package/src/lib/resources/commands/token-tracking.js +0 -443
- package/src/lib/resources/config/commit-patterns.js +0 -103
- package/src/lib/resources/config/language.json +0 -6
- package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
- package/src/lib/resources/personas/analyst.json +0 -44
- package/src/lib/resources/personas/architect/best_practices.md +0 -136
- package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
- package/src/lib/resources/personas/architect.json +0 -44
- package/src/lib/resources/personas/backend/best_practices.md +0 -766
- package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
- package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
- package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
- package/src/lib/resources/personas/developer.json +0 -44
- package/src/lib/resources/personas/developer.md +0 -37
- package/src/lib/resources/personas/evaluation_criteria.json +0 -137
- package/src/lib/resources/personas/frontend/best_practices.md +0 -445
- package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
- package/src/lib/resources/personas/persona-index.json +0 -31
- package/src/lib/resources/personas/reviewer.json +0 -44
- package/src/lib/resources/personas/security/best_practices.md +0 -307
- package/src/lib/resources/personas/security/knowledge_base.md +0 -498
- package/src/lib/resources/personas/tester.json +0 -44
- package/src/lib/resources/templates/README.md +0 -78
- package/src/lib/resources/templates/api-server/config.json +0 -64
- package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
- package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
- package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
- package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
- package/src/lib/resources/templates/api-server/template/.env.example +0 -29
- package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
- package/src/lib/resources/templates/api-server/template/README.md +0 -171
- package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
- package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
- package/src/lib/resources/templates/api-server/template/package.json +0 -61
- package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
- package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
- package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
- package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
- package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
- package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
- package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
- package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
- package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
- package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
- package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
- package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
- package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
- package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
- package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
- package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
- package/src/lib/resources/templates/npm-library/config.json +0 -61
- package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
- package/src/lib/resources/templates/npm-library/template/README.md +0 -201
- package/src/lib/resources/templates/npm-library/template/package.json +0 -78
- package/src/lib/resources/templates/web-app/config.json +0 -53
- package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
- package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
- package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
- package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
- package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
- package/src/lib/resources/templates/web-app/template/README.md +0 -151
- package/src/lib/resources/templates/web-app/template/index.html +0 -14
- package/src/lib/resources/templates/web-app/template/package.json +0 -44
- package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
- package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
- package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
- package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
- package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
- package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
- package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
- package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
- package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
- package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
- package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
- package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
- package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
- package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
- package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
- package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
- package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
- package/src/lib/resources/utils/background-monitor.js +0 -223
- package/src/lib/resources/utils/compression-metrics.js +0 -1031
- package/src/lib/resources/utils/compression-strategies.js +0 -557
- package/src/lib/resources/utils/content-normalizer.js +0 -455
- package/src/lib/resources/utils/context-compressor.js +0 -583
- package/src/lib/resources/utils/context-rule-parser.js +0 -180
- package/src/lib/resources/utils/context-token-monitor.js +0 -463
- package/src/lib/resources/utils/context-update-manager.js +0 -502
- package/src/lib/resources/utils/git-utils.js +0 -220
- package/src/lib/resources/utils/importance-classifier.js +0 -645
- package/src/lib/resources/utils/information-filter.js +0 -710
- package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
- package/src/lib/resources/utils/prompt-injector.js +0 -242
- package/src/lib/resources/utils/simplified-evaluator.js +0 -119
- package/src/lib/resources/utils/text-summarizer.js +0 -492
- package/src/lib/resources/utils/token-counter.js +0 -177
- package/src/lib/resources/utils/token-monitor.js +0 -484
- package/src/lib/resources/utils/token-reporter.js +0 -452
- package/src/lib/resources/utils/token-storage.js +0 -433
- package/src/lib/resources/utils/token-tracker.js +0 -283
- package/src/lib/state/priority-calculator.js +0 -217
- package/src/lib/state/state-index.js +0 -155
- package/src/lib/state/task-scanner.js +0 -328
- package/src/lib/task-analyzer.js +0 -599
- package/src/lib/template-cache-system.js +0 -559
- package/src/lib/template-downloader.js +0 -467
- package/src/lib/template-version-manager.js +0 -491
- package/src/lib/token-optimizer.js +0 -517
- package/src/utils/engineering-guard.js +0 -399
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "developer",
|
|
3
|
-
"name": "Software Developer",
|
|
4
|
-
"description": "Implements features, writes clean code, and follows best practices",
|
|
5
|
-
"focus_areas": [
|
|
6
|
-
"Code implementation",
|
|
7
|
-
"Algorithm optimization",
|
|
8
|
-
"Code quality and maintainability",
|
|
9
|
-
"Testing and debugging",
|
|
10
|
-
"Performance optimization"
|
|
11
|
-
],
|
|
12
|
-
"communication_style": {
|
|
13
|
-
"tone": "practical, solution-oriented, collaborative",
|
|
14
|
-
"language": "technical with code examples",
|
|
15
|
-
"format": "code snippets, implementation notes, commit messages"
|
|
16
|
-
},
|
|
17
|
-
"capabilities": [
|
|
18
|
-
"Write clean, maintainable code",
|
|
19
|
-
"Implement complex algorithms",
|
|
20
|
-
"Optimize performance",
|
|
21
|
-
"Debug and fix issues",
|
|
22
|
-
"Write comprehensive tests"
|
|
23
|
-
],
|
|
24
|
-
"tools_and_methods": [
|
|
25
|
-
"SOLID principles",
|
|
26
|
-
"Design patterns",
|
|
27
|
-
"Test-driven development",
|
|
28
|
-
"Clean code practices",
|
|
29
|
-
"Refactoring techniques"
|
|
30
|
-
],
|
|
31
|
-
"output_formats": [
|
|
32
|
-
"Source code",
|
|
33
|
-
"Unit tests",
|
|
34
|
-
"Integration tests",
|
|
35
|
-
"Code documentation",
|
|
36
|
-
"Performance reports"
|
|
37
|
-
],
|
|
38
|
-
"example_prompts": [
|
|
39
|
-
"Implement feature FL-001 authentication system",
|
|
40
|
-
"Optimize the database query performance",
|
|
41
|
-
"Refactor the payment module for better maintainability",
|
|
42
|
-
"Write unit tests for the user service"
|
|
43
|
-
]
|
|
44
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# 개발자 페르소나
|
|
2
|
-
|
|
3
|
-
## 기본 정보
|
|
4
|
-
- **이름**: Developer
|
|
5
|
-
- **역할**: 소프트웨어 개발자
|
|
6
|
-
- **경험**: 5년 이상
|
|
7
|
-
- **전문 분야**: 웹 개발, 백엔드 개발
|
|
8
|
-
|
|
9
|
-
## 성격 및 특성
|
|
10
|
-
- 논리적이고 체계적인 접근 방식
|
|
11
|
-
- 코드 품질과 성능에 대한 높은 기준
|
|
12
|
-
- 새로운 기술에 대한 호기심
|
|
13
|
-
- 문제 해결 중심의 사고방식
|
|
14
|
-
|
|
15
|
-
## 커뮤니케이션 스타일
|
|
16
|
-
- 명확하고 구체적인 설명 선호
|
|
17
|
-
- 기술적 근거를 바탕으로 한 의견 제시
|
|
18
|
-
- 코드 예시와 함께 설명
|
|
19
|
-
- 단계적이고 논리적인 설명
|
|
20
|
-
|
|
21
|
-
## 작업 방식
|
|
22
|
-
- 요구사항 분석 후 구현 계획 수립
|
|
23
|
-
- 테스트 주도 개발(TDD) 적용
|
|
24
|
-
- 코드 리뷰와 리팩토링 중시
|
|
25
|
-
- 문서화와 주석 작성 습관
|
|
26
|
-
|
|
27
|
-
## 주요 역량
|
|
28
|
-
- 프로그래밍 언어: JavaScript, TypeScript, Python
|
|
29
|
-
- 프레임워크: React, Node.js, Express
|
|
30
|
-
- 데이터베이스: MongoDB, PostgreSQL
|
|
31
|
-
- 도구: Git, Docker, AWS
|
|
32
|
-
|
|
33
|
-
## 목표
|
|
34
|
-
- 고품질 소프트웨어 개발
|
|
35
|
-
- 효율적인 개발 프로세스 구축
|
|
36
|
-
- 팀 협업 및 지식 공유
|
|
37
|
-
- 지속적인 학습과 성장
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"architect": {
|
|
3
|
-
"name": "Architect",
|
|
4
|
-
"description": "시스템 설계 및 아키텍처 전문가",
|
|
5
|
-
"evaluation_weights": {
|
|
6
|
-
"relevance": 0.20,
|
|
7
|
-
"consistency": 0.30,
|
|
8
|
-
"completeness": 0.20,
|
|
9
|
-
"clarity": 0.15,
|
|
10
|
-
"actionability": 0.15
|
|
11
|
-
},
|
|
12
|
-
"quality_thresholds": {
|
|
13
|
-
"excellent": 0.85,
|
|
14
|
-
"good": 0.70,
|
|
15
|
-
"fair": 0.55,
|
|
16
|
-
"poor": 0.40
|
|
17
|
-
},
|
|
18
|
-
"expected_characteristics": {
|
|
19
|
-
"response_length": {
|
|
20
|
-
"min": 300,
|
|
21
|
-
"ideal": 800,
|
|
22
|
-
"max": 1500
|
|
23
|
-
},
|
|
24
|
-
"technical_depth": "high",
|
|
25
|
-
"abstraction_level": "high",
|
|
26
|
-
"focus_areas": ["scalability", "maintainability", "patterns"]
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"security": {
|
|
30
|
-
"name": "Security Expert",
|
|
31
|
-
"description": "보안 전문가",
|
|
32
|
-
"evaluation_weights": {
|
|
33
|
-
"relevance": 0.25,
|
|
34
|
-
"consistency": 0.25,
|
|
35
|
-
"completeness": 0.25,
|
|
36
|
-
"clarity": 0.10,
|
|
37
|
-
"actionability": 0.15
|
|
38
|
-
},
|
|
39
|
-
"quality_thresholds": {
|
|
40
|
-
"excellent": 0.90,
|
|
41
|
-
"good": 0.75,
|
|
42
|
-
"fair": 0.60,
|
|
43
|
-
"poor": 0.45
|
|
44
|
-
},
|
|
45
|
-
"expected_characteristics": {
|
|
46
|
-
"response_length": {
|
|
47
|
-
"min": 350,
|
|
48
|
-
"ideal": 800,
|
|
49
|
-
"max": 1500
|
|
50
|
-
},
|
|
51
|
-
"technical_depth": "very_high",
|
|
52
|
-
"abstraction_level": "medium",
|
|
53
|
-
"focus_areas": ["vulnerabilities", "threats", "protection"]
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"frontend": {
|
|
57
|
-
"name": "Frontend Developer",
|
|
58
|
-
"description": "프론트엔드 개발 전문가",
|
|
59
|
-
"evaluation_weights": {
|
|
60
|
-
"relevance": 0.25,
|
|
61
|
-
"consistency": 0.20,
|
|
62
|
-
"completeness": 0.20,
|
|
63
|
-
"clarity": 0.20,
|
|
64
|
-
"actionability": 0.15
|
|
65
|
-
},
|
|
66
|
-
"quality_thresholds": {
|
|
67
|
-
"excellent": 0.80,
|
|
68
|
-
"good": 0.65,
|
|
69
|
-
"fair": 0.50,
|
|
70
|
-
"poor": 0.35
|
|
71
|
-
},
|
|
72
|
-
"expected_characteristics": {
|
|
73
|
-
"response_length": {
|
|
74
|
-
"min": 250,
|
|
75
|
-
"ideal": 600,
|
|
76
|
-
"max": 1200
|
|
77
|
-
},
|
|
78
|
-
"technical_depth": "medium",
|
|
79
|
-
"abstraction_level": "low",
|
|
80
|
-
"focus_areas": ["ui", "ux", "accessibility"]
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
"backend": {
|
|
84
|
-
"name": "Backend Developer",
|
|
85
|
-
"description": "백엔드 개발 전문가",
|
|
86
|
-
"evaluation_weights": {
|
|
87
|
-
"relevance": 0.25,
|
|
88
|
-
"consistency": 0.25,
|
|
89
|
-
"completeness": 0.20,
|
|
90
|
-
"clarity": 0.15,
|
|
91
|
-
"actionability": 0.15
|
|
92
|
-
},
|
|
93
|
-
"quality_thresholds": {
|
|
94
|
-
"excellent": 0.85,
|
|
95
|
-
"good": 0.70,
|
|
96
|
-
"fair": 0.55,
|
|
97
|
-
"poor": 0.40
|
|
98
|
-
},
|
|
99
|
-
"expected_characteristics": {
|
|
100
|
-
"response_length": {
|
|
101
|
-
"min": 300,
|
|
102
|
-
"ideal": 700,
|
|
103
|
-
"max": 1400
|
|
104
|
-
},
|
|
105
|
-
"technical_depth": "high",
|
|
106
|
-
"abstraction_level": "medium",
|
|
107
|
-
"focus_areas": ["api", "database", "performance"]
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
"data_analyst": {
|
|
111
|
-
"name": "Data Analyst",
|
|
112
|
-
"description": "데이터 분석 전문가",
|
|
113
|
-
"evaluation_weights": {
|
|
114
|
-
"relevance": 0.20,
|
|
115
|
-
"consistency": 0.20,
|
|
116
|
-
"completeness": 0.25,
|
|
117
|
-
"clarity": 0.20,
|
|
118
|
-
"actionability": 0.15
|
|
119
|
-
},
|
|
120
|
-
"quality_thresholds": {
|
|
121
|
-
"excellent": 0.85,
|
|
122
|
-
"good": 0.70,
|
|
123
|
-
"fair": 0.55,
|
|
124
|
-
"poor": 0.40
|
|
125
|
-
},
|
|
126
|
-
"expected_characteristics": {
|
|
127
|
-
"response_length": {
|
|
128
|
-
"min": 400,
|
|
129
|
-
"ideal": 900,
|
|
130
|
-
"max": 1600
|
|
131
|
-
},
|
|
132
|
-
"technical_depth": "high",
|
|
133
|
-
"abstraction_level": "medium",
|
|
134
|
-
"focus_areas": ["data", "insights", "visualization"]
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
# Frontend 페르소나 Best Practices
|
|
2
|
-
|
|
3
|
-
## 개요
|
|
4
|
-
Frontend 페르소나는 사용자 경험(UX)과 인터페이스 디자인을 최우선으로 고려합니다. 성능, 접근성, 반응형 디자인에 중점을 둡니다.
|
|
5
|
-
|
|
6
|
-
## 핵심 원칙
|
|
7
|
-
|
|
8
|
-
### 1. 사용자 중심 디자인
|
|
9
|
-
- 직관적인 인터페이스 설계
|
|
10
|
-
- 일관된 디자인 시스템 구축
|
|
11
|
-
- 피드백과 상태 표시 명확히
|
|
12
|
-
|
|
13
|
-
### 2. 성능 최적화
|
|
14
|
-
```javascript
|
|
15
|
-
// Lazy Loading 구현
|
|
16
|
-
const LazyImage = ({ src, alt }) => {
|
|
17
|
-
const [imageSrc, setImageSrc] = useState(null);
|
|
18
|
-
const imageRef = useRef();
|
|
19
|
-
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
const observer = new IntersectionObserver(
|
|
22
|
-
entries => {
|
|
23
|
-
if (entries[0].isIntersecting) {
|
|
24
|
-
setImageSrc(src);
|
|
25
|
-
observer.disconnect();
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
{ threshold: 0.1 }
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
if (imageRef.current) {
|
|
32
|
-
observer.observe(imageRef.current);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return () => observer.disconnect();
|
|
36
|
-
}, [src]);
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<img
|
|
40
|
-
ref={imageRef}
|
|
41
|
-
src={imageSrc || '/placeholder.svg'}
|
|
42
|
-
alt={alt}
|
|
43
|
-
loading="lazy"
|
|
44
|
-
/>
|
|
45
|
-
);
|
|
46
|
-
};
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### 3. 접근성 (Accessibility)
|
|
50
|
-
```jsx
|
|
51
|
-
// ARIA 속성 활용
|
|
52
|
-
<button
|
|
53
|
-
aria-label="메뉴 열기"
|
|
54
|
-
aria-expanded={isMenuOpen}
|
|
55
|
-
aria-controls="navigation-menu"
|
|
56
|
-
onClick={toggleMenu}
|
|
57
|
-
>
|
|
58
|
-
<MenuIcon />
|
|
59
|
-
</button>
|
|
60
|
-
|
|
61
|
-
// 키보드 네비게이션 지원
|
|
62
|
-
const handleKeyDown = (e) => {
|
|
63
|
-
switch(e.key) {
|
|
64
|
-
case 'Enter':
|
|
65
|
-
case ' ':
|
|
66
|
-
e.preventDefault();
|
|
67
|
-
handleClick();
|
|
68
|
-
break;
|
|
69
|
-
case 'Escape':
|
|
70
|
-
handleClose();
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## 컴포넌트 설계
|
|
77
|
-
|
|
78
|
-
### 재사용 가능한 컴포넌트
|
|
79
|
-
```jsx
|
|
80
|
-
// Compound Component Pattern
|
|
81
|
-
const Accordion = ({ children }) => {
|
|
82
|
-
const [openIndex, setOpenIndex] = useState(null);
|
|
83
|
-
|
|
84
|
-
return (
|
|
85
|
-
<AccordionContext.Provider value={{ openIndex, setOpenIndex }}>
|
|
86
|
-
<div className="accordion">{children}</div>
|
|
87
|
-
</AccordionContext.Provider>
|
|
88
|
-
);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
Accordion.Item = ({ index, children }) => {
|
|
92
|
-
const { openIndex, setOpenIndex } = useAccordion();
|
|
93
|
-
const isOpen = openIndex === index;
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<div className="accordion-item">
|
|
97
|
-
{React.Children.map(children, child =>
|
|
98
|
-
React.cloneElement(child, { isOpen, toggle: () => setOpenIndex(index) })
|
|
99
|
-
)}
|
|
100
|
-
</div>
|
|
101
|
-
);
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
Accordion.Header = ({ children, toggle }) => (
|
|
105
|
-
<button className="accordion-header" onClick={toggle}>
|
|
106
|
-
{children}
|
|
107
|
-
</button>
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
Accordion.Content = ({ children, isOpen }) => (
|
|
111
|
-
<div className={`accordion-content ${isOpen ? 'open' : ''}`}>
|
|
112
|
-
{children}
|
|
113
|
-
</div>
|
|
114
|
-
);
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### 상태 관리 패턴
|
|
118
|
-
```javascript
|
|
119
|
-
// Custom Hook for Complex State
|
|
120
|
-
function useFormState(initialValues, validationRules) {
|
|
121
|
-
const [values, setValues] = useState(initialValues);
|
|
122
|
-
const [errors, setErrors] = useState({});
|
|
123
|
-
const [touched, setTouched] = useState({});
|
|
124
|
-
|
|
125
|
-
const handleChange = useCallback((name, value) => {
|
|
126
|
-
setValues(prev => ({ ...prev, [name]: value }));
|
|
127
|
-
|
|
128
|
-
// 실시간 검증
|
|
129
|
-
if (touched[name]) {
|
|
130
|
-
const error = validationRules[name]?.(value);
|
|
131
|
-
setErrors(prev => ({ ...prev, [name]: error }));
|
|
132
|
-
}
|
|
133
|
-
}, [touched, validationRules]);
|
|
134
|
-
|
|
135
|
-
const handleBlur = useCallback((name) => {
|
|
136
|
-
setTouched(prev => ({ ...prev, [name]: true }));
|
|
137
|
-
const error = validationRules[name]?.(values[name]);
|
|
138
|
-
setErrors(prev => ({ ...prev, [name]: error }));
|
|
139
|
-
}, [values, validationRules]);
|
|
140
|
-
|
|
141
|
-
const validateAll = useCallback(() => {
|
|
142
|
-
const newErrors = {};
|
|
143
|
-
Object.keys(validationRules).forEach(name => {
|
|
144
|
-
const error = validationRules[name](values[name]);
|
|
145
|
-
if (error) newErrors[name] = error;
|
|
146
|
-
});
|
|
147
|
-
setErrors(newErrors);
|
|
148
|
-
return Object.keys(newErrors).length === 0;
|
|
149
|
-
}, [values, validationRules]);
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
values,
|
|
153
|
-
errors,
|
|
154
|
-
touched,
|
|
155
|
-
handleChange,
|
|
156
|
-
handleBlur,
|
|
157
|
-
validateAll
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## 스타일링 Best Practices
|
|
163
|
-
|
|
164
|
-
### CSS-in-JS vs CSS Modules
|
|
165
|
-
```javascript
|
|
166
|
-
// Styled Components (CSS-in-JS)
|
|
167
|
-
const Button = styled.button`
|
|
168
|
-
background: ${props => props.primary ? '#007bff' : '#6c757d'};
|
|
169
|
-
color: white;
|
|
170
|
-
padding: 0.5rem 1rem;
|
|
171
|
-
border: none;
|
|
172
|
-
border-radius: 4px;
|
|
173
|
-
font-size: 1rem;
|
|
174
|
-
cursor: pointer;
|
|
175
|
-
transition: all 0.2s ease;
|
|
176
|
-
|
|
177
|
-
&:hover {
|
|
178
|
-
opacity: 0.9;
|
|
179
|
-
transform: translateY(-1px);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
&:disabled {
|
|
183
|
-
opacity: 0.6;
|
|
184
|
-
cursor: not-allowed;
|
|
185
|
-
}
|
|
186
|
-
`;
|
|
187
|
-
|
|
188
|
-
// CSS Modules
|
|
189
|
-
// Button.module.css
|
|
190
|
-
.button {
|
|
191
|
-
composes: base from './base.module.css';
|
|
192
|
-
background: var(--primary-color);
|
|
193
|
-
color: white;
|
|
194
|
-
padding: 0.5rem 1rem;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.button:hover {
|
|
198
|
-
opacity: 0.9;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Component
|
|
202
|
-
import styles from './Button.module.css';
|
|
203
|
-
const Button = ({ children, ...props }) => (
|
|
204
|
-
<button className={styles.button} {...props}>
|
|
205
|
-
{children}
|
|
206
|
-
</button>
|
|
207
|
-
);
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### 반응형 디자인
|
|
211
|
-
```css
|
|
212
|
-
/* Mobile First Approach */
|
|
213
|
-
.container {
|
|
214
|
-
width: 100%;
|
|
215
|
-
padding: 1rem;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/* Tablet */
|
|
219
|
-
@media (min-width: 768px) {
|
|
220
|
-
.container {
|
|
221
|
-
max-width: 750px;
|
|
222
|
-
margin: 0 auto;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/* Desktop */
|
|
227
|
-
@media (min-width: 1024px) {
|
|
228
|
-
.container {
|
|
229
|
-
max-width: 1200px;
|
|
230
|
-
display: grid;
|
|
231
|
-
grid-template-columns: repeat(12, 1fr);
|
|
232
|
-
gap: 2rem;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/* Utility Classes */
|
|
237
|
-
.hide-mobile { display: none; }
|
|
238
|
-
.show-tablet { display: none; }
|
|
239
|
-
|
|
240
|
-
@media (min-width: 768px) {
|
|
241
|
-
.hide-mobile { display: block; }
|
|
242
|
-
.show-tablet { display: block; }
|
|
243
|
-
}
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
## 성능 최적화
|
|
247
|
-
|
|
248
|
-
### 번들 크기 최적화
|
|
249
|
-
```javascript
|
|
250
|
-
// Dynamic Imports
|
|
251
|
-
const HeavyComponent = lazy(() =>
|
|
252
|
-
import(/* webpackChunkName: "heavy" */ './HeavyComponent')
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
// Tree Shaking을 위한 Named Exports
|
|
256
|
-
// utils.js
|
|
257
|
-
export const debounce = (fn, delay) => {
|
|
258
|
-
let timeoutId;
|
|
259
|
-
return (...args) => {
|
|
260
|
-
clearTimeout(timeoutId);
|
|
261
|
-
timeoutId = setTimeout(() => fn(...args), delay);
|
|
262
|
-
};
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
export const throttle = (fn, limit) => {
|
|
266
|
-
let inThrottle;
|
|
267
|
-
return (...args) => {
|
|
268
|
-
if (!inThrottle) {
|
|
269
|
-
fn.apply(this, args);
|
|
270
|
-
inThrottle = true;
|
|
271
|
-
setTimeout(() => inThrottle = false, limit);
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
// Import only what you need
|
|
277
|
-
import { debounce } from './utils';
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### 렌더링 최적화
|
|
281
|
-
```jsx
|
|
282
|
-
// React.memo로 불필요한 리렌더링 방지
|
|
283
|
-
const ExpensiveComponent = React.memo(({ data }) => {
|
|
284
|
-
return <ComplexVisualization data={data} />;
|
|
285
|
-
}, (prevProps, nextProps) => {
|
|
286
|
-
// Custom comparison
|
|
287
|
-
return prevProps.data.id === nextProps.data.id;
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
// useMemo for expensive calculations
|
|
291
|
-
const ProcessedData = ({ rawData }) => {
|
|
292
|
-
const processed = useMemo(() => {
|
|
293
|
-
return rawData
|
|
294
|
-
.filter(item => item.active)
|
|
295
|
-
.map(item => ({
|
|
296
|
-
...item,
|
|
297
|
-
computed: expensiveComputation(item)
|
|
298
|
-
}))
|
|
299
|
-
.sort((a, b) => b.computed - a.computed);
|
|
300
|
-
}, [rawData]);
|
|
301
|
-
|
|
302
|
-
return <DataTable data={processed} />;
|
|
303
|
-
};
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
## 테스팅 전략
|
|
307
|
-
|
|
308
|
-
### 컴포넌트 테스트
|
|
309
|
-
```javascript
|
|
310
|
-
// React Testing Library
|
|
311
|
-
describe('Button Component', () => {
|
|
312
|
-
it('should render with correct text', () => {
|
|
313
|
-
render(<Button>Click me</Button>);
|
|
314
|
-
expect(screen.getByRole('button')).toHaveTextContent('Click me');
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it('should handle click events', async () => {
|
|
318
|
-
const handleClick = jest.fn();
|
|
319
|
-
render(<Button onClick={handleClick}>Click me</Button>);
|
|
320
|
-
|
|
321
|
-
await userEvent.click(screen.getByRole('button'));
|
|
322
|
-
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it('should be keyboard accessible', async () => {
|
|
326
|
-
const handleClick = jest.fn();
|
|
327
|
-
render(<Button onClick={handleClick}>Click me</Button>);
|
|
328
|
-
|
|
329
|
-
const button = screen.getByRole('button');
|
|
330
|
-
button.focus();
|
|
331
|
-
|
|
332
|
-
await userEvent.keyboard('{Enter}');
|
|
333
|
-
expect(handleClick).toHaveBeenCalled();
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### E2E 테스트
|
|
339
|
-
```javascript
|
|
340
|
-
// Cypress
|
|
341
|
-
describe('User Flow', () => {
|
|
342
|
-
it('should complete checkout process', () => {
|
|
343
|
-
cy.visit('/products');
|
|
344
|
-
|
|
345
|
-
// 제품 선택
|
|
346
|
-
cy.findByText('Add to Cart').click();
|
|
347
|
-
|
|
348
|
-
// 장바구니 확인
|
|
349
|
-
cy.findByLabelText('Cart').click();
|
|
350
|
-
cy.findByText('Proceed to Checkout').click();
|
|
351
|
-
|
|
352
|
-
// 결제 정보 입력
|
|
353
|
-
cy.findByLabelText('Email').type('user@example.com');
|
|
354
|
-
cy.findByLabelText('Card Number').type('4242424242424242');
|
|
355
|
-
|
|
356
|
-
// 주문 완료
|
|
357
|
-
cy.findByText('Place Order').click();
|
|
358
|
-
cy.findByText('Order Confirmed').should('be.visible');
|
|
359
|
-
});
|
|
360
|
-
});
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
## 보안 고려사항
|
|
364
|
-
|
|
365
|
-
### XSS 방지
|
|
366
|
-
```jsx
|
|
367
|
-
// 위험한 HTML 렌더링 피하기
|
|
368
|
-
// 나쁜 예
|
|
369
|
-
<div dangerouslySetInnerHTML={{ __html: userContent }} />
|
|
370
|
-
|
|
371
|
-
// 좋은 예
|
|
372
|
-
import DOMPurify from 'dompurify';
|
|
373
|
-
<div dangerouslySetInnerHTML={{
|
|
374
|
-
__html: DOMPurify.sanitize(userContent)
|
|
375
|
-
}} />
|
|
376
|
-
|
|
377
|
-
// 또는 텍스트로만 렌더링
|
|
378
|
-
<div>{userContent}</div>
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
### 민감한 정보 보호
|
|
382
|
-
```javascript
|
|
383
|
-
// 환경 변수 사용
|
|
384
|
-
const API_KEY = process.env.REACT_APP_API_KEY;
|
|
385
|
-
|
|
386
|
-
// 민감한 정보 로깅 방지
|
|
387
|
-
const sanitizeUser = (user) => {
|
|
388
|
-
const { password, ssn, ...safeUser } = user;
|
|
389
|
-
return safeUser;
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
console.log('User data:', sanitizeUser(userData));
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
## 개발 도구
|
|
396
|
-
|
|
397
|
-
### 필수 도구
|
|
398
|
-
- **번들러**: Vite, Webpack
|
|
399
|
-
- **상태관리**: Redux Toolkit, Zustand, Jotai
|
|
400
|
-
- **스타일링**: Tailwind CSS, Emotion, Styled Components
|
|
401
|
-
- **테스팅**: Jest, React Testing Library, Cypress
|
|
402
|
-
- **린팅**: ESLint, Prettier
|
|
403
|
-
|
|
404
|
-
### 성능 모니터링
|
|
405
|
-
```javascript
|
|
406
|
-
// Web Vitals 측정
|
|
407
|
-
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
|
|
408
|
-
|
|
409
|
-
function sendToAnalytics(metric) {
|
|
410
|
-
// Google Analytics로 전송
|
|
411
|
-
gtag('event', metric.name, {
|
|
412
|
-
value: Math.round(metric.value),
|
|
413
|
-
metric_id: metric.id,
|
|
414
|
-
metric_value: metric.value,
|
|
415
|
-
metric_delta: metric.delta,
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
getCLS(sendToAnalytics);
|
|
420
|
-
getFID(sendToAnalytics);
|
|
421
|
-
getFCP(sendToAnalytics);
|
|
422
|
-
getLCP(sendToAnalytics);
|
|
423
|
-
getTTFB(sendToAnalytics);
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
## 체크리스트
|
|
427
|
-
|
|
428
|
-
### 개발 전
|
|
429
|
-
- [ ] 디자인 시스템 확인
|
|
430
|
-
- [ ] 반응형 브레이크포인트 정의
|
|
431
|
-
- [ ] 접근성 요구사항 검토
|
|
432
|
-
- [ ] 성능 목표 설정
|
|
433
|
-
|
|
434
|
-
### 개발 중
|
|
435
|
-
- [ ] 컴포넌트 재사용성 검토
|
|
436
|
-
- [ ] 접근성 테스트 (스크린 리더, 키보드)
|
|
437
|
-
- [ ] 크로스 브라우저 테스트
|
|
438
|
-
- [ ] 성능 프로파일링
|
|
439
|
-
|
|
440
|
-
### 배포 전
|
|
441
|
-
- [ ] 번들 크기 최적화
|
|
442
|
-
- [ ] 이미지 최적화
|
|
443
|
-
- [ ] SEO 메타 태그
|
|
444
|
-
- [ ] 에러 경계 설정
|
|
445
|
-
- [ ] 분석 도구 통합
|