Skip to content

cliwant/dynamic-api-engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Prompt API Engine

MySQL 테이블 행 추가/수정만으로 API를 생성하고 관리하는 동적 API 엔진입니다.

🎯 핵심 컨셉

  • 코드 배포 없이 API 생성: DB에 행을 추가하면 즉시 새 API 엔드포인트 활성화
  • 다중 데이터소스 지원: MySQL, BigQuery, OpenSearch 등 다양한 데이터소스
  • 복잡한 쿼리 지원: 다중 쿼리, 파이프라인 처리
  • 버전 관리: 모든 변경 사항을 버전으로 관리, 언제든 롤백 가능
  • 감사 로그: 모든 변경 이력을 자동 기록
  • 보안: SQL Injection 방지, Soft Delete, API 키 인증
  • 🧠 AI 기능: LLM 기반 API 생성, SQL 최적화, 테스트 케이스 생성, 자연어 API 호출

⚙️ 시스템 동작 방식

전체 아키텍처

┌─────────────────────────────────────────────────────────────────────────────┐
│                              클라이언트 요청                                  │
│                    GET /api/users/list?limit=10                             │
└─────────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                           Universal Router                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │ 1. 요청 경로(/api/users/list)와 메서드(GET)로 API 메타데이터 조회    │   │
│  │ 2. APP_API_ROUTE_L 테이블에서 매칭되는 라우트 검색                   │   │
│  │ 3. USE_YN='Y', DEL_YN='N' 조건으로 활성 API만 처리                  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                           Version Resolver                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │ APP_API_VERSION_H 테이블에서 현재 버전(CRNT_YN='Y') 로직 조회        │   │
│  │ → LOGIC_TYPE, LOGIC_BODY, REQ_SPEC, RESP_SPEC 획득                  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                          Validator Service                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │ REQ_SPEC 기반으로 입력 파라미터 검증                                 │   │
│  │ - 필수값 체크 (required: true)                                       │   │
│  │ - 타입 검증 (string, int, float, bool, list)                        │   │
│  │ - 기본값 적용 (default)                                              │   │
│  │ - SQL Injection 위험 패턴 검사                                       │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                          Executor Service                                    │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │ LOGIC_TYPE에 따라 적절한 실행기 선택:                                │   │
│  │                                                                       │   │
│  │  SQL          → MySQL 쿼리 실행 (파라미터 바인딩)                    │   │
│  │  MULTI_SQL    → 다중 쿼리 순차 실행                                  │   │
│  │  PIPELINE     → 여러 스텝 연결 실행                                  │   │
│  │  BIGQUERY     → Google BigQuery 쿼리                                 │   │
│  │  OPENSEARCH   → OpenSearch 검색                                      │   │
│  │  HTTP_CALL    → 외부 API 호출                                        │   │
│  │  PYTHON_EXPR  → Python 표현식 실행 (제한적)                          │   │
│  │  STATIC       → 정적 JSON 반환                                       │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                          Response Mapper                                     │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │ RESP_SPEC에 따라 응답 형식 변환                                      │   │
│  │ - 필드명 매핑                                                         │   │
│  │ - 데이터 타입 변환 (datetime → ISO string)                           │   │
│  │ - 중첩 구조 처리                                                      │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
                                      │
                                      ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│                              JSON 응답 반환                                  │
│                    {"data": [...], "count": 10}                             │
└─────────────────────────────────────────────────────────────────────────────┘

1️⃣ API 요청 처리 흐름 (상세)

Step 1: Universal Router에서 요청 수신

# app/routers/universal_router.py
@router.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
async def handle_request(path: str, request: Request, db: AsyncSession):
    # 요청 경로와 메서드 추출
    method = request.method  # "GET"
    full_path = f"/api/{path}"  # "/api/users/list"

