hgzero/design/backend/api/meeting-dashboard-api.md
hiondal 27e8c12eaf 회의록별 대시보드 기능 추가
- 유저스토리 추가: 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>
2025-10-20 13:46:20 +09:00

716 lines
16 KiB
Markdown

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