mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 16:06:23 +00:00
- 유저스토리 추가: UFR-MEET-070 [회의록대시보드]
- 핵심내용, 결정사항, Todo 진행상황, 참고자료 4개 섹션 정의
- AI, RAG, Todo 서비스 연동 명시
- 복잡도: M/21
- UI/UX 설계서 작성 (design/uiux/uiux.md)
- 회의록별 대시보드 화면 구조 상세 설계
- 5개 주요 영역: Header, 핵심내용, 결정사항, Todo 진행상황, 참고자료
- 스타일 시스템 정의 (색상, 타이포그래피, 간격, 반응형)
- WCAG 2.1 Level AA 접근성 가이드라인
- 인터랙션 및 데이터 요구사항 명세
- 에러 처리 및 성능 최적화 방안
- API 설계서 작성 (design/backend/api/meeting-dashboard-api.md)
- 5개 주요 엔드포인트 정의
- 대시보드 전체 조회
- 핵심내용 조회
- 결정사항 조회 (페이지네이션)
- Todo 진행상황 조회 (필터링)
- 참고자료 조회 (타입별 필터, 페이지네이션)
- 데이터 모델 정의 (TypeScript Interface)
- Redis 캐싱 전략 (섹션별 TTL 설정)
- 성능 최적화 (병렬 처리, 선택적 로딩, 인덱싱)
- 보안 (JWT 인증, Rate Limiting)
- 에러 코드 및 테스트 시나리오
차별화 포인트:
- 회의 결과를 한눈에 파악할 수 있는 통합 뷰
- Todo 진행상황 실시간 업데이트 (WebSocket)
- 관련 회의록 및 업무 이력 자동 연결 (RAG)
- Mobile First 반응형 설계
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
16 KiB
16 KiB
회의록별 대시보드 API 설계서
개요
목적
회의록이 확정된 후 회의 결과를 한눈에 파악할 수 있는 대시보드 데이터를 제공하는 API
버전
- API Version: v1.0
- 작성일: 2024-01-15
- 작성자: 이준호 (Backend Developer)
관련 유저스토리
- UFR-MEET-070: [회의록대시보드] 회의록 작성자로서 | 나는, 회의 결과를 한눈에 파악하기 위해 | 회의록별 대시보드를 통해 핵심 정보를 조회하고 싶다.
API 엔드포인트
1. 대시보드 전체 데이터 조회
요청
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
Authorization: Bearer {access_token}
Content-Type: application/json
응답
Success (200 OK)
{
"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
// 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. 핵심내용 조회
요청
GET /api/v1/meetings/{meeting_id}/dashboard/key-points
Path Parameters
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
| meeting_id | string (UUID) | Y | 회의 ID |
응답
Success (200 OK)
{
"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. 결정사항 조회
요청
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)
{
"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 진행상황 조회
요청
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)
{
"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. 참고자료 조회
요청
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)
{
"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
interface KeyPoint {
id: string; // 핵심 포인트 ID
order: number; // 순서 (1, 2, 3...)
content: string; // 핵심 내용 텍스트
meeting_section_id: string; // 회의록 섹션 ID (링크용)
timestamp: string; // ISO 8601 형식 (언급 시간)
}
Keyword
interface Keyword {
tag: string; // 키워드 태그 (#디지털마케팅)
count: number; // 언급 횟수
}
Statistics
interface Statistics {
participants_count: number; // 참석자 수
duration_minutes: number; // 회의 시간 (분)
speech_count: number; // 발언 횟수
agenda_count: number; // 주요 의제 수
}
Decision
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
interface User {
user_id: string; // 사용자 ID
name: string; // 이름
position?: string; // 직책 (선택)
}
TodoSummary
interface TodoSummary {
total: number; // 전체 Todo 수
not_started: number; // 시작 전 수
in_progress: number; // 진행 중 수
completed: number; // 완료 수
}
TodoGroup
interface TodoGroup {
assignee: User; // 담당자 정보
todos: Todo[]; // Todo 배열
total_count: number; // 담당자의 전체 Todo 수
}
Todo
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
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
최적화 전략
-
병렬 처리
- 각 섹션(핵심내용, 결정사항, Todo, 참고자료)을 병렬로 조회
- Promise.all 활용
-
데이터 선택적 로딩
include파라미터로 필요한 섹션만 조회- 프론트엔드에서 탭 전환 시 필요한 데이터만 요청
-
페이지네이션
- 결정사항, 참고자료에 페이지네이션 적용
- 대량 데이터 조회 시 성능 저하 방지
-
인덱싱
- 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. 정상 케이스
시나리오: 회의록 확정 후 대시보드 조회
- 회의록 확정
- AI가 대시보드 데이터 생성 (핵심내용, 결정사항 추출)
GET /api/v1/meetings/{meeting_id}/dashboard호출- 200 OK 응답 확인
- 모든 섹션 데이터 포함 확인
2. 캐싱 테스트
시나리오: 동일 대시보드 연속 조회
- 첫 번째 조회 (DB 조회)
- 두 번째 조회 (캐시 조회)
- 응답 시간 비교 (캐시 조회가 50% 이상 빠름)
3. 실시간 업데이트 테스트
시나리오: Todo 진행상황 실시간 반영
- 대시보드 조회
- Todo 진행률 업데이트 (75% → 100%)
- 대시보드 재조회
- 변경된 진행률 확인
4. 에러 케이스
시나리오: 권한 없는 사용자 접근
- 다른 사용자 계정으로 로그인
- 회의 ID로 대시보드 조회
- 403 Forbidden 응답 확인
변경 이력
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| 1.0 | 2024-01-15 | 이준호 | 회의록별 대시보드 API 초안 작성 |