모든 /api/* 경로로 들어오는 요청은 Universal Router가 받습니다. 하드코딩된 엔드포인트가 아니라, DB에서 동적으로 라우팅합니다.

Step 2: DB에서 API 메타데이터 조회

-- 요청 경로와 메서드로 API 정의 검색
SELECT * FROM APP_API_ROUTE_L 
WHERE API_PATH = '/api/users/list' 
  AND HTTP_MTHD = 'GET'
  AND USE_YN = 'Y' 
  AND DEL_YN = 'N';

APP_API_ROUTE_L 테이블에서 활성화된 API 정의를 찾습니다.

Step 3: 현재 버전의 실행 로직 조회

-- 현재 활성 버전의 로직 조회
SELECT * FROM APP_API_VERSION_H 
WHERE ROUTE_ID = 'route-xxx' 
  AND CRNT_YN = 'Y';

APP_API_VERSION_H 테이블에서 CRNT_YN='Y'인 현재 버전의 로직을 가져옵니다.

Step 4: 파라미터 검증

# REQ_SPEC 예시
{
    "limit": {"type": "int", "required": false, "default": 10},
    "cmpny_id": {"type": "string", "required": true}
}

# 검증 과정
1. cmpny_id가 없으면400 에러 ("필수 파라미터 누락")
2. limit이 없으면기본값 10 적용
3. limit="abc" 이면400 에러 ("타입 불일치")
4. SQL Injection 패턴 검사"'; DROP TABLE" 감지  차단

Step 5: 로직 실행

# LOGIC_TYPE: "SQL"
# LOGIC_BODY: "SELECT * FROM APP_USER_L WHERE CMPNY_ID = :cmpny_id LIMIT :limit"

# 실행 (파라미터 바인딩으로 SQL Injection 방지)
result = await db.execute(
    text("SELECT * FROM APP_USER_L WHERE CMPNY_ID = :cmpny_id LIMIT :limit"),
    {"cmpny_id": "company-001", "limit": 10}
)

Step 6: 응답 반환

{
    "success": true,
    "data": [
        {"USER_ID": "user-001", "FIRST_NAME": "", "LAST_NAME": "길동", ...},
        {"USER_ID": "user-002", "FIRST_NAME": "", "LAST_NAME": "철수", ...}
    ],
    "count": 2,
    "execution_time_ms": 45.2
}

2️⃣ API 생성 흐름

새 API를 만들려면 코드 변경 없이 DB에 행만 추가하면 됩니다.

┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│   관리자 요청     │────▶│   Admin Router   │────▶│   DB에 저장       │
│  POST /admin/    │     │   (검증 + 저장)   │     │                  │
│    routes        │     │                  │     │                  │
└──────────────────┘     └──────────────────┘     └──────────────────┘
                                                           │
         ┌─────────────────────────────────────────────────┘
         ▼
┌──────────────────────────────────────────────────────────────────────┐
│  APP_API_ROUTE_L (새 행 추가)                                        │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │ ROUTE_ID: "route-new-001"                                      │ │
│  │ API_PATH: "/api/products/list"                                 │ │
│  │ HTTP_MTHD: "GET"                                               │ │
│  │ API_NAME: "상품 목록 조회"                                      │ │
│  │ USE_YN: "Y"                                                    │ │
│  │ DEL_YN: "N"                                                    │ │
│  └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌──────────────────────────────────────────────────────────────────────┐
│  APP_API_VERSION_H (새 행 추가)                                      │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │ VERSION_ID: "ver-new-001"                                      │ │
│  │ ROUTE_ID: "route-new-001"                                      │ │
│  │ VERSION_NO: 1                                                  │ │
│  │ CRNT_YN: "Y"  ← 현재 활성 버전                                  │ │
│  │ LOGIC_TYPE: "SQL"                                              │ │
│  │ LOGIC_BODY: "SELECT * FROM products LIMIT :limit"              │ │
│  │ REQ_SPEC: {"limit": {"type": "int", "default": 20}}            │ │
│  └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌──────────────────────────────────────────────────────────────────────┐
│  APP_API_AUDIT_H (감사 로그 자동 생성)                               │
│  ┌────────────────────────────────────────────────────────────────┐ │
│  │ ACTION: "CREATE"                                               │ │
│  │ ROUTE_ID: "route-new-001"                                      │ │
│  │ CREA_DT: "2026-01-09 18:30:00"                                 │ │
│  │ CHANGE_NOTE: "신규 API 생성"                                    │ │
│  └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌──────────────────────────────────────────────────────────────────────┐
│  ✅ 즉시 사용 가능                                                   │
│  GET /api/products/list?limit=10                                     │
│  (서버 재시작 불필요, 코드 배포 불필요)                               │
└──────────────────────────────────────────────────────────────────────┘

3️⃣ 버전 관리 흐름

API 수정이 필요할 때는 기존 버전을 수정하지 않고 새 버전을 추가합니다.

┌─────────────────────────────────────────────────────────────────────────┐
│                        버전 관리 시나리오                                │
└─────────────────────────────────────────────────────────────────────────┘

[초기 상태]
┌─────────────────────────────────────────┐
│ APP_API_VERSION_H                       │
├─────────────────────────────────────────┤
│ VERSION_NO: 1  │ CRNT_YN: Y ← 활성     │
│ LOGIC: SELECT * FROM users LIMIT 10     │
└─────────────────────────────────────────┘

                    │
                    │ 새 버전 추가 (POST /admin/routes/{id}/versions)
                    ▼

[버전 2 추가 후]
┌─────────────────────────────────────────┐
│ APP_API_VERSION_H                       │
├─────────────────────────────────────────┤
│ VERSION_NO: 1  │ CRNT_YN: N ← 비활성   │  ← 기존 버전 보존
│ LOGIC: SELECT * FROM users LIMIT 10     │
├─────────────────────────────────────────┤
│ VERSION_NO: 2  │ CRNT_YN: Y ← 활성     │  ← 새 버전이 현재 버전
│ LOGIC: SELECT * FROM users              │
│        WHERE del_yn='N' LIMIT 10        │
└─────────────────────────────────────────┘

                    │
                    │ 문제 발생! 롤백 필요 (PATCH /admin/routes/{id}/versions/1/activate)
                    ▼

[버전 1로 롤백]
┌─────────────────────────────────────────┐
│ APP_API_VERSION_H                       │
├─────────────────────────────────────────┤
│ VERSION_NO: 1  │ CRNT_YN: Y ← 활성     │  ← 즉시 롤백 완료!
│ LOGIC: SELECT * FROM users LIMIT 10     │
├─────────────────────────────────────────┤
│ VERSION_NO: 2  │ CRNT_YN: N ← 비활성   │
│ LOGIC: SELECT * FROM users              │
│        WHERE del_yn='N' LIMIT 10        │
└─────────────────────────────────────────┘

핵심 포인트:

  • 기존 버전은 절대 수정/삭제되지 않음 (Immutable)
  • 새 버전 추가 시 자동으로 CRNT_YN='Y' 설정
  • 롤백은 CRNT_YN 플래그만 변경하면 즉시 적용
  • 모든 버전 변경은 APP_API_AUDIT_H에 자동 기록

4️⃣ 로직 타입별 실행 방식

SQL (단일 쿼리)

# LOGIC_BODY: "SELECT * FROM APP_USER_L WHERE CMPNY_ID = :cmpny_id"
# 파라미터: {"cmpny_id": "company-001"}

result = await db.execute(text(logic_body), params)
return [dict(row) for row in result.fetchall()]

MULTI_SQL (다중 쿼리)

# LOGIC_BODY:
{
    "queries": [
        {"name": "company", "sql": "SELECT * FROM APP_CMPNY_L WHERE CMPNY_ID = :cmpny_id"},
        {"name": "users", "sql": "SELECT * FROM APP_USER_L WHERE CMPNY_ID = :cmpny_id"},
        {"name": "projects", "sql": "SELECT COUNT(*) as cnt FROM APP_PROJ_L WHERE CMPNY_ID = :cmpny_id"}
    ]
}

# 실행 결과:
{
    "company": {...},
    "users": [...],
    "projects": {"cnt": 15}
}

PIPELINE (파이프라인)

# LOGIC_BODY:
{
    "steps": [
        {"type": "SQL", "body": "SELECT COUNT(*) as total FROM APP_USER_L", "output": "user_count"},
        {"type": "SQL", "body": "SELECT COUNT(*) as total FROM APP_PROJ_L", "output": "proj_count"},
        {"type": "PYTHON_EXPR", "body": "$user_count.total + $proj_count.total", "output": "total"}
    ]
}

# Step 1 실행 → user_count = {"total": 100}
# Step 2 실행 → proj_count = {"total": 50}
# Step 3 실행 → total = 150 (이전 결과 참조)

HTTP_CALL (외부 API)

# LOGIC_BODY:
{
    "url": "https://api.external.com/data",
    "method": "GET",
    "headers": {"Authorization": "Bearer $params.token"},
    "timeout": 30
}

# 외부 API 호출 후 응답 반환
async with httpx.AsyncClient() as client:
    response = await client.request(method, url, headers=headers)
    return response.json()

5️⃣ LLM 기반 API 자동 생성 흐름

┌──────────────────────────────────────────────────────────────────────────────┐
│                         LLM API 생성 프로세스                                 │
└──────────────────────────────────────────────────────────────────────────────┘

[1단계: 테이블 선택]
┌─────────────────────────────────────────┐
│ 사용자: APP_USER_L, APP_CMPNY_L 선택    │
└─────────────────────────────────────────┘
                    │
                    ▼
[2단계: 스키마 정보 수집]
┌─────────────────────────────────────────────────────────────────────────────┐
│ GET /schema/tables/APP_USER_L                                               │
│ {                                                                           │
│   "columns": [                                                              │
│     {"name": "USER_ID", "type": "VARCHAR(50)", "key": "PRI"},              │
│     {"name": "CMPNY_ID", "type": "VARCHAR(50)", "key": "MUL"},             │
│     {"name": "FIRST_NAME", "type": "VARCHAR(100)"},                        │
│     ...                                                                     │
│   ],                                                                        │
│   "indexes": [{"name": "IX_CMPNY_ID", "columns": ["CMPNY_ID"]}],           │
│   "sample_data": [{"USER_ID": "user-001", "FIRST_NAME": "홍", ...}]        │
│ }                                                                           │
└─────────────────────────────────────────────────────────────────────────────┘
                    │
                    ▼
[3단계: 자연어 의도 입력]
┌─────────────────────────────────────────┐
│ 사용자: "회사별 사용자 수를 조회하는    │
│         API를 만들어줘"                 │
└─────────────────────────────────────────┘
                    │
                    ▼
[4단계: LLM 프롬프트 생성]
┌─────────────────────────────────────────────────────────────────────────────┐
│ 시스템 프롬프트:                                                            │
│ "당신은 API 설계 전문가입니다. 다음 테이블 스키마를 참고하여                │
│  사용자의 요청에 맞는 API 정의를 JSON으로 생성하세요."                      │
│                                                                             │
│ 테이블 스키마:                                                              │
│ - APP_USER_L: USER_ID(PK), CMPNY_ID(FK), FIRST_NAME, ...                   │
│ - APP_CMPNY_L: CMPNY_ID(PK), CMPNY_NAME, ...                               │
│                                                                             │
│ 사용자 요청: "회사별 사용자 수를 조회하는 API를 만들어줘"                   │
└─────────────────────────────────────────────────────────────────────────────┘
                    │
                    ▼
[5단계: Vertex AI Gemini 호출]
┌─────────────────────────────────────────────────────────────────────────────┐
│ POST https://vertex-ai.googleapis.com/v1/...                                │
│ Authorization: Bearer (gcloud-key.json)                                     │
│ Model: gemini-2.5-flash                                                     │
└─────────────────────────────────────────────────────────────────────────────┘
                    │
                    ▼
[6단계: LLM 응답 (API 정의)]
┌─────────────────────────────────────────────────────────────────────────────┐
│ {                                                                           │
│   "path": "/api/stats/users-by-company",                                   │
│   "method": "GET",                                                          │
│   "name": "회사별 사용자 수 조회",                                          │
│   "description": "각 회사별로 등록된 사용자 수를 집계합니다",               │
│   "logic_type": "SQL",                                                      │
│   "logic_body": "SELECT c.CMPNY_NAME, COUNT(u.USER_ID) as user_count       │
│                  FROM APP_CMPNY_L c                                         │
│                  LEFT JOIN APP_USER_L u ON c.CMPNY_ID = u.CMPNY_ID         │
│                  WHERE c.DEL_YN = 'N'                                       │
│                  GROUP BY c.CMPNY_ID, c.CMPNY_NAME                          │
│                  ORDER BY user_count DESC                                   │
│                  LIMIT :limit",                                             │
│   "request_spec": {                                                         │
│     "limit": {"type": "int", "required": false, "default": 10}             │
│   }                                                                         │
│ }                                                                           │
└─────────────────────────────────────────────────────────────────────────────┘
                    │
                    ▼
[7단계: SQL 테스트 실행]
┌─────────────────────────────────────────────────────────────────────────────┐
│ POST /schema/test-sql                                                       │
│ 생성된 SQL을 실제 DB에서 테스트 실행                                        │
│ → 성공 시: "✅ 테스트 통과 (5건 조회, 32ms)"                               │
│ → 실패 시: "❌ 문법 오류: Unknown column..."                               │
└─────────────────────────────────────────────────────────────────────────────┘
                    │
                    ▼
[8단계: API 등록]
┌─────────────────────────────────────────────────────────────────────────────┐
│ 테스트 통과 후 "API 생성" 버튼 클릭                                         │
│ → APP_API_ROUTE_L에 새 행 추가                                              │
│ → APP_API_VERSION_H에 새 행 추가                                            │
│ → 즉시 사용 가능: GET /api/stats/users-by-company                          │
└─────────────────────────────────────────────────────────────────────────────┘

6️⃣ 자연어 SQL 쿼리 실행 흐름

사용자 질문: "최근 가입한 사용자 5명 보여줘"
                    │
                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ [보안 검사 1단계] 악의적 의도 검사                                          │
│ check_question_intent()                                                      │
│ - "삭제해줘", "해킹", "비밀번호" 등 위험 키워드 감지                        │
│ → 통과 ✓                                                                    │
└─────────────────────────────────────────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ [SQL 생성] LLM이 자연어 → SQL 변환                                          │
│ generate_sql_from_natural_language()                                         │
│                                                                             │
│ 생성된 SQL:                                                                 │
│ SELECT USER_ID, FIRST_NAME, LAST_NAME, CREA_DT                              │
│ FROM APP_USER_L                                                             │
│ WHERE DEL_YN = 'N'                                                          │
│ ORDER BY CREA_DT DESC                                                       │
│ LIMIT 5                                                                     │
└─────────────────────────────────────────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ [보안 검사 2단계] SQL 보안 분석                                             │
│ check_sql_security()                                                         │
│ - DDL 명령어 (DROP, CREATE, ALTER) 검사 → 없음 ✓                           │
│ - DML 명령어 (INSERT, UPDATE, DELETE) 검사 → 없음 ✓                        │
│ - SQL Injection 패턴 검사 → 없음 ✓                                         │
│ - 민감 컬럼 (PASSWORD, TOKEN) 접근 검사 → 없음 ✓                           │
│ - LIMIT 존재 확인 → 있음 ✓                                                 │
│ → 결과: SAFE                                                                │
└─────────────────────────────────────────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ [SQL 정제] sanitize_sql_query()                                             │
│ - SELECT 문만 허용 확인                                                     │
│ - LIMIT 값이 max_results 초과 시 조정                                       │
│ - 주석 제거, 정규화                                                         │
└─────────────────────────────────────────────────────────────────────────────┘
                    │
                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ [실행] auto_execute=true 시 쿼리 실행                                       │
│                                                                             │
│ 결과:                                                                       │
│ ┌───────────────┬────────────┬───────────┬─────────────────────┐           │
│ │ USER_ID       │ FIRST_NAME │ LAST_NAME │ CREA_DT             │           │
│ ├───────────────┼────────────┼───────────┼─────────────────────┤           │
│ │ user-001      │ 김         │ 기연      │ 2026-01-09 10:00:00 │           │
│ │ user-002      │ 최         │ 준혁      │ 2026-01-08 15:30:00 │           │
│ │ ...           │            │           │                     │           │
│ └───────────────┴────────────┴───────────┴─────────────────────┘           │
└─────────────────────────────────────────────────────────────────────────────┘

📁 프로젝트 구조

prompt-api-engine/
├── app/
│   ├── core/           # 핵심 설정 (config, database)
│   ├── models/         # SQLAlchemy 모델
│   ├── routers/        # FastAPI 라우터
│   ├── schemas/        # Pydantic 스키마
│   ├── services/       # 비즈니스 로직 (Executor, Validator)
│   └── main.py         # 애플리케이션 엔트리포인트
├── scripts/            # 유틸리티 스크립트
├── requirements.txt
├── CHANGELOG.md
└── README.md

🚀 시작하기

1. 환경 설정

# 가상환경 생성 및 활성화
python -m venv venv
.\venv\Scripts\Activate

# 의존성 설치
pip install -r requirements.txt

# BigQuery 사용 시 (선택)
pip install google-cloud-bigquery

2. 환경 변수 설정

.env 파일에 다음 설정 추가:

# MySQL
MYSQL_HOST=localhost
MYSQL_USER=root
MYSQL_PASSWORD=password
MYSQL_DB=cliwant
MYSQL_PORT=3306

# 관리자 API 키
API_KEY=your-admin-api-key

# BigQuery (선택)
GCP_PROJECT_ID=your-project-id
GCP_CREDENTIALS_PATH=gcloud-key.json

# OpenSearch (선택)
OPENSEARCH_HOST=https://localhost:9200
OPENSEARCH_USER=admin
OPENSEARCH_PASSWORD=admin

3. 데이터베이스 초기화

# 테이블 생성
python scripts/create_tables.py

# 샘플 API 생성 (30개)
python scripts/insert_sample_apis.py

4. 서버 실행

uvicorn app.main:app --reload

접속 URL:

URL 설명
http://localhost:8000 API Tester UI (메인)
http://localhost:8000/docs Swagger UI
http://localhost:8000/redoc ReDoc
http://localhost:8000/admin/policy Immutable 정책 조회

📝 지원 로직 타입

타입 설명 예시 상태
SQL 단일 MySQL 쿼리 SELECT * FROM users WHERE id = :id ✅ 사용 가능
MULTI_SQL 다중 쿼리 순차 실행 여러 테이블 조인 결과 조합 ✅ 사용 가능
PIPELINE 여러 로직 파이프라인 연결 SQL → 변환 → 응답 ✅ 사용 가능
BIGQUERY Google BigQuery 쿼리 대용량 데이터 분석 ✅ 사용 가능
OPENSEARCH OpenSearch 검색 쿼리 전문 검색, 로그 분석 ✅ 사용 가능
PYTHON_EXPR Python 표현식 간단한 데이터 변환 비활성화
HTTP_CALL 외부 API 호출 타 서비스 연동 ✅ 사용 가능
STATIC_RESPONSE 정적 JSON 응답 목업, 테스트용 ✅ 사용 가능

⚠️ PYTHON_EXPR 비활성화: RCE(원격 코드 실행) 보안 위험으로 인해 v1.9.3부터 비활성화되었습니다. 대안으로 MULTI_SQL, PIPELINE, STATIC_RESPONSE를 사용하세요.

🗄️ DB 테이블 구조

APP_API_ROUTE_L (API 카탈로그)

컬럼 타입 설명
ROUTE_ID VARCHAR(50) PK
API_PATH VARCHAR(255) API 경로
HTTP_MTHD VARCHAR(10) HTTP 메서드
API_NAME VARCHAR(255) API 이름
USE_YN CHAR(1) 사용 여부 (Y/N)
DEL_YN CHAR(1) 삭제 여부 (Y/N)

APP_API_VERSION_H (실제 동작 로직)

컬럼 타입 설명
VERSION_ID VARCHAR(50) PK
ROUTE_ID VARCHAR(50) FK → APP_API_ROUTE_L
VERSION_NO INT 버전 번호
CRNT_YN CHAR(1) 현재 버전 여부
REQ_SPEC JSON 입력 파라미터 검증 규칙
LOGIC_TYPE VARCHAR(50) 로직 타입
LOGIC_BODY TEXT 실행할 로직
RESP_SPEC JSON 응답 매핑 규칙

📖 API 사용 예시

단일 SQL 쿼리

{
  "logic_type": "SQL",
  "logic_body": "SELECT * FROM APP_USER_L WHERE CMPNY_ID = :cmpny_id",
  "request_spec": {
    "cmpny_id": {"type": "string", "required": true}
  }
}

다중 SQL 쿼리 (MULTI_SQL)

{
  "logic_type": "MULTI_SQL",
  "logic_body": {
    "queries": [
      {"name": "users", "sql": "SELECT * FROM APP_USER_L WHERE CMPNY_ID = :cmpny_id"},
      {"name": "company", "sql": "SELECT * FROM APP_CMPNY_L WHERE CMPNY_ID = :cmpny_id"}
    ]
  }
}

파이프라인 (PIPELINE)

{
  "logic_type": "PIPELINE",
  "logic_body": {
    "steps": [
      {"type": "SQL", "body": "SELECT COUNT(*) as cnt FROM APP_USER_L", "output": "user_count"},
      {"type": "STATIC_RESPONSE", "body": "{\"total_users\": $params.user_count}"}
    ]
  }
}

BigQuery

{
  "logic_type": "BIGQUERY",
  "logic_body": "SELECT * FROM `project.dataset.table` WHERE date = @date LIMIT @limit",
  "request_spec": {
    "date": {"type": "string", "required": true},
    "limit": {"type": "int", "default": 100}
  }
}

OpenSearch

{
  "logic_type": "OPENSEARCH",
  "logic_body": {
    "index": "logs-*",
    "body": {
      "query": {"match": {"message": "$params.keyword"}},
      "size": 100
    }
  }
}

🧠 AI 기능 (v1.8.0+)

Vertex AI Gemini 2.5/3.0을 활용한 강력한 AI 기능들:

💬 자연어 API 호출

자연어로 질문하면 AI가 적합한 API를 찾아 실행합니다.

POST /schema/ai/chat

{
  "question": "최근 가입한 사용자 10명 보여줘",
  "auto_execute": true,
  "model": "vertex_ai/gemini-2.5-flash"
}

응답 예시:

  • 선택된 API: GET /api/users/list
  • 추출된 파라미터: {"limit": 10}
  • 신뢰도: 95%
  • 자동 실행 결과 포함

🔧 SQL 최적화 제안

SQL 쿼리를 분석하여 성능 개선 방안을 제안합니다.

POST /schema/ai/optimize-sql

{
  "sql_query": "SELECT * FROM APP_USER_L WHERE CMPNY_ID = :cmpny_id",
  "table_names": ["APP_USER_L"],
  "execution_time_ms": 500,
  "model": "vertex_ai/gemini-2.5-flash"
}

제안 항목:

  • 인덱스 활용 최적화
  • 쿼리 재작성 추천
  • JOIN 순서 최적화
  • 새 인덱스 생성 권장

🧪 테스트 케이스 자동 생성

API 정의를 분석하여 포괄적인 테스트 케이스를 생성합니다.

POST /schema/ai/generate-test-cases

{
  "route_id": "api-route-id",
  "model": "vertex_ai/gemini-2.5-flash"
}

생성 케이스 유형:

유형 설명 최소 개수
Positive 정상 동작 케이스 3개
Negative 에러 케이스 (필수값 누락 등) 2개
Boundary 경계값 테스트 2개
Performance 성능 테스트 1개

🔒 보안 기능

구현 완료된 보안 기능

기능 설명 버전
Immutable 정책 API 정의는 추가만 가능, 수정/삭제 불가 v1.0
SQL Injection 방지 DROP, TRUNCATE 등 위험 키워드 차단, 파라미터 바인딩 강제 v1.0
감사 로그 모든 변경 이력 기록 (누가, 언제, 무엇을) v1.0
버전 관리 기존 버전 보존, 언제든 이전 버전으로 전환 가능 v1.0
CORS 설정 강화 환경변수 기반 도메인 제한 (CORS_ORIGINS) v1.9.3
PYTHON_EXPR 비활성화 RCE 공격 방지를 위해 eval() 실행 차단 v1.9.3
auto_execute 기본 비활성화 Human-in-the-loop 강화 v1.9.3
읽기 전용 DB 계정 자연어 SQL 쿼리용 별도 계정 분리 v1.9.4
SQL 실행 타임아웃 30초 기본 타임아웃, DoS 방지 v1.9.4
SQL Injection 정규화 주석/공백 변형 우회 공격 차단 v1.9.4
민감 컬럼 마스킹 password, token 등 LLM 노출 방지 v1.9.4

⚠️ 보안 진단 보고서

자연어 SQL 쿼리 생성 기능에 대한 상세 보안 분석은 SECURITY_ASSESSMENT.md 문서를 참조하세요.

보안 개선 현황:

우선순위 항목 상태
🔴 P0 CORS 설정 강화 ✅ 완료 (v1.9.3)
🔴 P0 PYTHON_EXPR 비활성화 ✅ 완료 (v1.9.3)
🔴 P0 읽기 전용 DB 사용자 분리 ✅ 완료 (v1.9.4)
🔴 P0 SQL 실행 타임아웃 ✅ 완료 (v1.9.4)
🟠 P1 민감 컬럼 서버 측 마스킹 ✅ 완료 (v1.9.4)
🟠 P1 SQL Injection 정규화 ✅ 완료 (v1.9.4)
🔴 High API Key 인증 강화 📋 추후 진행
🔴 High JWT 인증 📋 추후 진행
🔴 High Redis 캐싱 📋 추후 진행
🟡 Medium RBAC 권한 관리 📋 추후 진행
🟡 Medium Rate Limiting 📋 추후 진행

ℹ️ P0 항목 대부분 완료됨. 프로덕션 배포 전 인증/캐싱 시스템 추가를 권장합니다.

🔒 Immutable 정책

API 정의 데이터의 무결성을 보장하기 위해 추가 전용(Append-only) 정책을 적용합니다:

리소스 허용 작업 금지 작업
APP_API_ROUTE_L CREATE, ACTIVATE, DEACTIVATE UPDATE, DELETE
APP_API_VERSION_H CREATE, SET_CURRENT UPDATE, DELETE
APP_API_AUDIT_H CREATE (자동) UPDATE, DELETE

장점:

  • ✅ 실수로 인한 API 삭제 완전 방지
  • ✅ 모든 변경 이력 영구 보존
  • ✅ 언제든 이전 버전으로 즉시 전환
  • ✅ 감사 추적 용이

🔧 관리자 API (Immutable)

⚠️ 주의: API 정의는 추가만 가능하며 수정/삭제할 수 없습니다.

# API 목록 조회 (공개)
curl http://localhost:8000/admin/routes

# 새 API 생성 (API 키 필요)
curl -X POST http://localhost:8000/admin/routes \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"path": "my-api", "method": "GET", "name": "My API"}'

# 새 버전 생성 (API 키 필요)
curl -X POST http://localhost:8000/admin/routes/{route_id}/versions \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "logic_type": "SQL",
    "logic_body": "SELECT * FROM users LIMIT :limit",
    "request_spec": {"limit": {"type": "int", "default": 10}},
    "change_note": "초기 버전"
  }'

# 상태 변경 (활성화/비활성화만 가능)
curl -X PATCH http://localhost:8000/admin/routes/{route_id}/status \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"is_active": false, "reason": "임시 비활성화"}'

# 현재 버전 변경
curl -X PATCH http://localhost:8000/admin/routes/{route_id}/versions/1/activate \
  -H "X-API-Key: your-api-key"

# 정책 조회
curl http://localhost:8000/admin/policy

📊 포함된 샘플 API (33개)

카테고리 API 수 예시
사용자 3 /api/users/list, /api/users/by-company
회사 3 /api/companies/list, /api/companies/by-bizno
프로젝트 6 /api/projects/recent, /api/projects/active
사전규격 3 /api/prcr-projects/recent
계약 4 /api/contracts/recent, /api/contracts/by-bizno
입찰계획 3 /api/bid-plans/by-year
면허 2 /api/licenses/by-bizno
검색 1 /api/searches/list
발주기관 2 /api/clients/list
다중쿼리 2 /api/company/dashboard
통계 1 /api/stats/projects-by-type
기본 3 /api/hello, /api/echo, /api/users

📄 라이선스

MIT License

About

MySQL 테이블 기반 동적 API 엔진 - LLM으로 API 자동 생성

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors