# 회의록별 대시보드 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 초안 작성 |

