# π¨ UI/UX μ€κ³ - WorkflowMCP Dashboard API
## π― μ€κ³ μμΉ
1. **κΈ°μ‘΄ μ¬μ©μ κ²½ν 보쑴**: νμ¬ λμ보λ μ¬μ©μμκ² μν₯ μμ
2. **μ μ§μ νμ₯**: κΈ°μ‘΄ κΈ°λ₯ μμ API κΈ°λ₯ λ μ΄μ΄ μΆκ°
3. **νμ νΈνμ±**: κΈ°μ‘΄ API μ¬μ©μμκ² λ³κ²½ μ¬ν λ―Έλ°μ
4. **μκΈ° μ€λͺ
μ **: μ μ¬μ©μλ μ½κ² νμ΅ κ°λ₯
5. **κ°λ°μ μ°μ **: API μ¬μ©μ±κ³Ό κ°λ°μ κ²½ν μ΅μ°μ
## π κΈ°μ‘΄ μμ€ν
νΈνμ± μ λ΅
### 1. κΈ°μ‘΄ API 보쑴 λ°©μ
#### νμ¬ μν λΆμ νμ
```javascript
// ꡬν μ κΈ°μ‘΄ API μ‘°μ¬ νμ
const existingApiAudit = {
// 1. νμ¬ λμ보λκ° μ¬μ©νλ API μλν¬μΈνΈ μλ³
currentEndpoints: [
// μ: '/api/prds', '/api/tasks' λ±μ΄ μ΄λ―Έ μ‘΄μ¬ν μ μμ
],
// 2. νμ¬ API μλ΅ νμ λΆμ
currentResponseFormats: {
// κΈ°μ‘΄ νμμ΄ μλ€λ©΄ λμΌνκ² μ μ§
},
// 3. νμ¬ URL λΌμ°ν
ν¨ν΄ νμΈ
currentRouting: {
// κΈ°μ‘΄ λΌμ°ν
κ³Ό μΆ©λνμ§ μλλ‘
}
};
```
#### νΈνμ± λ³΄μ₯ μ λ΅
```javascript
// κΈ°μ‘΄ API λν ν¨ν΄
class BackwardCompatibilityLayer {
constructor() {
this.legacyFormatMap = new Map();
this.versionDetection = new Map();
}
// κΈ°μ‘΄ API νΈμΆ κ°μ§ λ° κΈ°μ‘΄ νμμΌλ‘ μλ΅
detectLegacyRequest(req) {
// User-Agent, Headers, URL ν¨ν΄μΌλ‘ κΈ°μ‘΄ μ¬μ©μ κ°μ§
if (req.headers['user-agent']?.includes('SvelteKit') ||
req.headers['x-legacy-client'] === 'true') {
return true;
}
return false;
}
formatResponse(data, isLegacy = false) {
if (isLegacy) {
// κΈ°μ‘΄ νμ μ μ§
return data;
} else {
// μλ‘μ΄ νμ€ νμ
return {
success: true,
data: data,
links: this.generateHateoasLinks(),
timestamp: new Date().toISOString()
};
}
}
}
```
### 2. μ μ§μ λ§μ΄κ·Έλ μ΄μ
κ³ν
#### Phase 1: μ μλν¬μΈνΈλ§ μΆκ°
```
κΈ°μ‘΄ μ μ§:
- /api/prds (κΈ°μ‘΄ νμ)
- /api/tasks (κΈ°μ‘΄ νμ)
- /api/documents (κΈ°μ‘΄ νμ)
μλ‘ μΆκ°:
- /api/v1/prds (μ νμ€ νμ)
- /api/v1/tasks (μ νμ€ νμ)
- /api/v1/documents (μ νμ€ νμ)
- /api/help/* (μμ μ κ·)
- /api/discovery/* (μμ μ κ·)
```
#### Phase 2: κΈ°μ‘΄ API μ μ§μ νμ₯ (μ νμ¬ν)
```javascript
// κΈ°μ‘΄ μλν¬μΈνΈμ μ νμ κΈ°λ₯ μΆκ°
app.get('/api/prds', (req, res) => {
// κΈ°μ‘΄ λ‘μ§ μ μ§
const legacyResult = await existingPrdHandler(req);
// μλ‘μ΄ ν΄λΌμ΄μΈνΈ μμ² μμλ§ νμ₯ κΈ°λ₯ μ 곡
if (req.query.include_links === 'true') {
legacyResult.links = generateHateoasLinks();
}
if (req.query.include_help === 'true') {
legacyResult.help = '/api/help/prds';
}
res.json(legacyResult);
});
```
### 3. κΈ°μ‘΄ λμ보λ μν₯λ μ΅μν
#### λμ보λ API μ¬μ© ν¨ν΄ 보쑴
```javascript
// λμ보λ μ μ© λ―Έλ€μ¨μ΄
const dashboardCompatibility = (req, res, next) => {
// λμ보λμμ μ€λ μμ² κ°μ§
if (req.headers['referer']?.includes('localhost:3301') ||
req.headers['x-dashboard-client'] === 'true') {
// λμ보λμ© μλ΅ νμ μ μ§
req.useLegacyFormat = true;
req.skipHateoas = true;
}
next();
};
// λμ보λ μμ²μ κΈ°μ‘΄ νμ μ μ§
app.use('/api', dashboardCompatibility);
```
## π₯οΈ API μ¬μ©μ± μ€κ³
### 1. κ°λ°μ κ²½ν (DX) μ΅μ ν
#### API νμ μΈν°νμ΄μ€
```html
<!-- /api/console - λ΄μ₯ API νμ λꡬ -->
<!DOCTYPE html>
<html>
<head>
<title>WorkflowMCP API Console</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; margin: 0; }
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
.api-section { margin: 20px 0; padding: 20px; border: 1px solid #ddd; border-radius: 8px; }
.endpoint { background: #f5f5f5; padding: 10px; margin: 10px 0; border-radius: 4px; }
.method { display: inline-block; padding: 4px 8px; border-radius: 3px; color: white; font-weight: bold; }
.get { background: #4CAF50; }
.post { background: #2196F3; }
.put { background: #FF9800; }
.delete { background: #F44336; }
.try-button { background: #007bff; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; }
.response-area { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px; padding: 15px; margin-top: 10px; }
.help-link { color: #007bff; text-decoration: none; margin-left: 10px; }
.help-link:hover { text-decoration: underline; }
</style>
</head>
<body>
<div class="container">
<h1>π WorkflowMCP API Console</h1>
<p>Interactive API exploration tool. No authentication required for local development.</p>
<!-- API Discovery Section -->
<div class="api-section">
<h2>π Start Here - API Discovery</h2>
<div class="endpoint">
<span class="method get">GET</span>
<strong>/api</strong>
<span> - Complete API overview</span>
<a href="/api/help/getting-started" class="help-link">π Getting Started Guide</a>
<br>
<button class="try-button" onclick="tryEndpoint('/api')">Try It</button>
</div>
</div>
<!-- PRD Management Section -->
<div class="api-section">
<h2>π PRD Management</h2>
<div class="endpoint">
<span class="method get">GET</span>
<strong>/api/prds</strong>
<span> - List all PRDs</span>
<a href="/api/help/prds/collection" class="help-link">π Help</a>
<br>
<button class="try-button" onclick="tryEndpoint('/api/prds')">Try It</button>
</div>
<div class="endpoint">
<span class="method post">POST</span>
<strong>/api/prds</strong>
<span> - Create new PRD</span>
<a href="/api/help/prds/examples" class="help-link">π Examples</a>
<br>
<button class="try-button" onclick="showCreateForm('prd')">Create PRD</button>
</div>
</div>
<!-- Task Management Section -->
<div class="api-section">
<h2>π Task Management</h2>
<div class="endpoint">
<span class="method get">GET</span>
<strong>/api/tasks</strong>
<span> - List all tasks</span>
<a href="/api/help/tasks/collection" class="help-link">π Help</a>
<br>
<button class="try-button" onclick="tryEndpoint('/api/tasks')">Try It</button>
</div>
</div>
<!-- Response Area -->
<div class="api-section">
<h3>π€ API Response</h3>
<div id="response-area" class="response-area">
<p><em>Click "Try It" on any endpoint above to see the response here.</em></p>
</div>
</div>
<!-- Quick Help -->
<div class="api-section">
<h3>π Quick Help</h3>
<ul>
<li><a href="/api/help/getting-started">Getting Started Guide</a> - New to the API? Start here!</li>
<li><a href="/api/discovery/categories">Browse by Category</a> - Explore API by functional area</li>
<li><a href="/api/health">System Health</a> - Check if everything is running smoothly</li>
<li><a href="/api/help/errors">Error Reference</a> - Common errors and solutions</li>
</ul>
</div>
</div>
<script>
async function tryEndpoint(endpoint) {
const responseArea = document.getElementById('response-area');
responseArea.innerHTML = '<p><em>Loading...</em></p>';
try {
const response = await fetch(endpoint);
const data = await response.json();
responseArea.innerHTML = `
<h4>Response (${response.status})</h4>
<pre style="white-space: pre-wrap; overflow-x: auto;">${JSON.stringify(data, null, 2)}</pre>
`;
} catch (error) {
responseArea.innerHTML = `
<h4>Error</h4>
<p style="color: red;">${error.message}</p>
`;
}
}
function showCreateForm(type) {
const responseArea = document.getElementById('response-area');
const examples = {
prd: {
title: "User Authentication System",
description: "Implement JWT-based user authentication",
priority: "high",
status: "draft"
}
};
responseArea.innerHTML = `
<h4>Create ${type.toUpperCase()}</h4>
<textarea id="create-data" style="width: 100%; height: 200px; font-family: monospace;">
${JSON.stringify(examples[type], null, 2)}
</textarea>
<br><br>
<button class="try-button" onclick="submitCreate('${type}')">Submit</button>
<a href="/api/help/${type}s/examples" class="help-link">π More Examples</a>
`;
}
async function submitCreate(type) {
const data = document.getElementById('create-data').value;
const responseArea = document.getElementById('response-area');
try {
const response = await fetch(`/api/${type}s`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: data
});
const result = await response.json();
responseArea.innerHTML = `
<h4>Create Response (${response.status})</h4>
<pre style="white-space: pre-wrap;">${JSON.stringify(result, null, 2)}</pre>
`;
} catch (error) {
responseArea.innerHTML = `
<h4>Error</h4>
<p style="color: red;">${error.message}</p>
`;
}
}
</script>
</body>
</html>
```
### 2. μ μ§μ νμ΅ κ²½ν
#### νμ΅ κ²½λ‘ μ€κ³
```
Level 1: κΈ°λ³Έ νμ
βββββββββββββββββββ
β GET /api β β μμμ
βββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β μΉ΄ν
κ³ λ¦¬ μ ν β
β (prds/tasks) β
βββββββββββββββββββ
Level 2: κΈ°λ₯ μ΄ν΄
βββββββββββββββββββ
β /api/help/prds/ β β λμλ§ νμΈ
β overview β
βββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β μμ νμΈ β
β /help/examples β
βββββββββββββββββββ
Level 3: μ€μ μ¬μ©
βββββββββββββββββββ
β μ€ν€λ§ νμΈ β
β /api/schemas β
βββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β API νΈμΆ β
β POST /api/prds β
βββββββββββββββββββ
```
#### μν©λ³ κ°μ΄λ μ 곡
```json
// GET /api/help/getting-started?context=new_session
{
"title": "New Session Quick Start",
"estimated_time": "5 minutes",
"steps": [
{
"step": 1,
"title": "Explore the API structure",
"action": "GET /api",
"why": "Understand what's available and where to find it"
},
{
"step": 2,
"title": "Choose your area of interest",
"options": [
{"area": "PRD Management", "start": "/api/help/prds/overview"},
{"area": "Task Management", "start": "/api/help/tasks/overview"},
{"area": "Document Management", "start": "/api/help/documents/overview"}
]
},
{
"step": 3,
"title": "Try a simple operation",
"suggestion": "GET /api/prds - List existing PRDs (safe, read-only)"
}
],
"shortcuts": {
"api_console": "/api/console",
"common_examples": "/api/help/examples/common",
"troubleshooting": "/api/help/troubleshooting"
}
}
```
### 3. μλ¬ κ²½ν κ°μ
#### μΉνμ μλ¬ λ©μμ§
```json
// 400 μλ¬ μλ΅ μμ
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "We couldn't process your request due to some validation issues",
"user_friendly_message": "It looks like some required information is missing. Let me help you fix this!",
"details": [
"The 'title' field is required and cannot be empty",
"The 'priority' field must be one of: high, medium, low"
],
"how_to_fix": {
"title": "Add a descriptive title like 'User Authentication System'",
"priority": "Set priority to 'high', 'medium', or 'low'"
},
"helpful_links": {
"examples": "/api/help/prds/examples",
"schema": "/api/schemas/prd",
"help": "/api/help/prds/troubleshooting"
}
},
"request_id": "req-123",
"timestamp": "2025-09-11T16:00:00Z"
}
```
#### μλ¬ λ³΅κ΅¬ κ°μ΄λ
```json
// GET /api/help/errors/validation
{
"title": "Validation Error Solutions",
"common_scenarios": [
{
"error": "Field 'title' is required",
"cause": "Empty or missing title in request body",
"solution": "Add a title property with a non-empty string value",
"example": {
"wrong": "{}",
"correct": "{\"title\": \"My PRD Title\"}"
}
},
{
"error": "Invalid priority value",
"cause": "Priority field contains invalid value",
"solution": "Use only 'high', 'medium', or 'low'",
"example": {
"wrong": "{\"priority\": \"urgent\"}",
"correct": "{\"priority\": \"high\"}"
}
}
],
"prevention_tips": [
"Always check the schema first: GET /api/schemas/prd",
"Use the API console for testing: /api/console",
"Review examples: /api/help/prds/examples"
]
}
```
## π± λ€μν ν΄λΌμ΄μΈνΈ μ§μ
### 1. ν΄λΌμ΄μΈνΈλ³ μ΅μ ν
#### μΉ λμ보λ (κΈ°μ‘΄ μ μ§)
```javascript
// λμ보λλ κΈ°μ‘΄ ν¨ν΄ μ μ§
const dashboardApiClient = {
// κΈ°μ‘΄ νμμΌλ‘ κ³μ νΈμΆ
async getPrds() {
const response = await fetch('/api/prds', {
headers: { 'X-Dashboard-Client': 'true' }
});
return response.json(); // κΈ°μ‘΄ νμ μλ΅
}
};
```
#### Claude Code μΈμ
(μ κ· μ΅μ ν)
```javascript
// Claude Codeμ© μ΅μ νλ μλ΅
const claudeApiClient = {
async discoverApi() {
// μκΈ° μ€λͺ
μ μλ΅
const response = await fetch('/api', {
headers: { 'User-Agent': 'Claude-Code-Session' }
});
return response.json(); // μ νμ€ νμ
},
async getHelp(topic) {
const response = await fetch(`/api/help/${topic}`);
return response.json(); // ꡬ쑰νλ λμλ§
}
};
```
#### λͺ¨λ°μΌ/μ¨λνν° (ν₯ν μ§μ)
```javascript
// λͺ¨λ°μΌ μΉνμ μλ΅
app.get('/api/*', (req, res, next) => {
if (req.headers['user-agent']?.includes('Mobile')) {
req.mobileOptimized = true; // κ°μνλ μλ΅
}
next();
});
```
### 2. μλ΅ νμ μ μ
#### 컨ν
μ€νΈλ³ μλ΅ μ‘°μ
```javascript
class ResponseFormatter {
formatForClient(data, clientType) {
switch (clientType) {
case 'dashboard':
// κΈ°μ‘΄ νμ μ μ§
return data;
case 'claude-code':
// μκΈ° μ€λͺ
μ νμ
return {
success: true,
data: data,
links: this.generateHelpLinks(),
guidance: this.generateGuidance(data),
timestamp: new Date().toISOString()
};
case 'mobile':
// μ΅μνλ νμ
return {
data: data,
has_more: data.length >= this.limit
};
default:
// νμ€ νμ
return this.standardFormat(data);
}
}
generateGuidance(data) {
// λ°μ΄ν° κΈ°λ° λ€μ λ¨κ³ μ μ
if (Array.isArray(data) && data.length === 0) {
return {
suggestion: "No items found. Would you like to create one?",
next_action: "POST " + this.getCreateEndpoint()
};
}
return null;
}
}
```
## π§ κ°λ° λꡬ λ° μ νΈλ¦¬ν°
### 1. API κ²μ¦ λꡬ
#### μλ νΈνμ± κ²μ¬κΈ°
```javascript
// GET /api/compatibility/check
class CompatibilityChecker {
async checkBackwardCompatibility() {
const results = {
dashboard_compatibility: await this.testDashboardEndpoints(),
mcp_compatibility: await this.testMcpIntegration(),
breaking_changes: await this.detectBreakingChanges()
};
return {
status: results.breaking_changes.length === 0 ? 'compatible' : 'warnings',
results: results,
recommendations: this.generateRecommendations(results)
};
}
async testDashboardEndpoints() {
// λμ보λκ° μ¬μ©νλ λͺ¨λ μλν¬μΈνΈ ν
μ€νΈ
const dashboardEndpoints = ['/api/prds', '/api/tasks', '/api/documents'];
const results = [];
for (const endpoint of dashboardEndpoints) {
try {
const response = await fetch(endpoint, {
headers: { 'X-Dashboard-Client': 'true' }
});
results.push({
endpoint,
status: response.status,
compatible: response.ok
});
} catch (error) {
results.push({
endpoint,
status: 'error',
compatible: false,
error: error.message
});
}
}
return results;
}
}
```
### 2. λ§μ΄κ·Έλ μ΄μ
λꡬ
#### μ μ§μ μ
κ·Έλ μ΄λ λμ°λ―Έ
```javascript
// GET /api/migration/status
class MigrationHelper {
checkCurrentUsage() {
return {
legacy_endpoints_usage: this.analyzeLegacyUsage(),
new_endpoints_adoption: this.analyzeNewEndpointUsage(),
migration_readiness: this.assessMigrationReadiness()
};
}
generateMigrationPlan() {
return {
phases: [
{
phase: 1,
description: "Add new v1 endpoints alongside existing",
impact: "Zero - existing functionality unchanged",
duration: "1 day"
},
{
phase: 2,
description: "Enhance existing endpoints with optional features",
impact: "Minimal - opt-in only",
duration: "2 days"
},
{
phase: 3,
description: "Deprecate old formats (optional)",
impact: "Planned migration required",
duration: "Planned timeline"
}
]
};
}
}
```
## π μ¬μ©μ± λ©νΈλ¦
### 1. API μ¬μ© ν¨ν΄ λΆμ
#### μ¬μ©μ± μ§ν μμ§
```javascript
class UsabilityMetrics {
trackApiDiscovery(session) {
return {
discovery_path: session.endpoints_visited,
time_to_first_success: session.first_successful_call_time,
help_usage: session.help_endpoints_accessed,
error_recovery_rate: session.errors_resolved / session.total_errors
};
}
generateUsabilityReport() {
return {
top_entry_points: [
{ endpoint: '/api', usage_count: 1245 },
{ endpoint: '/api/help/getting-started', usage_count: 892 }
],
common_user_journeys: [
['GET /api', 'GET /api/help/prds', 'GET /api/prds', 'POST /api/prds'],
['GET /api/console', 'Try PRD creation', 'GET /api/help/prds/examples']
],
drop_off_points: [
{ endpoint: '/api/prds', reason: 'validation_errors' }
]
};
}
}
```
### 2. νΌλλ°± μμ§ μμ€ν
#### μ¬μ©μ κ²½ν νΌλλ°±
```json
// POST /api/feedback
{
"type": "usability",
"rating": 4,
"category": "api_discovery",
"feedback": "Help system is very useful, but could use more examples",
"context": {
"user_journey": ["GET /api", "GET /api/help/prds", "POST /api/prds"],
"session_duration": 1200,
"successful_operations": 3,
"errors_encountered": 1
},
"suggestions": [
"More real-world examples in help docs",
"Interactive tutorials"
]
}
```
## π― μ±κ³΅ μ§ν
### κ°λ°μ κ²½ν KPI
- **Time to First Success**: μ κ· μ¬μ©μκ° μ²« API νΈμΆ μ±κ³΅κΉμ§ μκ° (λͺ©ν: 5λΆ μ΄λ΄)
- **Help System Usage**: λμλ§ μμ€ν
μ¬μ©λ₯ (λͺ©ν: μ κ· μ¬μ©μ 80% μ΄μ)
- **Error Recovery Rate**: μλ¬ λ°μ ν μ±κ³΅μ ν΄κ²°λ₯ (λͺ©ν: 90% μ΄μ)
- **API Discovery Success**: `/api` μ§μ
ν μνλ κΈ°λ₯ μ°ΎκΈ° μ±κ³΅λ₯ (λͺ©ν: 95% μ΄μ)
### νΈνμ± μ§ν
- **Legacy Compatibility**: κΈ°μ‘΄ μμ€ν
νΈνμ± (λͺ©ν: 100%)
- **Zero Breaking Changes**: κΈ°μ‘΄ μ¬μ©μ μν₯λ (λͺ©ν: 0%)
- **Performance Impact**: κΈ°μ‘΄ μμ€ν
μ±λ₯ μν₯ (λͺ©ν: <5% μ¦κ°)
---
**μμ±μΌ**: 2025-09-11
**μμ±μ**: UX μ€κ³μ (Claude Code)
**κ²ν μ**: νλ‘ νΈμλ κ°λ°μ, κΈ°μ‘΄ μμ€ν
κ΄λ¦¬μ
**λ²μ **: 1.0
**μν**: κΈ°μ μ€ν λ¬Έμ λκΈ°