API Explorer 기능 스펙 및 개발 계획 문서 추가
- features/api-explorer-spec.md: API Explorer 기능 스펙 - plans/api-explorer-development-plan.md: API Explorer 개발 계획
This commit is contained in:
650
features/api-explorer-spec.md
Normal file
650
features/api-explorer-spec.md
Normal file
@@ -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
|
||||
<!-- 엔드포인트 목록 갱신 -->
|
||||
<div id="endpoint-list"
|
||||
hx-get="/dev-tools/api-explorer/endpoints"
|
||||
hx-trigger="load, search"
|
||||
hx-swap="innerHTML">
|
||||
</div>
|
||||
|
||||
<!-- 검색 입력 -->
|
||||
<input type="text"
|
||||
name="search"
|
||||
hx-get="/dev-tools/api-explorer/endpoints"
|
||||
hx-trigger="keyup changed delay:300ms"
|
||||
hx-target="#endpoint-list"
|
||||
hx-include="[name='methods[]'], [name='tags[]']">
|
||||
|
||||
<!-- 엔드포인트 선택 -->
|
||||
<button hx-get="/dev-tools/api-explorer/endpoints/{{ $operationId }}"
|
||||
hx-target="#request-panel"
|
||||
hx-swap="innerHTML">
|
||||
</button>
|
||||
|
||||
<!-- API 실행 -->
|
||||
<form hx-post="/dev-tools/api-explorer/execute"
|
||||
hx-target="#response-panel"
|
||||
hx-indicator="#loading-spinner">
|
||||
</form>
|
||||
|
||||
<!-- 즐겨찾기 토글 -->
|
||||
<button hx-post="/dev-tools/api-explorer/bookmarks"
|
||||
hx-vals='{"endpoint": "{{ $endpoint }}", "method": "{{ $method }}"}'
|
||||
hx-swap="outerHTML">
|
||||
⭐
|
||||
</button>
|
||||
```
|
||||
|
||||
### 8.2 OOB (Out-of-Band) 업데이트
|
||||
|
||||
```html
|
||||
<!-- 응답 후 히스토리 자동 갱신 -->
|
||||
<div id="history-count" hx-swap-oob="true">
|
||||
{{ $historyCount }}
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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)
|
||||
Reference in New Issue
Block a user