# 회의록별 대시보드 API 설계서 ## 개요 ### 목적 회의록이 확정된 후 회의 결과를 한눈에 파악할 수 있는 대시보드 데이터를 제공하는 API ### 버전 - API Version: v1.0 - 작성일: 2024-01-15 - 작성자: 이준호 (Backend Developer) ### 관련 유저스토리 - **UFR-MEET-070**: [회의록대시보드] 회의록 작성자로서 | 나는, 회의 결과를 한눈에 파악하기 위해 | 회의록별 대시보드를 통해 핵심 정보를 조회하고 싶다. --- ## API 엔드포인트 ### 1. 대시보드 전체 데이터 조회 #### 요청 ```http GET /api/v1/meetings/{meeting_id}/dashboard ``` **Path Parameters** | 이름 | 타입 | 필수 | 설명 | |------|------|------|------| | meeting_id | string (UUID) | Y | 회의 ID | **Query Parameters** | 이름 | 타입 | 필수 | 기본값 | 설명 | |------|------|------|--------|------| | include | string[] | N | all | 포함할 섹션 (key_points, decisions, todos, references) | | todo_status | string | N | all | Todo 필터 (all, not_started, in_progress, completed) | **Headers** ```http Authorization: Bearer {access_token} Content-Type: application/json ``` #### 응답 **Success (200 OK)** ```json { "meeting_id": "uuid-1234", "meeting_title": "2024 Q4 마케팅 전략 회의", "meeting_date": "2024-01-15T14:00:00Z", "location": "본사 대회의실", "participants_count": 5, "key_points": { "points": [ { "id": "kp-001", "order": 1, "content": "Q4 마케팅 예산을 전년 대비 30% 증액하여 디지털 채널 확대에 집중하기로 결정", "meeting_section_id": "section-123", "timestamp": "2024-01-15T14:25:00Z" }, { "id": "kp-002", "order": 2, "content": "신규 인플루언서 마케팅 캠페인을 2월부터 시작하며, 타겟 연령층을 20-30대로 설정", "meeting_section_id": "section-124", "timestamp": "2024-01-15T14:35:00Z" } ], "keywords": [ { "tag": "#디지털마케팅", "count": 15 }, { "tag": "#예산증액", "count": 8 } ], "statistics": { "participants_count": 5, "duration_minutes": 90, "speech_count": 32, "agenda_count": 8 } }, "decisions": { "items": [ { "id": "decision-001", "content": "Q4 마케팅 예산 30% 증액 승인 (총 3억 → 3.9억)", "decider": { "user_id": "user-001", "name": "김민준", "position": "마케팅 본부장" }, "decided_at": "2024-01-15T14:25:00Z", "background": "디지털 채널 성과가 예상을 상회하며, 경쟁사 대비 투자 비중이 낮아 시장 점유율 확대를 위해 예산 증액 필요", "meeting_section_id": "section-123", "related_todo_ids": ["todo-001", "todo-002"] } ], "total_count": 3 }, "todos": { "summary": { "total": 12, "not_started": 3, "in_progress": 6, "completed": 3 }, "groups": [ { "assignee": { "user_id": "user-002", "name": "박서연", "position": "디지털 마케팅 팀장" }, "todos": [ { "todo_id": "todo-001", "title": "인플루언서 후보 리스트 작성 및 제안서 준비", "progress": 75, "status": "in_progress", "due_date": "2024-01-20T23:59:59Z", "priority": "high", "meeting_section_id": "section-124", "last_updated_at": "2024-01-16T10:30:00Z" } ], "total_count": 4 } ] }, "references": { "related_meetings": { "items": [ { "meeting_id": "meeting-456", "title": "2024 Q3 마케팅 전략 회의", "date": "2023-12-20T14:00:00Z", "author": { "user_id": "user-001", "name": "김민준" }, "relevance_score": 92, "summary": "이전 분기 마케팅 전략 회의로, 디지털 채널 투자 확대 방향성이 처음 논의되었으며, 예산 증액 근거 자료로 활용 가능" } ], "total_count": 3 }, "project_documents": { "items": [ { "document_id": "doc-789", "type": "project", "title": "Q4 디지털 마케팅 프로젝트 기획서", "created_at": "2024-01-10T09:00:00Z", "author": { "user_id": "user-002", "name": "박서연" }, "relevance_score": 88, "summary": "Q4 디지털 채널 확대 계획 및 예산 배분 전략이 상세히 기술되어 있음" } ], "total_count": 5 }, "issues": { "items": [], "total_count": 0 }, "wiki_pages": { "items": [], "total_count": 0 } }, "generated_at": "2024-01-16T10:00:00Z" } ``` **Error Responses** ```json // 401 Unauthorized { "error": { "code": "UNAUTHORIZED", "message": "인증이 필요합니다." } } // 403 Forbidden { "error": { "code": "FORBIDDEN", "message": "이 회의록에 접근 권한이 없습니다." } } // 404 Not Found { "error": { "code": "MEETING_NOT_FOUND", "message": "회의를 찾을 수 없습니다." } } // 500 Internal Server Error { "error": { "code": "INTERNAL_SERVER_ERROR", "message": "서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요." } } ``` --- ### 2. 핵심내용 조회 #### 요청 ```http GET /api/v1/meetings/{meeting_id}/dashboard/key-points ``` **Path Parameters** | 이름 | 타입 | 필수 | 설명 | |------|------|------|------| | meeting_id | string (UUID) | Y | 회의 ID | #### 응답 **Success (200 OK)** ```json { "meeting_id": "uuid-1234", "points": [ { "id": "kp-001", "order": 1, "content": "Q4 마케팅 예산을 전년 대비 30% 증액하여 디지털 채널 확대에 집중하기로 결정", "meeting_section_id": "section-123", "timestamp": "2024-01-15T14:25:00Z" } ], "keywords": [ { "tag": "#디지털마케팅", "count": 15 } ], "statistics": { "participants_count": 5, "duration_minutes": 90, "speech_count": 32, "agenda_count": 8 }, "generated_at": "2024-01-16T10:00:00Z" } ``` --- ### 3. 결정사항 조회 #### 요청 ```http GET /api/v1/meetings/{meeting_id}/dashboard/decisions ``` **Path Parameters** | 이름 | 타입 | 필수 | 설명 | |------|------|------|------| | meeting_id | string (UUID) | Y | 회의 ID | **Query Parameters** | 이름 | 타입 | 필수 | 기본값 | 설명 | |------|------|------|--------|------| | page | integer | N | 1 | 페이지 번호 | | size | integer | N | 10 | 페이지 크기 (최대 50) | #### 응답 **Success (200 OK)** ```json { "meeting_id": "uuid-1234", "decisions": { "items": [ { "id": "decision-001", "content": "Q4 마케팅 예산 30% 증액 승인 (총 3억 → 3.9억)", "decider": { "user_id": "user-001", "name": "김민준", "position": "마케팅 본부장" }, "decided_at": "2024-01-15T14:25:00Z", "background": "디지털 채널 성과가 예상을 상회하며...", "meeting_section_id": "section-123", "related_todo_ids": ["todo-001", "todo-002"] } ], "pagination": { "current_page": 1, "total_pages": 1, "total_items": 3, "page_size": 10 } }, "generated_at": "2024-01-16T10:00:00Z" } ``` --- ### 4. Todo 진행상황 조회 #### 요청 ```http GET /api/v1/meetings/{meeting_id}/dashboard/todos ``` **Path Parameters** | 이름 | 타입 | 필수 | 설명 | |------|------|------|------| | meeting_id | string (UUID) | Y | 회의 ID | **Query Parameters** | 이름 | 타입 | 필수 | 기본값 | 설명 | |------|------|------|--------|------| | status | string | N | all | Todo 상태 필터 (all, not_started, in_progress, completed) | | assignee_id | string (UUID) | N | - | 담당자 ID 필터 | #### 응답 **Success (200 OK)** ```json { "meeting_id": "uuid-1234", "summary": { "total": 12, "not_started": 3, "in_progress": 6, "completed": 3 }, "groups": [ { "assignee": { "user_id": "user-002", "name": "박서연", "position": "디지털 마케팅 팀장" }, "todos": [ { "todo_id": "todo-001", "title": "인플루언서 후보 리스트 작성 및 제안서 준비", "progress": 75, "status": "in_progress", "due_date": "2024-01-20T23:59:59Z", "priority": "high", "meeting_section_id": "section-124", "last_updated_at": "2024-01-16T10:30:00Z" } ], "total_count": 4 } ], "generated_at": "2024-01-16T10:00:00Z" } ``` --- ### 5. 참고자료 조회 #### 요청 ```http GET /api/v1/meetings/{meeting_id}/dashboard/references ``` **Path Parameters** | 이름 | 타입 | 필수 | 설명 | |------|------|------|------| | meeting_id | string (UUID) | Y | 회의 ID | **Query Parameters** | 이름 | 타입 | 필수 | 기본값 | 설명 | |------|------|------|--------|------| | type | string | N | all | 참고자료 타입 (all, meetings, documents, issues, wiki) | | page | integer | N | 1 | 페이지 번호 | | size | integer | N | 5 | 페이지 크기 (최대 20) | #### 응답 **Success (200 OK)** ```json { "meeting_id": "uuid-1234", "type": "all", "related_meetings": { "items": [ { "meeting_id": "meeting-456", "title": "2024 Q3 마케팅 전략 회의", "date": "2023-12-20T14:00:00Z", "author": { "user_id": "user-001", "name": "김민준" }, "relevance_score": 92, "summary": "이전 분기 마케팅 전략 회의로..." } ], "pagination": { "current_page": 1, "total_pages": 1, "total_items": 3, "page_size": 5 } }, "project_documents": { "items": [], "pagination": { "current_page": 1, "total_pages": 0, "total_items": 0, "page_size": 5 } }, "issues": { "items": [], "pagination": { "current_page": 1, "total_pages": 0, "total_items": 0, "page_size": 5 } }, "wiki_pages": { "items": [], "pagination": { "current_page": 1, "total_pages": 0, "total_items": 0, "page_size": 5 } }, "generated_at": "2024-01-16T10:00:00Z" } ``` --- ## 데이터 모델 ### KeyPoint ```typescript interface KeyPoint { id: string; // 핵심 포인트 ID order: number; // 순서 (1, 2, 3...) content: string; // 핵심 내용 텍스트 meeting_section_id: string; // 회의록 섹션 ID (링크용) timestamp: string; // ISO 8601 형식 (언급 시간) } ``` ### Keyword ```typescript interface Keyword { tag: string; // 키워드 태그 (#디지털마케팅) count: number; // 언급 횟수 } ``` ### Statistics ```typescript interface Statistics { participants_count: number; // 참석자 수 duration_minutes: number; // 회의 시간 (분) speech_count: number; // 발언 횟수 agenda_count: number; // 주요 의제 수 } ``` ### Decision ```typescript interface Decision { id: string; // 결정사항 ID content: string; // 결정 내용 decider: User; // 결정자 정보 decided_at: string; // ISO 8601 형식 (결정 시간) background: string; // 결정 근거/배경 meeting_section_id: string; // 회의록 섹션 ID related_todo_ids: string[]; // 관련 Todo ID 배열 } ``` ### User ```typescript interface User { user_id: string; // 사용자 ID name: string; // 이름 position?: string; // 직책 (선택) } ``` ### TodoSummary ```typescript interface TodoSummary { total: number; // 전체 Todo 수 not_started: number; // 시작 전 수 in_progress: number; // 진행 중 수 completed: number; // 완료 수 } ``` ### TodoGroup ```typescript interface TodoGroup { assignee: User; // 담당자 정보 todos: Todo[]; // Todo 배열 total_count: number; // 담당자의 전체 Todo 수 } ``` ### Todo ```typescript interface Todo { todo_id: string; // Todo ID title: string; // Todo 제목 progress: number; // 진행률 (0-100) status: string; // 상태 (not_started, in_progress, completed) due_date: string; // ISO 8601 형식 (마감일) priority: string; // 우선순위 (low, medium, high, urgent) meeting_section_id: string; // 회의록 섹션 ID last_updated_at: string; // ISO 8601 형식 (최종 업데이트 시간) } ``` ### Reference ```typescript interface Reference { id: string; // 참고자료 ID type: string; // 타입 (meeting, document, issue, wiki) title: string; // 제목 date?: string; // ISO 8601 형식 (날짜) created_at?: string; // ISO 8601 형식 (생성일) author: User; // 작성자 relevance_score: number; // 관련도 점수 (0-100) summary: string; // 요약 (100자 이내) } ``` --- ## 캐싱 전략 ### Redis 캐싱 **대시보드 전체 데이터** - Key: `dashboard:meeting:{meeting_id}` - TTL: 30분 - 캐시 무효화: 회의록 수정, Todo 업데이트 시 **핵심내용** - Key: `dashboard:keypoints:{meeting_id}` - TTL: 1시간 - 캐시 무효화: 회의록 수정 시 **Todo 진행상황** - Key: `dashboard:todos:{meeting_id}` - TTL: 5분 (실시간 업데이트) - 캐시 무효화: Todo 상태 변경 시 **참고자료** - Key: `dashboard:references:{meeting_id}:{type}` - TTL: 24시간 - 캐시 무효화: 매일 자동 업데이트 --- ## 성능 최적화 ### 응답 시간 목표 - 대시보드 전체 조회: < 500ms - 개별 섹션 조회: < 200ms ### 최적화 전략 1. **병렬 처리** - 각 섹션(핵심내용, 결정사항, Todo, 참고자료)을 병렬로 조회 - Promise.all 활용 2. **데이터 선택적 로딩** - `include` 파라미터로 필요한 섹션만 조회 - 프론트엔드에서 탭 전환 시 필요한 데이터만 요청 3. **페이지네이션** - 결정사항, 참고자료에 페이지네이션 적용 - 대량 데이터 조회 시 성능 저하 방지 4. **인덱싱** - meeting_id, user_id, status 등 주요 필드에 인덱스 생성 --- ## 보안 ### 인증 및 권한 **인증 방식** - JWT Bearer Token 인증 **권한 검증** - 회의 참석자 또는 조직 멤버만 조회 가능 - 회의록 공유 권한 설정 준수 ### Rate Limiting ``` - 사용자당: 100 requests/minute - IP당: 200 requests/minute ``` --- ## 에러 코드 | HTTP Status | Error Code | 설명 | |-------------|-----------|------| | 400 | INVALID_PARAMETER | 잘못된 파라미터 | | 401 | UNAUTHORIZED | 인증 필요 | | 403 | FORBIDDEN | 권한 없음 | | 404 | MEETING_NOT_FOUND | 회의를 찾을 수 없음 | | 404 | DASHBOARD_NOT_GENERATED | 대시보드 미생성 (회의록 미확정) | | 429 | RATE_LIMIT_EXCEEDED | 요청 한도 초과 | | 500 | INTERNAL_SERVER_ERROR | 서버 오류 | | 503 | SERVICE_UNAVAILABLE | 서비스 일시 중단 | --- ## 테스트 시나리오 ### 1. 정상 케이스 **시나리오**: 회의록 확정 후 대시보드 조회 1. 회의록 확정 2. AI가 대시보드 데이터 생성 (핵심내용, 결정사항 추출) 3. `GET /api/v1/meetings/{meeting_id}/dashboard` 호출 4. 200 OK 응답 확인 5. 모든 섹션 데이터 포함 확인 ### 2. 캐싱 테스트 **시나리오**: 동일 대시보드 연속 조회 1. 첫 번째 조회 (DB 조회) 2. 두 번째 조회 (캐시 조회) 3. 응답 시간 비교 (캐시 조회가 50% 이상 빠름) ### 3. 실시간 업데이트 테스트 **시나리오**: Todo 진행상황 실시간 반영 1. 대시보드 조회 2. Todo 진행률 업데이트 (75% → 100%) 3. 대시보드 재조회 4. 변경된 진행률 확인 ### 4. 에러 케이스 **시나리오**: 권한 없는 사용자 접근 1. 다른 사용자 계정으로 로그인 2. 회의 ID로 대시보드 조회 3. 403 Forbidden 응답 확인 --- ## 변경 이력 | 버전 | 날짜 | 작성자 | 변경 내용 | |------|------|--------|-----------| | 1.0 | 2024-01-15 | 이준호 | 회의록별 대시보드 API 초안 작성 |