From 1770c2ed231edb841bb4dca81c7f705cd1b58882 Mon Sep 17 00:00:00 2001 From: hskwon Date: Thu, 18 Dec 2025 20:29:09 +0900 Subject: [PATCH] =?UTF-8?q?API=20Explorer=20=EA=B8=B0=EB=8A=A5=20=EC=8A=A4?= =?UTF-8?q?=ED=8E=99=20=EB=B0=8F=20=EA=B0=9C=EB=B0=9C=20=EA=B3=84=ED=9A=8D?= =?UTF-8?q?=20=EB=AC=B8=EC=84=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - features/api-explorer-spec.md: API Explorer 기능 스펙 - plans/api-explorer-development-plan.md: API Explorer 개발 계획 --- features/api-explorer-spec.md | 650 +++++++++++++++++++++++ plans/api-explorer-development-plan.md | 688 +++++++++++++++++++++++++ 2 files changed, 1338 insertions(+) create mode 100644 features/api-explorer-spec.md create mode 100644 plans/api-explorer-development-plan.md diff --git a/features/api-explorer-spec.md b/features/api-explorer-spec.md new file mode 100644 index 0000000..93e50ac --- /dev/null +++ b/features/api-explorer-spec.md @@ -0,0 +1,650 @@ +# API Explorer 상세 설계서 + +> **문서 버전**: 1.0 +> **작성일**: 2025-12-17 +> **대상 프로젝트**: mng (Plain Laravel + Blade + Tailwind) + +--- + +## 1. 개요 + +### 1.1 목적 +Swagger UI의 한계를 극복하고, 개발팀의 API 관리 효율성을 높이기 위한 커스텀 API Explorer 개발 + +### 1.2 Swagger 대비 개선점 + +| 기능 | Swagger UI | API Explorer | +|------|------------|--------------| +| 검색 | 엔드포인트명만 | 풀텍스트 (설명, 파라미터 포함) | +| 그룹핑 | 태그만 | 태그 + 상태 + 메서드 + 커스텀 | +| 즐겨찾기 | ❌ | ⭐ 사용자별 북마크 | +| 요청 템플릿 | ❌ | 💾 저장/공유 가능 | +| 히스토리 | ❌ | 📋 최근 요청 + 재실행 | +| 환경 전환 | 수동 | 🔄 원클릭 전환 | + +### 1.3 기술 스택 +- **Backend**: Laravel 12 (mng 프로젝트) +- **Frontend**: Blade + Tailwind CSS + HTMX +- **Data Source**: OpenAPI 3.0 JSON (`api/storage/api-docs/api-docs.json`) +- **HTTP Client**: Guzzle (서버사이드 프록시) + +--- + +## 2. 시스템 아키텍처 + +### 2.1 전체 구조 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ API Explorer (mng) │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ +│ │ Browser │───>│ Laravel │───>│ API Server │ │ +│ │ (HTMX) │<───│ (Proxy) │<───│ (api/) │ │ +│ └──────────────┘ └──────────────┘ └──────────────────┘ │ +│ │ │ │ +│ │ ┌──────┴──────┐ │ +│ │ │ │ │ +│ │ ┌─────┴─────┐ ┌─────┴─────┐ │ +│ │ │ SQLite │ │ OpenAPI │ │ +│ │ │ (Local) │ │ JSON │ │ +│ │ └───────────┘ └───────────┘ │ +│ │ │ +│ ┌──────┴───────────────────────────────────────────────────┐ │ +│ │ Local Storage │ │ +│ │ • 환경 설정 (현재 서버) │ │ +│ │ • UI 상태 (패널 크기, 필터) │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 2.2 데이터 흐름 + +``` +1. OpenAPI 파싱 + api-docs.json ──> OpenApiParserService ──> 구조화된 API 데이터 + +2. API 요청 프록시 + Browser ──HTMX──> ApiExplorerController ──Guzzle──> API Server + +3. 사용자 데이터 (즐겨찾기, 템플릿, 히스토리) + Browser ──> ApiExplorerController ──> SQLite/MySQL +``` + +--- + +## 3. 디렉토리 구조 + +``` +mng/ +├── app/ +│ ├── Http/ +│ │ └── Controllers/ +│ │ └── DevTools/ +│ │ └── ApiExplorerController.php +│ ├── Services/ +│ │ └── ApiExplorer/ +│ │ ├── OpenApiParserService.php # OpenAPI JSON 파싱 +│ │ ├── ApiRequestService.php # API 호출 프록시 +│ │ └── ApiExplorerService.php # 비즈니스 로직 통합 +│ └── Models/ +│ └── DevTools/ +│ ├── ApiBookmark.php # 즐겨찾기 +│ ├── ApiTemplate.php # 요청 템플릿 +│ └── ApiHistory.php # 요청 히스토리 +│ +├── database/ +│ └── migrations/ +│ └── 2024_xx_xx_create_api_explorer_tables.php +│ +├── resources/ +│ └── views/ +│ └── dev-tools/ +│ └── api-explorer/ +│ ├── index.blade.php # 메인 레이아웃 +│ ├── partials/ +│ │ ├── sidebar.blade.php # API 목록 + 검색/필터 +│ │ ├── endpoint-item.blade.php # 개별 엔드포인트 항목 +│ │ ├── request-panel.blade.php # 요청 작성 패널 +│ │ ├── response-panel.blade.php # 응답 표시 패널 +│ │ ├── template-modal.blade.php # 템플릿 저장/불러오기 +│ │ └── history-drawer.blade.php # 히스토리 서랍 +│ └── components/ +│ ├── method-badge.blade.php # HTTP 메서드 배지 +│ ├── param-input.blade.php # 파라미터 입력 필드 +│ ├── json-editor.blade.php # JSON 편집기 +│ └── json-viewer.blade.php # JSON 뷰어 (트리/Raw) +│ +├── routes/ +│ └── web.php # 라우트 추가 +│ +└── config/ + └── api-explorer.php # 설정 파일 +``` + +--- + +## 4. 데이터베이스 스키마 + +### 4.1 ERD + +``` +┌─────────────────────┐ ┌─────────────────────┐ +│ api_bookmarks │ │ api_templates │ +├─────────────────────┤ ├─────────────────────┤ +│ id (PK) │ │ id (PK) │ +│ user_id (FK) │ │ user_id (FK) │ +│ endpoint │ │ endpoint │ +│ method │ │ method │ +│ display_name │ │ name │ +│ display_order │ │ description │ +│ color │ │ headers (JSON) │ +│ created_at │ │ path_params (JSON) │ +│ updated_at │ │ query_params (JSON) │ +└─────────────────────┘ │ body (JSON) │ + │ is_shared │ + │ created_at │ + │ updated_at │ + └─────────────────────┘ + +┌─────────────────────┐ ┌─────────────────────┐ +│ api_histories │ │ api_environments │ +├─────────────────────┤ ├─────────────────────┤ +│ id (PK) │ │ id (PK) │ +│ user_id (FK) │ │ user_id (FK) │ +│ endpoint │ │ name │ +│ method │ │ base_url │ +│ request_headers │ │ api_key │ +│ request_body │ │ auth_token │ +│ response_status │ │ variables (JSON) │ +│ response_headers │ │ is_default │ +│ response_body │ │ created_at │ +│ duration_ms │ │ updated_at │ +│ environment │ └─────────────────────┘ +│ created_at │ +└─────────────────────┘ +``` + +### 4.2 마이그레이션 + +```php +// api_bookmarks +Schema::create('api_bookmarks', function (Blueprint $table) { + $table->id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->string('endpoint', 500); + $table->string('method', 10); + $table->string('display_name', 100)->nullable(); + $table->integer('display_order')->default(0); + $table->string('color', 20)->nullable(); + $table->timestamps(); + + $table->unique(['user_id', 'endpoint', 'method']); + $table->index('user_id'); +}); + +// api_templates +Schema::create('api_templates', function (Blueprint $table) { + $table->id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->string('endpoint', 500); + $table->string('method', 10); + $table->string('name', 100); + $table->text('description')->nullable(); + $table->json('headers')->nullable(); + $table->json('path_params')->nullable(); + $table->json('query_params')->nullable(); + $table->json('body')->nullable(); + $table->boolean('is_shared')->default(false); + $table->timestamps(); + + $table->index(['user_id', 'endpoint', 'method']); + $table->index('is_shared'); +}); + +// api_histories +Schema::create('api_histories', function (Blueprint $table) { + $table->id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->string('endpoint', 500); + $table->string('method', 10); + $table->json('request_headers')->nullable(); + $table->json('request_body')->nullable(); + $table->integer('response_status'); + $table->json('response_headers')->nullable(); + $table->longText('response_body')->nullable(); + $table->integer('duration_ms'); + $table->string('environment', 50); + $table->timestamp('created_at'); + + $table->index(['user_id', 'created_at']); + $table->index(['endpoint', 'method']); +}); + +// api_environments +Schema::create('api_environments', function (Blueprint $table) { + $table->id(); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->string('name', 50); + $table->string('base_url', 500); + $table->string('api_key', 500)->nullable(); + $table->text('auth_token')->nullable(); + $table->json('variables')->nullable(); + $table->boolean('is_default')->default(false); + $table->timestamps(); + + $table->index('user_id'); +}); +``` + +--- + +## 5. UI 설계 + +### 5.1 메인 레이아웃 (3-Panel) + +``` +┌────────────────────────────────────────────────────────────────────────────┐ +│ 🔍 Search... │ [로컬 ▼] │ 📋 History │ ⚙️ Settings │ +├────────────────────────────────────────────────────────────────────────────┤ +│ │ │ │ +│ API Sidebar │ Request Panel │ Response Panel │ +│ (resizable) │ (resizable) │ (resizable) │ +│ │ │ │ +│ ┌───────────────────┐ │ ┌──────────────────────┐ │ ┌──────────────────────┐ │ +│ │ 🔍 필터 │ │ │ POST /api/v1/login │ │ │ Status: 200 OK ✓ │ │ +│ │ [GET][POST][PUT] │ │ │ │ │ │ Time: 45ms │ │ +│ │ [DELETE][PATCH] │ │ │ ┌─ Headers ─────────┐│ │ │ │ │ +│ │ │ │ │ │ Authorization: [] ││ │ │ ┌─ Headers ─────────┐│ │ +│ │ ⭐ 즐겨찾기 (3) │ │ │ │ Content-Type: [] ││ │ │ │ content-type: ... ││ │ +│ │ POST login │ │ │ └──────────────────┘│ │ │ │ x-request-id: ... ││ │ +│ │ GET users │ │ │ │ │ │ └──────────────────┘│ │ +│ │ POST logout │ │ │ ┌─ Path Params ────┐│ │ │ │ │ +│ │ │ │ │ │ (none) ││ │ │ ┌─ Body ─────────────┐│ │ +│ │ 📁 Auth │ │ │ └──────────────────┘│ │ │ │ { ││ │ +│ │ ├ POST login │ │ │ │ │ │ │ "success": true, ││ │ +│ │ ├ POST logout │ │ │ ┌─ Query Params ───┐│ │ │ │ "data": { ││ │ +│ │ └ GET me │ │ │ │ (none) ││ │ │ │ "token": "..." ││ │ +│ │ │ │ │ └──────────────────┘│ │ │ │ } ││ │ +│ │ 📁 Users │ │ │ │ │ │ │ } ││ │ +│ │ ├ GET list │ │ │ ┌─ Body (JSON) ────┐│ │ │ └──────────────────┘│ │ +│ │ ├ GET {id} │ │ │ │ { ││ │ │ │ │ +│ │ ├ POST create │ │ │ │ "user_id": "", ││ │ │ [Raw] [Pretty] [Tree]│ │ +│ │ ├ PUT {id} │ │ │ │ "user_pwd": "" ││ │ │ │ │ +│ │ └ DELETE {id} │ │ │ │ } ││ │ │ [📋 Copy] [💾 Save] │ │ +│ │ │ │ │ └──────────────────┘│ │ │ │ │ +│ │ 📁 Products │ │ │ │ │ └──────────────────────┘ │ +│ │ └ ... │ │ │ [📋 템플릿] [▶ 실행]│ │ │ +│ └───────────────────┘ │ └──────────────────────┘ │ │ +│ │ │ │ +└────────────────────────────────────────────────────────────────────────────┘ +``` + +### 5.2 컬러 스킴 + +```css +/* HTTP 메서드 배지 */ +.method-get { @apply bg-green-100 text-green-800; } +.method-post { @apply bg-blue-100 text-blue-800; } +.method-put { @apply bg-yellow-100 text-yellow-800; } +.method-patch { @apply bg-orange-100 text-orange-800; } +.method-delete { @apply bg-red-100 text-red-800; } + +/* 상태 코드 */ +.status-2xx { @apply text-green-600; } /* 성공 */ +.status-3xx { @apply text-blue-600; } /* 리다이렉트 */ +.status-4xx { @apply text-yellow-600; } /* 클라이언트 에러 */ +.status-5xx { @apply text-red-600; } /* 서버 에러 */ +``` + +### 5.3 반응형 동작 + +| 화면 크기 | 동작 | +|-----------|------| +| Desktop (≥1280px) | 3-Panel 표시 | +| Tablet (768-1279px) | 2-Panel (사이드바 접힘 가능) | +| Mobile (<768px) | 1-Panel (탭 전환) | + +--- + +## 6. API 설계 + +### 6.1 라우트 정의 + +```php +// routes/web.php +Route::prefix('dev-tools/api-explorer') + ->middleware(['auth']) + ->name('api-explorer.') + ->group(function () { + // 메인 페이지 + Route::get('/', [ApiExplorerController::class, 'index'])->name('index'); + + // API 목록 (HTMX partial) + Route::get('/endpoints', [ApiExplorerController::class, 'endpoints'])->name('endpoints'); + Route::get('/endpoints/{operationId}', [ApiExplorerController::class, 'endpoint'])->name('endpoint'); + + // API 실행 (프록시) + Route::post('/execute', [ApiExplorerController::class, 'execute'])->name('execute'); + + // 즐겨찾기 + Route::get('/bookmarks', [ApiExplorerController::class, 'bookmarks'])->name('bookmarks'); + Route::post('/bookmarks', [ApiExplorerController::class, 'addBookmark'])->name('bookmarks.add'); + Route::delete('/bookmarks/{id}', [ApiExplorerController::class, 'removeBookmark'])->name('bookmarks.remove'); + Route::put('/bookmarks/reorder', [ApiExplorerController::class, 'reorderBookmarks'])->name('bookmarks.reorder'); + + // 템플릿 + Route::get('/templates', [ApiExplorerController::class, 'templates'])->name('templates'); + Route::get('/templates/{endpoint}', [ApiExplorerController::class, 'templatesForEndpoint'])->name('templates.endpoint'); + Route::post('/templates', [ApiExplorerController::class, 'saveTemplate'])->name('templates.save'); + Route::delete('/templates/{id}', [ApiExplorerController::class, 'deleteTemplate'])->name('templates.delete'); + + // 히스토리 + Route::get('/history', [ApiExplorerController::class, 'history'])->name('history'); + Route::delete('/history', [ApiExplorerController::class, 'clearHistory'])->name('history.clear'); + Route::post('/history/{id}/replay', [ApiExplorerController::class, 'replayHistory'])->name('history.replay'); + + // 환경 + Route::get('/environments', [ApiExplorerController::class, 'environments'])->name('environments'); + Route::post('/environments', [ApiExplorerController::class, 'saveEnvironment'])->name('environments.save'); + Route::delete('/environments/{id}', [ApiExplorerController::class, 'deleteEnvironment'])->name('environments.delete'); + Route::post('/environments/{id}/default', [ApiExplorerController::class, 'setDefaultEnvironment'])->name('environments.default'); + }); +``` + +### 6.2 Controller 메서드 시그니처 + +```php +class ApiExplorerController extends Controller +{ + public function __construct( + private OpenApiParserService $parser, + private ApiRequestService $requester, + private ApiExplorerService $explorer + ) {} + + // GET /dev-tools/api-explorer + public function index(): View + + // GET /dev-tools/api-explorer/endpoints?search=&tags[]=&methods[]= + public function endpoints(Request $request): View // HTMX partial + + // GET /dev-tools/api-explorer/endpoints/{operationId} + public function endpoint(string $operationId): View // HTMX partial + + // POST /dev-tools/api-explorer/execute + public function execute(ExecuteApiRequest $request): JsonResponse + + // Bookmarks CRUD... + // Templates CRUD... + // History CRUD... + // Environments CRUD... +} +``` + +### 6.3 Service 클래스 + +```php +// OpenApiParserService - OpenAPI JSON 파싱 +class OpenApiParserService +{ + public function parse(): array; // 전체 스펙 파싱 + public function getEndpoints(): Collection; // 엔드포인트 목록 + public function getEndpoint(string $operationId): ?array; // 단일 엔드포인트 + public function getTags(): array; // 태그 목록 + public function search(string $query): Collection; // 검색 + public function filter(array $filters): Collection; // 필터링 +} + +// ApiRequestService - API 호출 프록시 +class ApiRequestService +{ + public function execute( + string $method, + string $url, + array $headers = [], + array $query = [], + ?array $body = null + ): ApiResponse; +} + +// ApiExplorerService - 비즈니스 로직 통합 +class ApiExplorerService +{ + // Bookmark operations + public function getBookmarks(int $userId): Collection; + public function addBookmark(int $userId, array $data): ApiBookmark; + public function removeBookmark(int $bookmarkId): void; + + // Template operations + public function getTemplates(int $userId, ?string $endpoint = null): Collection; + public function saveTemplate(int $userId, array $data): ApiTemplate; + public function deleteTemplate(int $templateId): void; + + // History operations + public function logRequest(int $userId, array $data): ApiHistory; + public function getHistory(int $userId, int $limit = 50): Collection; + public function clearHistory(int $userId): void; + + // Environment operations + public function getEnvironments(int $userId): Collection; + public function saveEnvironment(int $userId, array $data): ApiEnvironment; +} +``` + +--- + +## 7. 핵심 기능 상세 + +### 7.1 스마트 검색 + +```php +// 검색 대상 필드 +$searchFields = [ + 'endpoint', // /api/v1/users + 'summary', // "사용자 목록 조회" + 'description', // 상세 설명 + 'operationId', // getUserList + 'parameters.*.name', // 파라미터명 + 'parameters.*.description', // 파라미터 설명 + 'tags', // 태그 +]; + +// 검색 알고리즘 +1. 정확히 일치 → 최상위 +2. 시작 부분 일치 → 높은 순위 +3. 포함 → 일반 순위 +4. Fuzzy 매칭 → 낮은 순위 (선택적) +``` + +### 7.2 필터링 옵션 + +```php +$filters = [ + 'methods' => ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], + 'tags' => ['Auth', 'Users', 'Products', ...], + 'status' => ['stable', 'beta', 'deprecated'], + 'hasBody' => true|false, + 'requiresAuth' => true|false, +]; +``` + +### 7.3 요청 템플릿 시스템 + +```json +// 템플릿 저장 형식 +{ + "name": "로그인 테스트", + "description": "테스트 계정으로 로그인", + "endpoint": "/api/v1/login", + "method": "POST", + "headers": { + "X-API-KEY": "{{API_KEY}}" + }, + "body": { + "user_id": "test", + "user_pwd": "testpass" + }, + "is_shared": false +} +``` + +### 7.4 환경 변수 시스템 + +```json +// 환경 설정 형식 +{ + "name": "로컬", + "base_url": "http://api.sam.kr", + "api_key": "your-api-key", + "auth_token": null, + "variables": { + "TENANT_ID": "1", + "USER_ID": "test" + } +} + +// 변수 치환: {{VARIABLE_NAME}} +// 예: "Authorization": "Bearer {{AUTH_TOKEN}}" +``` + +--- + +## 8. HTMX 통합 + +### 8.1 주요 HTMX 패턴 + +```html + +
+
+ + + + + + + + +
+
+ + + +``` + +### 8.2 OOB (Out-of-Band) 업데이트 + +```html + +
+ {{ $historyCount }} +
+``` + +--- + +## 9. 보안 고려사항 + +### 9.1 접근 제어 +- mng 프로젝트 로그인 필수 (`auth` 미들웨어) +- 개발 환경에서만 접근 가능 (선택적) +- API Key/Token은 서버사이드에서만 관리 + +### 9.2 민감 정보 처리 +- 환경 설정의 API Key는 암호화 저장 +- 히스토리에서 민감 헤더 마스킹 옵션 +- 공유 템플릿에서 인증 정보 자동 제외 + +### 9.3 프록시 보안 +- 허용된 base_url만 프록시 가능 (화이트리스트) +- 요청 크기 제한 (body 최대 1MB) +- 타임아웃 설정 (30초) + +--- + +## 10. 구현 로드맵 + +### Phase 1: 기본 구조 (3-4일) +- [ ] 디렉토리 구조 생성 +- [ ] 마이그레이션 파일 작성 +- [ ] OpenApiParserService 구현 +- [ ] 기본 UI 레이아웃 (3-Panel) +- [ ] 엔드포인트 목록 표시 + +### Phase 2: 검색/필터/요청 (3일) +- [ ] 풀텍스트 검색 구현 +- [ ] 메서드/태그 필터링 +- [ ] 요청 패널 UI +- [ ] ApiRequestService (프록시) +- [ ] 응답 표시 (JSON Viewer) + +### Phase 3: 사용자 데이터 (3일) +- [ ] 즐겨찾기 CRUD +- [ ] 템플릿 저장/불러오기 +- [ ] 히스토리 기록/재실행 +- [ ] 드래그&드롭 정렬 + +### Phase 4: 환경/고급 기능 (2-3일) +- [ ] 환경 관리 UI +- [ ] 변수 치환 시스템 +- [ ] 키보드 단축키 +- [ ] UI 폴리싱 +- [ ] 반응형 최적화 + +### Phase 5: 테스트/배포 (2일) +- [ ] 기능 테스트 +- [ ] 성능 최적화 +- [ ] 문서화 +- [ ] 배포 + +**예상 총 기간: 13-15일** + +--- + +## 11. 향후 확장 가능성 + +### 11.1 추가 기능 후보 +- **Mock 서버**: 테스트용 가짜 응답 생성 +- **API 비교**: 두 환경 간 응답 비교 +- **자동 테스트**: 저장된 템플릿 일괄 실행 +- **변경 감지**: OpenAPI 스펙 변경 알림 +- **문서 생성**: Markdown/PDF 문서 자동 생성 + +### 11.2 통합 가능성 +- **CI/CD**: API 테스트 자동화 +- **Slack/Teams**: 알림 연동 +- **Postman**: 컬렉션 import/export + +--- + +## 12. 참고 자료 + +### 12.1 유사 도구 +- [Scalar](https://scalar.com/) - 현대적 API 문서 +- [Stoplight](https://stoplight.io/) - API 설계 도구 +- [Insomnia](https://insomnia.rest/) - API 클라이언트 + +### 12.2 기술 문서 +- [OpenAPI 3.0 Specification](https://spec.openapis.org/oas/v3.0.3) +- [HTMX Documentation](https://htmx.org/docs/) +- [Laravel HTTP Client](https://laravel.com/docs/http-client) diff --git a/plans/api-explorer-development-plan.md b/plans/api-explorer-development-plan.md new file mode 100644 index 0000000..d16f142 --- /dev/null +++ b/plans/api-explorer-development-plan.md @@ -0,0 +1,688 @@ +# API Explorer 개발 작업 계획 + +> **작성일**: 2025-12-17 +> **기준 문서**: API Explorer 상세 설계서 (api-explorer-spec.md) +> **상태**: 🟡 계획 수립 완료 + +--- + +## 📚 참고 문서 + +### 핵심 참고 문서 +| 문서 | 경로 | 참조 섹션 | +|------|------|----------| +| **상세 설계서** | [`docs/features/api-explorer-spec.md`](../features/api-explorer-spec.md) | UI 와이어프레임(§5), HTMX 패턴(§8), 보안(§9) | +| **OpenAPI 스펙** | `api/storage/api-docs/api-docs.json` | 파싱 대상 JSON | + +### 개발 표준 문서 +| 문서 | 경로 | 용도 | +|------|------|------| +| API 개발 규칙 | [`docs/standards/api-rules.md`](../standards/api-rules.md) | Service-First, FormRequest | +| 품질 체크리스트 | [`docs/standards/quality-checklist.md`](../standards/quality-checklist.md) | 코드 품질 검증 | + +### 기존 코드 참조 +| 항목 | 경로 | 용도 | +|------|------|------| +| mng 레이아웃 | `mng/resources/views/layouts/app.blade.php` | 기존 UI 패턴 | +| mng dev-tools | `mng/resources/views/dev-tools/` | 기존 개발 도구 패턴 | +| mng 라우트 | `mng/routes/web.php` | 라우트 패턴 | + +### 기술 스택 참조 +| 기술 | 문서 | 용도 | +|------|------|------| +| HTMX | [htmx.org/docs](https://htmx.org/docs/) | 동적 UI 업데이트 | +| Tailwind CSS | [tailwindcss.com](https://tailwindcss.com/docs) | 스타일링 | +| Laravel HTTP | [laravel.com/docs/http-client](https://laravel.com/docs/http-client) | API 프록시 | + +--- + +## 🗄️ 핵심 스키마 (인라인) + +### DB 테이블 구조 + +```sql +-- api_bookmarks: 즐겨찾기 +CREATE TABLE api_bookmarks ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + endpoint VARCHAR(500) NOT NULL, + method VARCHAR(10) NOT NULL, + display_name VARCHAR(100), + display_order INT DEFAULT 0, + color VARCHAR(20), + created_at TIMESTAMP, + updated_at TIMESTAMP, + UNIQUE KEY (user_id, endpoint, method), + INDEX (user_id) +); + +-- api_templates: 요청 템플릿 +CREATE TABLE api_templates ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + endpoint VARCHAR(500) NOT NULL, + method VARCHAR(10) NOT NULL, + name VARCHAR(100) NOT NULL, + description TEXT, + headers JSON, + path_params JSON, + query_params JSON, + body JSON, + is_shared BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP, + updated_at TIMESTAMP, + INDEX (user_id, endpoint, method), + INDEX (is_shared) +); + +-- api_histories: 요청 히스토리 +CREATE TABLE api_histories ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + endpoint VARCHAR(500) NOT NULL, + method VARCHAR(10) NOT NULL, + request_headers JSON, + request_body JSON, + response_status INT NOT NULL, + response_headers JSON, + response_body LONGTEXT, + duration_ms INT NOT NULL, + environment VARCHAR(50) NOT NULL, + created_at TIMESTAMP, + INDEX (user_id, created_at), + INDEX (endpoint, method) +); + +-- api_environments: 환경 설정 +CREATE TABLE api_environments ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + name VARCHAR(50) NOT NULL, + base_url VARCHAR(500) NOT NULL, + api_key VARCHAR(500), -- 암호화 저장 + auth_token TEXT, -- 암호화 저장 + variables JSON, + is_default BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP, + updated_at TIMESTAMP, + INDEX (user_id) +); +``` + +### 라우트 정의 + +```php +// routes/web.php - API Explorer 라우트 그룹 +Route::prefix('dev-tools/api-explorer') + ->middleware(['auth']) + ->name('api-explorer.') + ->group(function () { + // 메인 + Route::get('/', 'index')->name('index'); + + // 엔드포인트 (HTMX partial) + Route::get('/endpoints', 'endpoints')->name('endpoints'); + Route::get('/endpoints/{operationId}', 'endpoint')->name('endpoint'); + + // API 실행 + Route::post('/execute', 'execute')->name('execute'); + + // 즐겨찾기 + Route::get('/bookmarks', 'bookmarks')->name('bookmarks'); + Route::post('/bookmarks', 'addBookmark')->name('bookmarks.add'); + Route::delete('/bookmarks/{id}', 'removeBookmark')->name('bookmarks.remove'); + Route::put('/bookmarks/reorder', 'reorderBookmarks')->name('bookmarks.reorder'); + + // 템플릿 + Route::get('/templates', 'templates')->name('templates'); + Route::get('/templates/{endpoint}', 'templatesForEndpoint')->name('templates.endpoint'); + Route::post('/templates', 'saveTemplate')->name('templates.save'); + Route::delete('/templates/{id}', 'deleteTemplate')->name('templates.delete'); + + // 히스토리 + Route::get('/history', 'history')->name('history'); + Route::delete('/history', 'clearHistory')->name('history.clear'); + Route::post('/history/{id}/replay', 'replayHistory')->name('history.replay'); + + // 환경 + Route::get('/environments', 'environments')->name('environments'); + Route::post('/environments', 'saveEnvironment')->name('environments.save'); + Route::delete('/environments/{id}', 'deleteEnvironment')->name('environments.delete'); + Route::post('/environments/{id}/default', 'setDefaultEnvironment')->name('environments.default'); + }); +``` + +### Config 파일 + +```php +// config/api-explorer.php +return [ + // OpenAPI 스펙 파일 경로 + 'openapi_path' => env('API_EXPLORER_OPENAPI_PATH', base_path('../api/storage/api-docs/api-docs.json')), + + // 기본 환경 설정 + 'default_environments' => [ + [ + 'name' => '로컬', + 'base_url' => 'http://api.sam.kr', + 'api_key' => env('API_EXPLORER_LOCAL_KEY', ''), + ], + [ + 'name' => '개발', + 'base_url' => 'https://api.codebridge-x.com', + 'api_key' => env('API_EXPLORER_DEV_KEY', ''), + ], + ], + + // 프록시 설정 + 'proxy' => [ + 'timeout' => 30, // 초 + 'max_body_size' => 1024 * 1024, // 1MB + 'allowed_hosts' => [ // 화이트리스트 + 'api.sam.kr', + 'api.codebridge-x.com', + 'localhost', + ], + ], + + // 히스토리 설정 + 'history' => [ + 'max_entries' => 100, // 사용자당 최대 + 'retention_days' => 30, // 보관 기간 + ], + + // 보안 설정 + 'security' => [ + 'encrypt_tokens' => true, // API Key/Token 암호화 + 'mask_sensitive_headers' => [ // 히스토리에서 마스킹 + 'Authorization', + 'X-API-KEY', + 'Cookie', + ], + ], +]; +``` + +### Service 메서드 시그니처 + +```php +// OpenApiParserService - OpenAPI JSON 파싱 +class OpenApiParserService +{ + public function parse(): array; // 전체 스펙 파싱 + public function getEndpoints(): Collection; // 엔드포인트 목록 + public function getEndpoint(string $operationId): ?array; // 단일 엔드포인트 + public function getTags(): array; // 태그 목록 + public function search(string $query): Collection; // 검색 + public function filter(array $filters): Collection; // 필터링 +} + +// ApiRequestService - API 호출 프록시 +class ApiRequestService +{ + public function execute( + string $method, + string $url, + array $headers = [], + array $query = [], + ?array $body = null + ): array; // ['status', 'headers', 'body', 'duration_ms'] +} + +// ApiExplorerService - CRUD 통합 +class ApiExplorerService +{ + // Bookmarks + public function getBookmarks(int $userId): Collection; + public function addBookmark(int $userId, array $data): ApiBookmark; + public function removeBookmark(int $bookmarkId): void; + public function reorderBookmarks(int $userId, array $order): void; + + // Templates + public function getTemplates(int $userId, ?string $endpoint = null): Collection; + public function saveTemplate(int $userId, array $data): ApiTemplate; + public function deleteTemplate(int $templateId): void; + + // History + public function logRequest(int $userId, array $data): ApiHistory; + public function getHistory(int $userId, int $limit = 50): Collection; + public function clearHistory(int $userId): void; + + // Environments + public function getEnvironments(int $userId): Collection; + public function saveEnvironment(int $userId, array $data): ApiEnvironment; + public function deleteEnvironment(int $environmentId): void; + public function setDefaultEnvironment(int $userId, int $environmentId): void; +} +``` + +--- + +## 📊 개발 범위 요약 + +| 구분 | 항목 | 예상 기간 | 상태 | +|------|------|----------|------| +| Phase 1 | 기본 구조 + OpenAPI 파싱 | 3-4일 | ⬜ 대기 | +| Phase 2 | 검색/필터 + 요청/응답 | 3일 | ⬜ 대기 | +| Phase 3 | 즐겨찾기/템플릿/히스토리 | 3일 | ⬜ 대기 | +| Phase 4 | 환경 관리 + 고급 기능 | 2-3일 | ⬜ 대기 | +| Phase 5 | 테스트 + 폴리싱 | 2일 | ⬜ 대기 | +| **합계** | | **13-15일** | | + +--- + +## 🚀 Phase 1: 기본 구조 + OpenAPI 파싱 (예상 3-4일) + +> 📖 **설계서 참조**: 디렉토리 구조(§3), 아키텍처(§2) + +### 1.1 디렉토리 구조 생성 + +- [ ] **Controller 생성** + - [ ] `mng/app/Http/Controllers/DevTools/ApiExplorerController.php` + +- [ ] **Service 생성** + - [ ] `mng/app/Services/ApiExplorer/OpenApiParserService.php` + - [ ] `mng/app/Services/ApiExplorer/ApiRequestService.php` + - [ ] `mng/app/Services/ApiExplorer/ApiExplorerService.php` + +- [ ] **Model 생성** + - [ ] `mng/app/Models/DevTools/ApiBookmark.php` + - [ ] `mng/app/Models/DevTools/ApiTemplate.php` + - [ ] `mng/app/Models/DevTools/ApiHistory.php` + - [ ] `mng/app/Models/DevTools/ApiEnvironment.php` + +- [ ] **Config 생성** + - [ ] `mng/config/api-explorer.php` (설정 파일) + +### 1.2 데이터베이스 마이그레이션 + +- [ ] **마이그레이션 파일 생성** + - [ ] `create_api_bookmarks_table.php` + - [ ] `create_api_templates_table.php` + - [ ] `create_api_histories_table.php` + - [ ] `create_api_environments_table.php` + +- [ ] **마이그레이션 실행** + - [ ] `php artisan migrate` 실행 및 검증 + +### 1.3 OpenAPI 파서 구현 + +- [ ] **OpenApiParserService 구현** + - [ ] `parse()` - 전체 스펙 파싱 + - [ ] `getEndpoints()` - 엔드포인트 목록 추출 + - [ ] `getEndpoint($operationId)` - 단일 엔드포인트 조회 + - [ ] `getTags()` - 태그 목록 추출 + - [ ] 캐싱 로직 (파일 변경 감지) + +- [ ] **테스트** + - [ ] api-docs.json 파싱 테스트 + - [ ] 엔드포인트 추출 검증 + +### 1.4 기본 UI 레이아웃 + +- [ ] **View 파일 생성** + - [ ] `mng/resources/views/dev-tools/api-explorer/index.blade.php` + - [ ] `mng/resources/views/dev-tools/api-explorer/partials/sidebar.blade.php` + - [ ] `mng/resources/views/dev-tools/api-explorer/partials/request-panel.blade.php` + - [ ] `mng/resources/views/dev-tools/api-explorer/partials/response-panel.blade.php` + +- [ ] **컴포넌트 생성** + - [ ] `components/method-badge.blade.php` (HTTP 메서드 배지) + - [ ] `components/endpoint-item.blade.php` (엔드포인트 목록 항목) + +- [ ] **3-Panel 레이아웃 구현** + - [ ] 사이드바 (API 목록) + - [ ] 요청 패널 + - [ ] 응답 패널 + - [ ] 리사이즈 가능 패널 + +- [ ] **라우트 설정** + - [ ] `routes/web.php`에 api-explorer 라우트 그룹 추가 + - [ ] 메뉴에 API Explorer 링크 추가 + +### 1.5 Phase 1 완료 검증 + +- [ ] 기본 페이지 접근 가능 +- [ ] OpenAPI 스펙에서 엔드포인트 목록 표시 +- [ ] 태그별 그룹핑 동작 +- [ ] 마이그레이션 정상 실행 + +--- + +## 🔍 Phase 2: 검색/필터 + 요청/응답 (예상 3일) + +> 📖 **설계서 참조**: 검색 알고리즘(§7.1), 필터링(§7.2), HTMX 패턴(§8.1) + +### 2.1 검색 기능 + +- [ ] **풀텍스트 검색 구현** + - [ ] 엔드포인트 URL 검색 + - [ ] summary/description 검색 + - [ ] 파라미터명 검색 + - [ ] operationId 검색 + +- [ ] **HTMX 연동** + - [ ] 입력 지연 (debounce 300ms) + - [ ] 부분 업데이트 (sidebar만 갱신) + +### 2.2 필터 기능 + +- [ ] **필터 UI 구현** + - [ ] HTTP 메서드 필터 (GET/POST/PUT/DELETE/PATCH) + - [ ] 태그 필터 (다중 선택) + - [ ] 필터 초기화 버튼 + +- [ ] **필터 로직** + - [ ] `filter(array $filters)` 메서드 구현 + - [ ] 복합 필터 (AND 조건) + +### 2.3 요청 패널 + +- [ ] **View 구현** + - [ ] `partials/request-panel.blade.php` 완성 + +- [ ] **입력 폼 구현** + - [ ] Headers 입력 (key-value) + - [ ] Path Parameters 입력 + - [ ] Query Parameters 입력 + - [ ] Body (JSON) 입력 + +- [ ] **JSON 에디터** + - [ ] `components/json-editor.blade.php` + - [ ] 기본 textarea + syntax highlight (선택적) + - [ ] JSON 유효성 검사 + +### 2.4 API 실행 (프록시) + +- [ ] **ApiRequestService 구현** + - [ ] `execute()` 메서드 + - [ ] Guzzle HTTP 클라이언트 사용 + - [ ] 타임아웃 설정 (30초) + - [ ] 에러 핸들링 + +- [ ] **Controller 메서드** + - [ ] `execute(ExecuteApiRequest $request)` 구현 + +- [ ] **FormRequest 생성** + - [ ] `ExecuteApiRequest.php` + +### 2.5 응답 패널 + +- [ ] **View 구현** + - [ ] `partials/response-panel.blade.php` 완성 + +- [ ] **응답 표시** + - [ ] Status Code (색상 구분) + - [ ] Response Headers + - [ ] Response Body + +- [ ] **JSON 뷰어** + - [ ] `components/json-viewer.blade.php` + - [ ] Raw / Pretty / Tree 모드 + - [ ] 복사 버튼 + +### 2.6 Phase 2 완료 검증 + +- [ ] 검색 정상 동작 +- [ ] 필터 정상 동작 +- [ ] API 실행 및 응답 표시 +- [ ] JSON 편집/표시 정상 동작 + +--- + +## ⭐ Phase 3: 즐겨찾기/템플릿/히스토리 (예상 3일) + +> 📖 **설계서 참조**: 템플릿 시스템(§7.3), HTMX OOB(§8.2) + +### 3.1 즐겨찾기 기능 + +- [ ] **UI 구현** + - [ ] 즐겨찾기 토글 버튼 (⭐) + - [ ] 즐겨찾기 목록 섹션 (사이드바 상단) + - [ ] 드래그&드롭 정렬 + +- [ ] **CRUD 구현** + - [ ] `addBookmark()` 메서드 + - [ ] `removeBookmark()` 메서드 + - [ ] `reorderBookmarks()` 메서드 + - [ ] `getBookmarks()` 메서드 + +- [ ] **HTMX 연동** + - [ ] 즐겨찾기 추가/제거 시 OOB 업데이트 + - [ ] 정렬 변경 시 자동 저장 + +### 3.2 템플릿 기능 + +- [ ] **UI 구현** + - [ ] 템플릿 저장 모달 + - [ ] 템플릿 목록 드롭다운 + - [ ] 템플릿 불러오기 버튼 + +- [ ] **CRUD 구현** + - [ ] `saveTemplate()` 메서드 + - [ ] `deleteTemplate()` 메서드 + - [ ] `getTemplates()` 메서드 + - [ ] `templatesForEndpoint()` 메서드 + +- [ ] **기능 구현** + - [ ] 현재 요청값을 템플릿으로 저장 + - [ ] 템플릿 적용 시 폼 자동 채우기 + - [ ] 공유 템플릿 (is_shared) + +### 3.3 히스토리 기능 + +- [ ] **UI 구현** + - [ ] 히스토리 서랍 (슬라이드) + - [ ] 히스토리 목록 (최근 50개) + - [ ] 재실행 버튼 + - [ ] 전체 삭제 버튼 + +- [ ] **CRUD 구현** + - [ ] `logRequest()` - 요청 자동 기록 + - [ ] `getHistory()` - 히스토리 조회 + - [ ] `clearHistory()` - 히스토리 삭제 + - [ ] `replayHistory()` - 히스토리 재실행 + +- [ ] **자동화** + - [ ] API 실행 시 자동 히스토리 저장 + - [ ] 실행 시간(duration_ms) 기록 + +### 3.4 Phase 3 완료 검증 + +- [ ] 즐겨찾기 추가/제거/정렬 동작 +- [ ] 템플릿 저장/불러오기 동작 +- [ ] 히스토리 자동 저장/재실행 동작 + +--- + +## 🔧 Phase 4: 환경 관리 + 고급 기능 (예상 2-3일) + +> 📖 **설계서 참조**: 환경 변수(§7.4), UI 레이아웃(§5.1), 반응형(§5.3) + +### 4.1 환경 관리 + +- [ ] **UI 구현** + - [ ] 환경 선택 드롭다운 (헤더) + - [ ] 환경 설정 모달 + - [ ] 환경 추가/수정/삭제 + +- [ ] **CRUD 구현** + - [ ] `getEnvironments()` 메서드 + - [ ] `saveEnvironment()` 메서드 + - [ ] `deleteEnvironment()` 메서드 + - [ ] `setDefaultEnvironment()` 메서드 + +- [ ] **기본 환경 설정** + - [ ] 로컬 (http://api.sam.kr) + - [ ] 개발 (https://api.codebridge-x.com) + +### 4.2 변수 치환 시스템 + +- [ ] **구현** + - [ ] `{{VARIABLE_NAME}}` 패턴 인식 + - [ ] 환경별 변수 저장 + - [ ] 요청 시 자동 치환 + +- [ ] **UI** + - [ ] 변수 입력 폼 (환경 설정 내) + - [ ] 치환 미리보기 + +### 4.3 키보드 단축키 + +- [ ] **구현** + - [ ] `Ctrl/Cmd + Enter` - API 실행 + - [ ] `Ctrl/Cmd + S` - 템플릿 저장 + - [ ] `Ctrl/Cmd + K` - 검색 포커스 + - [ ] `Escape` - 모달 닫기 + +### 4.4 UI 폴리싱 + +- [ ] **반응형 최적화** + - [ ] Desktop (≥1280px): 3-Panel + - [ ] Tablet (768-1279px): 2-Panel + 접이식 사이드바 + - [ ] Mobile (<768px): 1-Panel + 탭 전환 + +- [ ] **애니메이션** + - [ ] 패널 전환 트랜지션 + - [ ] 로딩 인디케이터 + - [ ] 성공/실패 토스트 메시지 + +### 4.5 Phase 4 완료 검증 + +- [ ] 환경 전환 정상 동작 +- [ ] 변수 치환 정상 동작 +- [ ] 키보드 단축키 동작 +- [ ] 반응형 UI 동작 + +--- + +## ✅ Phase 5: 테스트 + 배포 (예상 2일) + +> 📖 **설계서 참조**: 보안 고려사항(§9), 확장 가능성(§11) + +### 5.1 기능 테스트 + +- [ ] **단위 테스트** + - [ ] OpenApiParserService 테스트 + - [ ] ApiRequestService 테스트 + - [ ] ApiExplorerService 테스트 + +- [ ] **통합 테스트** + - [ ] Controller 엔드포인트 테스트 + - [ ] HTMX 부분 업데이트 테스트 + +### 5.2 수동 테스트 + +- [ ] **시나리오 테스트** + - [ ] 신규 사용자 온보딩 플로우 + - [ ] 즐겨찾기 → 템플릿 → 실행 플로우 + - [ ] 환경 전환 플로우 + +- [ ] **브라우저 호환성** + - [ ] Chrome 최신 + - [ ] Safari 최신 + - [ ] Firefox 최신 + +### 5.3 성능 최적화 + +- [ ] **캐싱** + - [ ] OpenAPI 스펙 캐싱 + - [ ] 엔드포인트 목록 캐싱 + +- [ ] **로딩 최적화** + - [ ] 초기 로딩 시간 측정 + - [ ] 필요시 지연 로딩 적용 + +### 5.4 문서화 + +- [ ] **사용자 가이드** + - [ ] 기본 사용법 + - [ ] 환경 설정 방법 + - [ ] 템플릿 활용법 + +- [ ] **개발자 가이드** + - [ ] 아키텍처 설명 + - [ ] 확장 방법 + +### 5.5 배포 + +- [ ] **코드 정리** + - [ ] Pint 포맷팅 + - [ ] 불필요한 코드 제거 + - [ ] 주석 정리 + +- [ ] **배포 준비** + - [ ] 환경 변수 확인 + - [ ] 마이그레이션 순서 확인 + +--- + +## 📋 기획 확인 필요 항목 + +> ⚠️ 구현 전 결정 필요 + +### 접근 권한 +- [ ] API Explorer 접근 가능 사용자 범위 (개발자만? 전체?) +- [ ] 환경별 접근 제한 (개발 환경에서만?) + +### 보안 +- [ ] API Key/Token 저장 방식 (암호화 필요?) +- [ ] 히스토리에 민감 정보 마스킹 여부 +- [ ] 프록시 허용 URL 화이트리스트 + +### 기능 범위 +- [ ] 공유 템플릿 기능 활성화 여부 +- [ ] 히스토리 보관 기간 (기본 30일?) +- [ ] 최대 저장 템플릿 수 제한? + +--- + +## 📝 작업 일지 + +### 2025-12-17 +- [x] API Explorer 상세 설계서 작성 완료 (api-explorer-spec.md) +- [x] 개발 작업 계획 초안 작성 +- [x] 계획서 보완 (핵심 스키마 인라인 + 설계서 참조 링크) + - DB 테이블 구조 (SQL) + - 라우트 정의 (PHP) + - Config 파일 + - Service 메서드 시그니처 + - Phase별 설계서 섹션 참조 추가 + +### YYYY-MM-DD +- [ ] (작업 내용 기록) + +--- + +## ✅ 완료 기준 + +### Phase 1 완료 조건 +- [ ] 기본 페이지 접근 및 엔드포인트 목록 표시 +- [ ] 마이그레이션 정상 실행 +- [ ] 3-Panel 레이아웃 동작 + +### Phase 2 완료 조건 +- [ ] 검색/필터 정상 동작 +- [ ] API 실행 및 응답 표시 + +### Phase 3 완료 조건 +- [ ] 즐겨찾기/템플릿/히스토리 CRUD 동작 + +### Phase 4 완료 조건 +- [ ] 환경 관리 및 변수 치환 동작 +- [ ] 반응형 UI 동작 + +### 전체 완료 조건 +- [ ] 모든 기능 구현 완료 +- [ ] 테스트 통과 +- [ ] Pint 포맷팅 완료 +- [ ] 문서화 완료 + +--- + +## 🔗 관련 링크 + +- **상세 설계서**: [`docs/features/api-explorer-spec.md`](../features/api-explorer-spec.md) +- **mng 프로젝트**: `mng/` +- **기존 dev-tools**: `mng/resources/views/dev-tools/` +- **OpenAPI 스펙**: `api/storage/api-docs/api-docs.json`