Django 4.2 + Django REST Framework | Port 8000
최종 업데이트: 2026-04-07
- 공통 사항
- 인증
- 에러 처리
- 문서 및 헬스체크
- 학습 코치 API
- 로드맵 API
- 기술 카드 API
- 댓글 인텔리전스 API
- 검색 및 추천 API
- Init Data 관리 API
- 노드 콘텐츠 생성 API
- 프론트엔드 통합 가이드
http://localhost:8000/api/ai/
게이트웨이 경유 시 /api/ai/ 프리픽스가 붙는다. 직접 호출 시 /ai/만 사용.
- 대부분의 AI 엔드포인트는 GET + Query Parameter 방식 (request body 아님)
- POST가 필요한 엔드포인트:
init-data생성,document-roadmapPOST,node-resource-save - JSON key: snake_case
- Content-Type:
application/json
| 유형 | 제한 |
|---|---|
| 비인증 (Anonymous) | 100 req/hour |
| 인증 (Authenticated) | 1,000 req/hour |
초과 시 429 Too Many Requests 응답.
Authorization: Bearer <token>
{
"sub": "user_123",
"roadmapId": "rm_frontend",
"permissions": ["READ", "EDIT"],
"iat": 1712448000,
"exp": 1712534400
}| 클레임 | 타입 | 설명 |
|---|---|---|
sub |
string |
사용자 ID |
roadmapId |
string? |
스코핑된 로드맵 ID (선택) |
permissions |
string[] |
READ, EDIT, ADMIN 중 하나 이상 |
iat |
number |
발급 시각 (Unix timestamp) |
exp |
number |
만료 시각 (Unix timestamp) |
개발 환경에서는 서버 환경변수 AI_AUTH_ENABLED=false로 인증을 끌 수 있다. 이 경우 모든 요청이 허용됨.
{
"detail": "Invalid authorization header. Expected Bearer token."
}HTTP Status: 401 Unauthorized 또는 403 Forbidden
{
"error": "에러 메시지"
}| 코드 | 의미 | 프론트 대응 |
|---|---|---|
200 |
성공 | 정상 처리 |
201 |
생성 성공 | 리소스 생성 완료 |
204 |
삭제 성공 | body 없음 |
400 |
잘못된 요청 | 파라미터 확인 후 재요청 |
401 |
인증 필요 | 토큰 갱신 후 재시도 |
403 |
권한 없음 | 접근 권한 확인 |
404 |
리소스 없음 | UI에 없음 표시 |
429 |
Rate Limit 초과 | 백오프 후 재시도 |
500 |
서버 에러 | 재시도 또는 에러 UI 표시 |
GET /ai/schema/
OpenAPI 3.0 JSON 스키마 반환. DRF Spectacular 자동 생성.
GET /ai/docs/
브라우저에서 API 테스트 가능한 Swagger UI.
GET /ai/redoc/
읽기 전용 API 문서 뷰.
서버 상태 및 외부 AI 서비스 연결 가능 여부를 확인한다.
GET /ai/health/
인증: 불필요
응답 예시:
{
"status": "ok",
"version": "1.0.0",
"services": {
"gemini": true,
"tavily": true,
"exa": true,
"graph_rag": true,
"semantic_cache": true
},
"timestamp": "2026-04-07T12:00:00.000000"
}| 필드 | 타입 | 설명 |
|---|---|---|
status |
string |
"ok" 또는 "error" |
version |
string |
API 버전 |
services |
Record<string, boolean> |
각 서비스 가용 여부 |
timestamp |
string |
체크 시각 (ISO 8601) |
프론트 활용: 앱 시작 시 서비스 가용성을 체크하여, 특정 AI 기능 버튼을 비활성화할 수 있다.
모든 AI 기능을 한 번에 호출하여 통합 결과를 반환한다. 개발 중 대시보드 UI의 샘플 데이터를 확인할 때 유용하다.
GET /ai/demo
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
roadmap_id |
string |
"rm_frontend" |
로드맵 ID |
tech_slug |
string |
"react" |
기술 카드 슬러그 |
user_id |
string |
"user_1" |
사용자 ID |
question |
string |
(예시 포함) | 질문/검색 문장 |
goal |
string |
- | 로드맵 생성 목표 |
target_role |
string |
- | 추천 대상 역할 |
compose_level |
"quick" | "full" |
"quick" |
답변 상세 수준 |
include_rationale |
boolean |
false |
태그 rationale 포함 여부 |
응답 예시 (축약):
{
"meta": {
"generated_at": "2026-04-07T00:00:00Z",
"roadmap_id": "rm_frontend",
"tech_slug": "react",
"user_id": "user_1",
"compose_level": "quick"
},
"record_coach": { "record_id": "rec1", "scores": { "quality_score": 68 } },
"related_roadmaps": { "roadmap_id": "rm_frontend", "candidates": [] },
"tech_card": { "name": "react", "summary": "..." },
"tech_fingerprint": { "roadmap_id": "rm_frontend", "tags": [] },
"comment_digest": { "highlights": [], "bottlenecks": [] },
"duplicate_suggest": [],
"resource_recommendation": { "query": "...", "items": [] },
"learning_pattern": { "patterns": { "active_days": 5 } },
"graph_rag_context": { "retrieval_evidence": [] },
"roadmap_generated": { "nodes": [], "edges": [] },
"roadmap_recommendation": { "nodes": [] },
"learning_coach": { "answer": "..." }
}학습 기록을 루브릭으로 점수화하고, 개선 포인트와 수정 제안을 제공한다.
GET /ai/record-coach
인증: 선택 (토큰의 roadmapId로 자동 스코핑)
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
roadmap_id |
string |
O | 로드맵 ID |
node_id |
string |
X | 특정 노드 ID |
compose_level |
"quick" | "full" |
X | quick: 점수/질문 중심, full: LLM 문장화 포함 |
응답 예시:
{
"record_id": "rec1",
"model_version": "rule-based",
"prompt_version": "coach_v1",
"created_at": "2026-04-07T12:00:00Z",
"scores": {
"evidence_level": 3,
"structure_score": 75,
"specificity_score": 45,
"reproducibility_score": 100,
"quality_score": 68
},
"strengths": [
"링크 기반 근거가 있어 신뢰도가 높다",
"목표/문제/해결 구조가 일정 부분 보인다",
"재현 가능한 링크가 포함되어 있다"
],
"gaps": ["구체적인 수치나 에러 메시지가 부족하다"],
"rewrite_suggestions": {
"portfolio_bullets": ["React 컴포넌트 최적화를 통해 렌더링 성능 30% 개선"],
"improved_memo": "useEffect 의존성 배열 최적화로 불필요한 리렌더 제거. Before: 12회/초 -> After: 2회/초"
},
"code_feedback": [],
"next_actions": [{ "effort": "2h", "task": "에러 로그/수치 기록 및 원인 분석 추가" }],
"followup_questions": ["어떤 에러가 발생했나요? 에러 메시지를 기록해 주세요."],
"retrieval_evidence": [
{
"source": "tech_card",
"id": "card_react",
"snippet": "useEffect 의존성 배열 누락 시 무한 렌더 발생"
}
]
}| 필드 | 타입 | 설명 |
|---|---|---|
record_id |
string |
기록 ID |
model_version |
string |
사용된 모델 버전 |
prompt_version |
string |
프롬프트 버전 |
created_at |
string |
생성 시각 |
scores.evidence_level |
integer |
근거 수준 (0-5) |
scores.structure_score |
integer |
구조 점수 (0-100) |
scores.specificity_score |
integer |
구체성 점수 (0-100) |
scores.reproducibility_score |
integer |
재현 가능성 점수 (0-100) |
scores.quality_score |
integer |
종합 품질 점수 (0-100) |
strengths |
string[] |
장점 목록 |
gaps |
string[] |
보완 필요 사항 |
rewrite_suggestions |
object |
포트폴리오 문장/메모 개선안 (compose_level=full 시) |
code_feedback |
array |
코드 품질 피드백 |
next_actions |
{ effort, task }[] |
다음 행동 제안 |
followup_questions |
string[] |
기록 보완 질문 |
retrieval_evidence |
RetrievalEvidence[] |
AI가 참조한 근거 |
프론트 활용: 학습 기록 작성 후 피드백 카드로 표시. scores로 레이더 차트를 그리면 시각적으로 효과적. compose_level=quick으로 빠른 피드백, full로 상세 개선안 제공.
질문 의도 분류 -> 도구 실행 -> 답변 구성의 멀티스테이지 학습 코치.
GET /ai/learning-coach
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
question |
string |
O | 사용자 질문 |
user_id |
string |
O | 사용자 ID |
compose_level |
"quick" | "full" |
X | quick: 캐시/로컬 중심, full: LLM 정제 포함 |
응답 예시:
{
"user_id": "user_1",
"question": "React 상태관리 어떻게 하나요?",
"intent": "concept",
"toolchain": ["graph_explorer"],
"plan": ["route", "retrieve", "compose"],
"answer": "React 상태관리는 크게 로컬 상태(useState), 전역 상태(Zustand, Redux), 서버 상태(TanStack Query)로 나뉩니다. 프로젝트 규모에 따라 선택하세요.",
"retrieval_evidence": [
{ "source": "graph", "id": "rm_react:node_state", "snippet": "상태관리 redux zustand" }
],
"behavior_summary": {
"motivation": 0.7,
"ability": 0.6,
"prompt_hour": 20,
"dropout_risk": 0.01
},
"model_version": "coach_v1",
"prompt_version": "coach_v1",
"created_at": "2026-04-07T12:00:00Z",
"cache_hit": false
}| 필드 | 타입 | 설명 |
|---|---|---|
intent |
string |
의도 분류 (concept, debug, recommendation 등) |
toolchain |
string[] |
실행된 도구 (graph_explorer, doc_retriever, progress_checker) |
plan |
string[] |
실행 계획 단계 |
answer |
string |
최종 답변 텍스트 |
retrieval_evidence |
RetrievalEvidence[] |
참조 근거 |
behavior_summary |
object |
Fogg B=MAP 행동 모델 분석 |
behavior_summary.motivation |
number |
동기 수준 (0-1) |
behavior_summary.ability |
number |
능력 수준 (0-1) |
behavior_summary.prompt_hour |
number |
최적 학습 시간대 |
behavior_summary.dropout_risk |
number |
이탈 위험도 (0-1) |
cache_hit |
boolean |
시맨틱 캐시 히트 여부 |
프론트 활용: 채팅 UI에서 사용. cache_hit=true이면 응답이 빠름을 UI에 표시 가능. behavior_summary.dropout_risk가 높으면 동기 부여 알림 표시.
학습 이벤트 로그 기반으로 활동 패턴을 분석하고 개선 제안을 제공한다.
GET /ai/learning-pattern
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
user_id |
string |
O | 사용자 ID |
days |
integer |
X | 분석 기간 (일, 기본 30) |
응답 예시:
{
"user_id": "user_1",
"period": "last_30d",
"patterns": {
"active_days": 12,
"avg_session_gap_days": 2.3,
"completion_velocity": 0.4
},
"recommendations": [
"현재 학습 패턴이 안정적입니다. 난이도를 조금 올려보세요",
"주 3-4회 학습을 유지하면 완주율이 크게 높아집니다"
],
"model_version": "pattern_v1",
"generated_at": "2026-04-07T12:00:00Z"
}| 필드 | 타입 | 설명 |
|---|---|---|
period |
string |
분석 기간 (last_7d, last_30d 등) |
patterns.active_days |
integer |
활동 일수 |
patterns.avg_session_gap_days |
number |
평균 세션 간격 (일) |
patterns.completion_velocity |
number |
완료 속도 (0-1) |
recommendations |
string[] |
개선 제안 |
model_version |
string |
모델 버전 |
generated_at |
string |
생성 시각 |
프론트 활용: 프로필/대시보드에서 학습 통계 카드로 표시. active_days와 completion_velocity로 진행률 차트 렌더링.
행동/콘텐츠/그래프 유사도를 종합하여 연관 로드맵 후보를 반환한다.
GET /ai/related-roadmaps
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
roadmap_id |
string |
O | 기준 로드맵 ID |
응답 예시:
{
"roadmap_id": "rm_frontend",
"generated_at": "2026-04-07T12:00:00Z",
"candidates": [
{
"related_roadmap_id": "rm_react",
"score": 0.92,
"reasons": [
{ "type": "co_complete", "value": 0.31 },
{ "type": "tag_overlap", "value": 4 }
]
},
{
"related_roadmap_id": "rm_backend",
"score": 0.53,
"reasons": [{ "type": "tag_overlap", "value": 2 }]
}
],
"model_version": "ranker_v1",
"evidence_snapshot": {
"tracks": ["behavior", "content", "structure"],
"candidate_count": 2
}
}| 필드 | 타입 | 설명 |
|---|---|---|
candidates[].related_roadmap_id |
string |
추천 로드맵 ID |
candidates[].score |
number |
유사도 점수 (0-1) |
candidates[].reasons |
{ type, value }[] |
추천 근거 (co_complete, tag_overlap 등) |
evidence_snapshot |
object |
랭킹 피처 스냅샷 |
프론트 활용: 로드맵 상세 페이지 하단에 "관련 로드맵" 섹션으로 표시. score 기준으로 정렬하여 카드 리스트 렌더링.
목표와 선호 태그를 기반으로 로드맵을 자동 생성한다.
GET /ai/roadmap-generated
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
goal |
string |
O | 목표 (예: "백엔드 개발자") |
preferred_tags |
string |
X | 선호 태그 (콤마 구분, 예: "python,django,docker") |
max_nodes |
integer |
X | 최대 노드 수 |
compose_level |
"quick" | "full" |
X | quick: 규칙 기반, full: LLM 문장화 포함 |
응답 예시:
{
"roadmap_id": "generated",
"title": "백엔드 개발자 로드맵",
"description": "Python과 Django를 중심으로 백엔드 개발 역량을 키우는 로드맵입니다.",
"nodes": [
{ "node_id": "node_python", "title": "Python 기초", "tags": ["python", "basics"] },
{ "node_id": "node_django", "title": "Django 프레임워크", "tags": ["django", "web"] },
{ "node_id": "node_db", "title": "Database 설계", "tags": ["database", "sql"] },
{ "node_id": "node_api", "title": "REST API 설계", "tags": ["api", "rest"] },
{ "node_id": "node_deploy", "title": "Docker 배포", "tags": ["docker", "deploy"] }
],
"edges": [
{ "source": "node_python", "target": "node_django" },
{ "source": "node_django", "target": "node_api" },
{ "source": "node_python", "target": "node_db" },
{ "source": "node_api", "target": "node_deploy" }
],
"tags": ["python", "django", "backend"],
"model_version": "generator_v1",
"prompt_version": "gen_v1",
"created_at": "2026-04-07T12:00:00Z",
"retrieval_evidence": [
{ "source": "graph", "id": "rm_backend:node_python", "snippet": "Python 기초 문법과 자료구조" }
]
}| 필드 | 타입 | 설명 |
|---|---|---|
roadmap_id |
string |
항상 "generated" (아직 저장 전) |
title |
string |
생성된 로드맵 제목 |
description |
string |
로드맵 설명 |
nodes |
{ node_id, title, tags }[] |
노드 목록 |
edges |
{ source, target }[] |
엣지 목록 (선후행 관계) |
tags |
string[] |
로드맵 태그 |
retrieval_evidence |
RetrievalEvidence[] |
생성 시 참조한 근거 |
프론트 활용: "AI 로드맵 생성" 기능에서 nodes와 edges를 그래프로 렌더링. 사용자가 확인 후 로드맵 서비스에 저장하는 플로우로 구성.
목표 역할 기반으로 그래프 온톨로지에서 학습 순서를 추천한다.
GET /ai/roadmap-recommendation
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
target_role |
string |
O | 목표 역할 (예: "frontend_dev") |
user_id |
string |
X | 사용자 ID |
응답 예시:
{
"roadmap_id": "roadmap:frontend_dev",
"target_role": "frontend_dev",
"nodes": [
{ "node_id": "node_html", "status": "COMPLETED" },
{ "node_id": "node_css", "status": "AVAILABLE" },
{ "node_id": "node_js", "status": "AVAILABLE" },
{ "node_id": "node_react", "status": "LOCKED" }
],
"edges": [
{ "source": "node_html", "target": "node_css" },
{ "source": "node_css", "target": "node_js" },
{ "source": "node_js", "target": "node_react" }
],
"gnn_predictions": {
"node_html": ["node_css"],
"node_css": ["node_js"],
"node_js": ["node_react"]
},
"model_version": "gnn_v1",
"created_at": "2026-04-07T12:00:00Z"
}| 필드 | 타입 | 설명 |
|---|---|---|
target_role |
string |
요청된 목표 역할 |
nodes[].status |
string |
COMPLETED, AVAILABLE, LOCKED, IN_PROGRESS, NEEDS_REVIEW |
gnn_predictions |
Record<string, string[]> |
GNN 기반 다음 학습 후보 예측 |
프론트 활용: 온보딩 또는 "추천 학습 경로" 페이지에서 사용. status로 노드 색상 분기, gnn_predictions로 "다음 추천" 하이라이트.
이력서/학습 계획서 등 문서를 분석하여 맞춤 학습 로드맵을 추천한다.
GET /ai/document-roadmap
POST /ai/document-roadmap
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
document |
string |
O | 분석할 문서 내용 |
goal |
string |
X | 목표 직군/분야 (예: "Backend Developer") |
Request Body:
{
"document": "저는 Python과 Django를 1년간 공부했습니다. 백엔드 개발자로 취업하고 싶습니다.",
"goal": "Backend Developer"
}응답 예시 (GET/POST 동일):
{
"document_summary": "Python/Django 1년 학습 경험 보유. 백엔드 개발자 취업 목표.",
"extracted_keywords": ["python", "django", "백엔드"],
"recommended_roadmaps": [
{ "related_roadmap_id": "rm_backend:node_api", "score": 0.95, "reasons": [] },
{ "related_roadmap_id": "rm_backend:node_db", "score": 0.85, "reasons": [] },
{ "related_roadmap_id": "rm_devops:node_docker", "score": 0.75, "reasons": [] }
],
"suggested_topics": ["rest api", "database", "docker", "ci/cd", "testing"],
"model_version": "doc_v1",
"created_at": "2026-04-07T12:00:00Z"
}| 필드 | 타입 | 설명 |
|---|---|---|
document_summary |
string |
AI 문서 요약 |
extracted_keywords |
string[] |
추출된 핵심 키워드 |
recommended_roadmaps |
RelatedRoadmapCandidate[] |
추천 로드맵 목록 (ID + 점수) |
suggested_topics |
string[] |
후속 학습 추천 주제 |
프론트 활용: 문서(이력서) 업로드 -> 분석 결과로 맞춤 로드맵 추천. suggested_topics로 태그 선택 UI 연계.
기술의 요약/사용 시점/대안/주의사항/학습 경로를 카드 형태로 제공한다.
GET /ai/tech-cards
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
tech_slug |
string |
O | 기술 슬러그 (예: "react", "django", "docker") |
응답 예시:
{
"id": "card_react",
"name": "react",
"tech_slug": "react",
"category": "tech",
"version": "2026-04",
"summary": "Facebook이 개발한 UI 라이브러리. 컴포넌트 기반 아키텍처로 복잡한 UI를 선언적으로 구성.",
"summary_vector": [0.12, 0.34, 0.56],
"why_it_matters": [
"업계 표준에 가까운 사용 사례를 확보할 수 있다",
"방대한 생태계와 커뮤니티 지원"
],
"when_to_use": [
"UI/서비스의 구조를 빠르게 확장해야 할 때",
"SPA 또는 SSR이 필요한 웹 애플리케이션"
],
"alternatives": [
{ "slug": "vue", "why": "학습 난이도가 낮고 템플릿 기반" },
{ "slug": "svelte", "why": "번들 크기가 작고 빌드 타임 컴파일" }
],
"pitfalls": [
"의존성 배열을 누락해 무한 렌더가 발생하는 케이스가 많다",
"과도한 상태 리프팅은 성능 저하를 유발한다"
],
"learning_path": [
{ "stage": "basic", "items": ["JSX 문법", "컴포넌트 props/state"] },
{ "stage": "intermediate", "items": ["Hooks (useEffect, useMemo)", "Context API"] },
{ "stage": "advanced", "items": ["Server Components", "Concurrent Features"] }
],
"metadata": {
"language": "JavaScript/TypeScript",
"license": "MIT",
"latest_version": "19.1.0",
"last_updated": "2026-03"
},
"relationships": {
"based_on": ["javascript"],
"alternatives": ["vue", "svelte", "angular"]
},
"reliability_metrics": {
"community_score": 95,
"doc_freshness": 90,
"source_count": 12
},
"latest_changes": {
"changed": true,
"change_ratio": 0.15,
"summary": "React 19 Server Actions 추가"
},
"reel_evidence": [{ "query": "license", "snippet": "MIT License" }],
"sources": [
{ "title": "React 공식 문서", "url": "https://react.dev", "fetched_at": "2026-04-01T00:00:00Z" }
],
"generated_by": {
"model_version": "card_v1",
"prompt_version": "card_v1"
}
}| 필드 | 타입 | 설명 |
|---|---|---|
id |
string |
카드 고유 ID |
name |
string |
기술명 |
tech_slug |
string |
슬러그 |
summary |
string |
기술 요약 |
why_it_matters |
string[] |
왜 중요한지 |
when_to_use |
string[] |
언제 사용하는지 |
alternatives |
{ slug, why }[] |
대안 기술 |
pitfalls |
string[] |
주의사항 |
learning_path |
{ stage, items }[] |
단계별 학습 경로 |
metadata |
object |
언어/라이선스/최신 버전 |
relationships |
object |
기반 기술/대안 관계 |
reliability_metrics |
object |
커뮤니티 점수/문서 최신성/소스 수 |
latest_changes |
object |
최근 변경 여부/비율/요약 |
sources |
{ title, url, fetched_at }[] |
출처 |
프론트 활용: 노드 클릭 시 기술 카드 모달/패널로 표시. learning_path로 "학습 순서" 스텝퍼 구현. alternatives로 "대안 기술 보기" 링크.
로드맵 텍스트에서 핵심 기술을 추출하여 core/optional/alternative/deprecated로 분류한다.
GET /ai/tech-fingerprint
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
roadmap_id |
string |
O | 로드맵 ID |
include_rationale |
boolean |
X | true 시 분류 근거 포함 |
응답 예시:
{
"roadmap_id": "rm_frontend",
"tags": [
{
"tech_slug": "react",
"type": "core",
"confidence": 0.95,
"rationale": "로드맵의 핵심 주제로, 5개 이상의 노드에서 직접 언급"
},
{
"tech_slug": "zustand",
"type": "alternative",
"confidence": 0.72,
"rationale": "상태관리 대안으로 1개 노드에서 언급"
},
{
"tech_slug": "jquery",
"type": "deprecated",
"confidence": 0.88,
"rationale": "현대 프레임워크 대비 사용이 권장되지 않음"
}
],
"generated_at": "2026-04-07T12:00:00Z",
"model_version": "tagger_v1"
}| 필드 | 타입 | 설명 |
|---|---|---|
tags[].tech_slug |
string |
기술 슬러그 |
tags[].type |
string |
core, optional, alternative, deprecated |
tags[].confidence |
number |
신뢰도 (0-1) |
tags[].rationale |
string? |
분류 근거 (include_rationale=true 시) |
프론트 활용: 로드맵 편집 페이지에서 자동 태그 제안. type 기준으로 태그 색상 분기 (core=파랑, deprecated=빨강 등).
최근 댓글에서 주요 이슈와 병목 노드를 요약한다.
GET /ai/comment-digest
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
roadmap_id |
string |
O | 로드맵 ID |
period_days |
integer |
X | 분석 기간 (일, 기본 14) |
응답 예시:
{
"roadmap_id": "rm_frontend",
"period": "last_14d",
"highlights": [
"useEffect에서 의존성 배열을 비우면 렌더가 반복돼요",
"JS async/await 에러 처리를 어떻게 정리하나요?",
"CSS Grid와 Flexbox 차이가 헷갈립니다"
],
"bottlenecks": [
{
"node_id": "node_js",
"score": 1.0,
"top_topics": ["질문 빈도 증가", "에러 처리 반복 질문"]
},
{
"node_id": "node_css",
"score": 0.65,
"top_topics": ["레이아웃 혼란"]
}
],
"generated_by": {
"model_version": "digest_v1",
"prompt_version": null
}
}| 필드 | 타입 | 설명 |
|---|---|---|
highlights |
string[] |
주요 이슈 문장 |
bottlenecks |
object[] |
병목 노드 (질문 수/부정 반응/미해결 비율 기반) |
bottlenecks[].node_id |
string |
병목 노드 ID |
bottlenecks[].score |
number |
병목 점수 (0-1, 높을수록 심각) |
bottlenecks[].top_topics |
string[] |
주요 토픽 |
프론트 활용: 로드맵 관리자 대시보드에서 병목 노드 하이라이트. score가 높은 노드에 경고 아이콘 표시. highlights를 "이번 주 주요 질문" 섹션으로 표시.
질문 작성 시 유사한 기존 질문을 추천한다. LLM 없이 벡터 유사도 기반으로 빠르게 동작.
GET /ai/comment-duplicates
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
roadmap_id |
string |
O | 로드맵 ID |
question |
string |
O | 작성 중인 질문 |
top_k |
integer |
X | 추천 개수 (기본 5) |
응답 예시:
[
{
"comment_id": "c2",
"snippet": "JS async/await 에러 처리를 어떻게 정리하나요?"
},
{
"comment_id": "c1",
"snippet": "useEffect에서 의존성 배열을 비우면 렌더가 반복돼요"
}
]| 필드 | 타입 | 설명 |
|---|---|---|
comment_id |
string |
기존 댓글 ID |
snippet |
string |
유사 질문 내용 |
프론트 활용: 댓글 작성 폼에서 입력 중 debounce(300ms)로 호출. "이미 비슷한 질문이 있어요" 팝오버로 중복 질문 방지.
로컬 지식베이스(BM25/Vector)와 웹 검색 결과를 결합한 하이브리드 추천.
GET /ai/resource-recommendation
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
query |
string |
O | 검색 질의 (예: "React hooks 상태 관리") |
top_k |
integer |
X | 추천 개수 (기본 3) |
recency_days |
integer |
X | 최신 자료 기준 기간 (기본 30, 0이면 제한 없음) |
응답 예시:
{
"query": "React hooks 상태 관리",
"generated_at": "2026-04-07T12:00:00Z",
"items": [
{
"title": "React 공식 문서 - useState",
"url": "https://react.dev/reference/react/useState",
"source": "web",
"score": 0.95
},
{
"title": "Zustand로 간단한 상태 관리",
"url": "https://zustand-demo.pmnd.rs/",
"source": "web",
"score": 0.87
},
{
"title": "커뮤니티 추천: React 상태 관리 비교",
"url": "https://example.com/react-state",
"source": "resource",
"score": 0.82
}
],
"model_version": "retriever_v1",
"retrieval_evidence": [
{
"source": "resource",
"id": "res_react_hooks",
"snippet": "useState와 useReducer의 차이점..."
}
]
}| 필드 | 타입 | 설명 |
|---|---|---|
items[].title |
string |
자료 제목 |
items[].url |
string |
자료 URL |
items[].source |
string |
출처 (web, resource, internal) |
items[].score |
number |
관련성 점수 (0-1) |
retrieval_evidence |
RetrievalEvidence[] |
추천 근거 |
프론트 활용: 노드 상세 패널의 "추천 자료" 섹션. recency_days를 줄여 최신 자료 우선 표시. score로 추천 순위 표시.
Tavily(웹 검색) + Exa(시맨틱 검색)을 조합한 실시간 웹 검색.
GET /ai/web-search
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
query |
string |
O | 검색 쿼리 (예: "Python 비동기 프로그래밍") |
top_k |
integer |
X | 최대 결과 수 (기본 5, 최대 20) |
engine |
"tavily" | "exa" | "all" |
X | 사용할 검색 엔진 (기본 "all") |
recency_days |
integer |
X | 최신 자료 기준 기간 (기본 30, 0이면 제한 없음) |
응답 예시:
{
"query": "Django tutorial 2026",
"results": [
{
"title": "Django Girls Tutorial",
"url": "https://tutorial.djangogirls.org/en/",
"content": "Django를 사용하여 블로그 웹사이트를 만드는 단계별 가이드...",
"score": 0.9998,
"source": "tavily",
"fetched_at": "2026-04-07T12:00:00Z"
},
{
"title": "Getting started with Django",
"url": "https://www.djangoproject.com/start/",
"content": "Django 공식 시작 가이드. 설치부터 첫 앱 만들기까지...",
"score": 0.9995,
"source": "exa",
"fetched_at": "2026-04-07T12:00:00Z"
}
],
"engines_used": ["tavily", "exa"],
"total_results": 2,
"generated_at": "2026-04-07T12:00:00Z"
}| 필드 | 타입 | 설명 |
|---|---|---|
results[].title |
string |
검색 결과 제목 |
results[].url |
string |
검색 결과 URL |
results[].content |
string |
내용 요약 |
results[].score |
number |
관련성 점수 (0-1) |
results[].source |
string |
출처 엔진 (tavily 또는 exa) |
results[].fetched_at |
string |
검색 수행 시각 |
engines_used |
string[] |
실제 사용된 엔진 목록 |
total_results |
integer |
총 결과 수 |
프론트 활용: 검색 결과 페이지 또는 "자료 찾기" 모달에서 사용. 최신성이 중요하면 recency_days=7, 다양성이 중요하면 engine=all 사용.
그래프 기반 RAG 검색 결과를 반환한다. 근거 스니펫과 그래프 스냅샷을 함께 제공.
GET /ai/graph-rag
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
query |
string |
O | 검색 질의 |
top_k |
integer |
X | 근거 개수 |
응답 예시:
{
"retrieval_evidence": [
{
"source": "graph",
"id": "rm_frontend:node_html",
"snippet": "HTML 구조와 시맨틱 태그"
},
{
"source": "graph",
"id": "rm_react:node_state",
"snippet": "상태관리 redux zustand"
}
],
"graph_snapshot": {
"nodes": [
{ "node_id": "rm_frontend:node_html", "text": "HTML 구조", "tags": ["html"] },
{ "node_id": "rm_react:node_state", "text": "상태관리", "tags": ["redux", "zustand"] }
],
"edges": [
{
"source": "rm_frontend:node_html",
"target": "rm_frontend:node_css",
"type": "prerequisite"
}
]
}
}| 필드 | 타입 | 설명 |
|---|---|---|
retrieval_evidence |
RetrievalEvidence[] |
근거 스니펫 목록 |
graph_snapshot.nodes |
{ node_id, text, tags }[] |
관련 그래프 노드 |
graph_snapshot.edges |
{ source, target, type? }[] |
관련 그래프 엣지 |
프론트 활용: 다른 AI 응답의 retrieval_evidence를 "근거 보기"로 확장할 때 사용. 그래프 시각화로 관련 노드 간 관계를 보여줄 수 있다.
로드맵 생성을 위한 초기 데이터(파일/텍스트)를 관리하는 CRUD API.
GET /ai/init-data
인증: 선택 (토큰의 roadmapId로 자동 스코핑)
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
roadmap_id |
string |
O | 로드맵 ID (미지정 시 토큰의 roadmapId 사용) |
응답 예시:
[
{
"init_data_id": "init_a1b2c3d4",
"roadmap_id": "rm_frontend",
"content": "React는 Facebook에서 개발한 UI 라이브러리로...",
"data_type": "text",
"filename": null,
"created_at": "2026-04-07T10:00:00Z",
"updated_at": "2026-04-07T10:00:00Z"
},
{
"init_data_id": "init_e5f6g7h8",
"roadmap_id": "rm_frontend",
"content": "# Frontend Roadmap\n## HTML/CSS\n...",
"data_type": "file",
"filename": "frontend-roadmap.md",
"created_at": "2026-04-06T09:00:00Z",
"updated_at": "2026-04-06T09:00:00Z"
}
]POST /ai/init-data
인증: 선택
Request Body:
{
"roadmap_id": "rm_frontend",
"content": "React 컴포넌트 설계 가이드...",
"data_type": "text",
"filename": null
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
roadmap_id |
string |
X | 로드맵 ID (미지정 시 토큰 roadmapId) |
content |
string |
O | 데이터 내용 |
data_type |
"file" | "text" |
X | 데이터 타입 (기본 "text") |
filename |
string? |
X | 파일명 (file 타입일 때) |
응답: 201 Created
{
"init_data_id": "init_x9y0z1w2",
"roadmap_id": "rm_frontend",
"content": "React 컴포넌트 설계 가이드...",
"data_type": "text",
"filename": null,
"created_at": "2026-04-07T12:00:00Z",
"updated_at": "2026-04-07T12:00:00Z"
}GET /ai/init-data/{init_data_id}
인증: 선택
Path Parameters:
| 파라미터 | 타입 | 설명 |
|---|---|---|
init_data_id |
string |
Init Data ID (예: init_a1b2c3d4) |
응답: InitData 객체 (목록 조회와 동일 형태)
404 응답:
{ "error": "Not found" }PUT /ai/init-data/{init_data_id}
인증: 선택
Request Body:
{
"content": "수정된 내용..."
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
content |
string |
O | 수정할 내용 |
응답: 200 OK, 수정된 InitData 객체
DELETE /ai/init-data/{init_data_id}
인증: 선택
응답: 204 No Content (body 없음)
| 필드 | 타입 | 설명 |
|---|---|---|
init_data_id |
string |
고유 ID (자동 생성, init_ 접두사) |
roadmap_id |
string |
관련 로드맵 ID |
content |
string |
데이터 내용 |
data_type |
"file" | "text" |
데이터 타입 |
filename |
string? |
파일명 (file 타입일 때) |
created_at |
string |
생성 시각 (ISO 8601) |
updated_at |
string |
수정 시각 (ISO 8601) |
Init 데이터를 분석하여 로드맵 노드를 자동 생성한다.
GET /ai/node-generation
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
init_data_id |
string |
O | Init Data ID |
응답 예시 (RoadmapGenerated 형태):
{
"roadmap_id": "generated",
"title": "Init 데이터 기반 생성 로드맵",
"description": "업로드된 데이터를 분석하여 생성된 노드 구조입니다.",
"nodes": [
{ "node_id": "node_1", "title": "HTML 기초", "tags": ["html"] },
{ "node_id": "node_2", "title": "CSS 레이아웃", "tags": ["css", "layout"] },
{ "node_id": "node_3", "title": "JavaScript 기본", "tags": ["javascript"] }
],
"edges": [
{ "source": "node_1", "target": "node_2" },
{ "source": "node_2", "target": "node_3" }
],
"tags": ["frontend", "web"],
"model_version": "gen_v1",
"prompt_version": "gen_v1",
"created_at": "2026-04-07T12:00:00Z",
"retrieval_evidence": []
}에러 응답 (404):
{ "error": "Init data not found: init_invalid_id" }프론트 활용: Init 데이터 업로드 후 "노드 생성" 버튼 클릭 시 호출. 결과를 그래프 에디터에 프리로드.
노드 제목으로부터 AI가 설명을 생성한다.
GET /ai/node-description
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
node_title |
string |
O | 노드 제목 (예: "React Hooks") |
context |
string |
X | 추가 컨텍스트 (로드맵 제목 등) |
응답 예시:
{
"node_title": "React Hooks",
"description": "React 16.8에서 도입된 함수형 컴포넌트에서 상태와 라이프사이클을 관리하는 기능입니다. useState, useEffect, useContext 등의 내장 훅을 제공하며, 커스텀 훅을 통해 로직 재사용이 가능합니다.",
"generated_at": "2026-04-07T12:00:00Z"
}| 필드 | 타입 | 설명 |
|---|---|---|
node_title |
string |
요청한 노드 제목 |
description |
string |
AI 생성 설명 |
generated_at |
string |
생성 시각 |
프론트 활용: 노드 편집 시 "AI 설명 생성" 버튼으로 사용. 생성된 텍스트를 설명 필드에 자동 입력.
특정 노드 주제에 대한 학습 자료를 추천한다.
GET /ai/node-resource-recommendation
인증: 선택
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
node_id |
string |
O | 노드 ID |
roadmap_id |
string |
X | 로드맵 ID (미지정 시 토큰 roadmapId) |
응답 예시 (ResourceRecommendation 형태):
{
"query": "React Hooks 학습 자료",
"generated_at": "2026-04-07T12:00:00Z",
"items": [
{
"title": "React 공식 문서 - Hooks",
"url": "https://react.dev/reference/react/hooks",
"source": "web",
"score": 0.96
},
{
"title": "useHooks - Custom Hook 레시피 모음",
"url": "https://usehooks.com",
"source": "web",
"score": 0.88
}
],
"model_version": "retriever_v1",
"retrieval_evidence": []
}프론트 활용: 노드 상세 패널에서 "추천 자료" 탭으로 표시. 추천 결과에서 "저장" 버튼을 누르면 node-resource-save를 호출.
추천된 학습 자료를 노드에 저장한다.
POST /ai/node-resource-save
인증: 선택
Request Body:
{
"node_id": "node_react_hooks",
"title": "React 공식 문서 - Hooks",
"url": "https://react.dev/reference/react/hooks",
"source": "web",
"description": "React 공식 Hooks API 레퍼런스"
}| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
node_id |
string |
O | 노드 ID |
title |
string |
O | 자료 제목 |
url |
string |
O | 자료 URL |
source |
"web" | "internal" | "generated" |
X | 출처 (기본 "web") |
description |
string? |
X | 자료 설명 |
응답: 201 Created
{
"resource_id": "nr_a1b2c3d4",
"node_id": "node_react_hooks",
"title": "React 공식 문서 - Hooks",
"url": "https://react.dev/reference/react/hooks",
"source": "web",
"description": "React 공식 Hooks API 레퍼런스",
"created_at": "2026-04-07T12:00:00Z"
}| 필드 | 타입 | 설명 |
|---|---|---|
resource_id |
string |
고유 ID (자동 생성, nr_ 접두사) |
node_id |
string |
관련 노드 ID |
title |
string |
자료 제목 |
url |
string |
자료 URL |
source |
"web" | "internal" | "generated" |
출처 |
description |
string? |
자료 설명 |
created_at |
string |
생성 시각 (ISO 8601) |
/** AI가 참조한 근거 */
interface RetrievalEvidence {
source: string;
id: string;
snippet: string;
}
/** 에러 응답 */
interface AIErrorResponse {
error: string;
}
/** Rate Limit 초과 시 */
interface RateLimitError {
detail: string;
}const AI_BASE_URL = process.env.NEXT_PUBLIC_AI_URL ?? 'http://localhost:8000';
async function fetchAI<T>(
path: string,
params?: Record<string, string | number | boolean>,
options?: { method?: string; body?: unknown },
): Promise<T> {
const url = new URL(`/ai/${path}`, AI_BASE_URL);
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
url.searchParams.set(key, String(value));
}
});
}
const token = getAccessToken(); // 프로젝트의 토큰 관리 방식에 맞게
const res = await fetch(url.toString(), {
method: options?.method ?? 'GET',
headers: {
'Content-Type': 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
},
...(options?.body ? { body: JSON.stringify(options.body) } : {}),
});
if (res.status === 429) {
throw new Error('Rate limit exceeded. 잠시 후 다시 시도하세요.');
}
if (!res.ok) {
const err = await res.json().catch(() => ({ error: 'Unknown error' }));
throw new Error(err.error ?? `HTTP ${res.status}`);
}
if (res.status === 204) return undefined as T;
return res.json();
}// 학습 코치 Q&A
function useLearningCoach(question: string, userId: string) {
return useQuery({
queryKey: ['ai', 'learning-coach', question, userId],
queryFn: () =>
fetchAI<LearningCoachResponse>('learning-coach', {
question,
user_id: userId,
}),
enabled: !!question && !!userId,
staleTime: 5 * 60 * 1000, // 캐시 히트가 있으므로 5분
});
}
// 기술 카드
function useTechCard(techSlug: string) {
return useQuery({
queryKey: ['ai', 'tech-cards', techSlug],
queryFn: () => fetchAI<TechCardResponse>('tech-cards', { tech_slug: techSlug }),
enabled: !!techSlug,
staleTime: 30 * 60 * 1000, // 카드는 잘 안 바뀌므로 30분
});
}
// Init Data CRUD
function useCreateInitData() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: CreateInitDataRequest) =>
fetchAI<InitDataResponse>('init-data', undefined, {
method: 'POST',
body: data,
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['ai', 'init-data'] });
},
});
}AI 엔드포인트는 처리 시간이 길 수 있다 (특히 compose_level=full). 프론트에서 적절한 로딩 UI를 반드시 구현할 것.
| 엔드포인트 | 예상 응답 시간 | 권장 로딩 UI |
|---|---|---|
health |
< 1초 | 불필요 |
comment-duplicates |
< 1초 | 인라인 스피너 |
tech-fingerprint |
1-3초 | 스켈레톤 |
record-coach (quick) |
1-3초 | 스켈레톤 |
learning-coach (quick) |
2-5초 | 채팅 타이핑 인디케이터 |
roadmap-generated |
3-10초 | 프로그레스 바 + 메시지 |
tech-cards |
3-10초 | 카드 스켈레톤 |
web-search |
2-5초 | 검색 중 스피너 |
learning-coach (full) |
5-15초 | 단계별 프로그레스 |
async function fetchWithRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (err) {
if (err instanceof Error && err.message.includes('Rate limit') && i < maxRetries - 1) {
await new Promise((r) => setTimeout(r, 2000 * (i + 1))); // 지수 백오프
continue;
}
throw err;
}
}
throw new Error('Max retries exceeded');
}많은 AI 응답에 포함되는 공통 필드:
| 필드 | 설명 | 프론트 활용 |
|---|---|---|
retrieval_evidence |
AI가 참조한 근거 소스 | "출처 보기" 토글로 투명성 제공 |
model_version |
사용된 AI 모델 버전 | 디버깅/로깅용 |
prompt_version |
프롬프트 버전 | 디버깅/로깅용 |
generated_at / created_at |
결과 생성 시각 | "N분 전 생성" 표시 |
cache_hit |
캐시 재사용 여부 | 빠른 응답 표시 |