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

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

최적화 전략

  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 초안 작성