hgzero/claude/SCHEMA-REPORT-SUMMARY.md

15 KiB
Raw Blame History

Meeting Service 데이터베이스 스키마 분석 최종 보고서

작성일: 2025-10-28
분석 대상: Meeting Service (feat/meeting-ai 브랜치)
분석 범위: 마이그레이션 V1~V4, 엔티티 구조, 데이터 플로우


Executive Summary

핵심 발견사항

  1. minutes 테이블에 content 필드가 없음

    • 실제 회의록 내용은 minutes_sections.content에 저장
    • minutes 테이블은 메타데이터만 보유 (title, status, version 등)
  2. 사용자별 회의록 완벽하게 지원 (V3)

    • minutes.user_id = NULL: AI 통합 회의록
    • minutes.user_id = 참석자ID: 개인별 회의록
    • 인덱스: idx_minutes_meeting_user (meeting_id, user_id)
  3. AI 분석 결과 구조화 저장 (V3, V4)

    • agenda_sections: 안건별 구조화된 요약
    • ai_summaries: AI 처리 결과 캐싱
    • todos (V4): 각 안건의 JSON으로 저장
  4. 정규화 완료 (V2)

    • meetings.participants (CSV) → meeting_participants (테이블)
    • 복합 PK: (meeting_id, user_id)

데이터베이스 구조 개요

테이블 분류

핵심 테이블 (V1):

  • meetings: 회의 기본 정보
  • minutes: 회의록 메타데이터
  • minutes_sections: 회의록 섹션 (실제 내용)

참석자 관리 (V2):

  • meeting_participants: 회의 참석자 정보

AI 분석 (V3):

  • agenda_sections: 안건별 AI 요약
  • ai_summaries: AI 처리 결과 캐시
  • todos: Todo 아이템 (expanded)

1. 핵심 테이블별 상세 분석

1.1 meetings (회의 기본 정보)

구성:

  • PK: meeting_id (VARCHAR(50))
  • 주요 필드: title, purpose, description
  • 상태: SCHEDULED → IN_PROGRESS → COMPLETED
  • 시간: scheduled_at, started_at, ended_at (V3)

중요 변경:

  • V3에서 ended_at 추가
  • 회의 정확한 종료 시간 기록
-- 조회 예시
SELECT * FROM meetings 
WHERE status = 'COMPLETED' 
  AND ended_at >= NOW() - INTERVAL '7 days'
ORDER BY ended_at DESC;

1.2 minutes (회의록 메타데이터)

구성:

minutes_id (PK)
├─ meeting_id (FK)
├─ user_id (V3) ← NULL: AI 통합 회의록 / NOT NULL: 개인 회의록
├─ title
├─ status (DRAFT, FINALIZED)
├─ version
├─ created_by, finalized_by
└─ created_at, finalized_at

중요:

  • content 필드 없음 → minutes_sections에 저장
  • 메타데이터만 관리 (생성자, 확정자, 버전 등)

쿼리 패턴:

-- AI 통합 회의록
SELECT * FROM minutes 
WHERE meeting_id = ? AND user_id IS NULL;

-- 특정 사용자의 회의록
SELECT * FROM minutes 
WHERE meeting_id = ? AND user_id = ?;

-- 복합 인덱스 활용: idx_minutes_meeting_user

1.3 minutes_sections (회의록 섹션 - 실제 내용)

구성:

section_id (PK)
├─ minutes_id (FK) ← 어느 회의록에 속하는가
├─ type (AGENDA, DISCUSSION, DECISION, ACTION_ITEM)
├─ title
├─ content ← ★ 실제 회의록 내용
├─ order
├─ verified (검증 완료)
├─ locked (수정 불가)
└─ locked_by

핵심 특성:

  • content: 사용자가 작성한 실제 내용
  • locked: finalize_minutes 호출시 잠금
  • verified: 확정시 TRUE로 설정

데이터 흐름:

1. CreateMinutes → minutes_sections 초기 생성
2. UpdateMinutes → content 저장 (여러 번)
3. FinalizeMinutes → locked=TRUE, verified=TRUE
4. (locked 상태에서 수정 불가)

1.4 agenda_sections (AI 요약 - V3)

구성:

id (PK, UUID)
├─ minutes_id (FK) ← 통합 회의록만 (user_id=NULL)
├─ meeting_id (FK)
├─ agenda_number (1, 2, 3...)
├─ agenda_title
├─ ai_summary_short (1줄 요약)
├─ discussions (3-5문장 논의)
├─ decisions (JSON 배열)
├─ pending_items (JSON 배열)
├─ opinions (JSON 배열: {speaker, opinion})
└─ todos (JSON 배열 [V4])

V4 추가 사항:

  • todos JSON 필드 추가
  • 안건별 추출된 Todo 저장
{
  "title": "시장 조사 보고서 작성",
  "assignee": "김민준",
  "dueDate": "2025-02-15",
  "description": "20-30대 타겟 시장 조사",
  "priority": "HIGH"
}

중요:

  • 통합 회의록만 분석 (user_id=NULL인 것)
  • 참석자별 회의록(user_id NOT NULL)은 AI 분석 대상 아님
  • minutes_id로 통합 회의록 참조

1.5 minutes_sections vs agenda_sections

항목 minutes_sections agenda_sections
용도 사용자 작성 AI 요약
모든 회의록 ✓ 통합 + 개인 ✗ 통합만
구조 순차적 섹션 안건별 구조화
내용 저장 content (TEXT) JSON 필드들
관계 1:N (minutes과) N:1 (minutes과)
목적 기록 분석/요약

생성 흐름:

회의 시작
  ↓
minutes 생성 (통합 + 개인)
  ↓
minutes_sections 생성 (4개 그룹)
  ↓
사용자 작성 중...
  ↓
회의 종료 → FinalizeMinutes
  ↓
minutes_sections locked
  ↓
AI 분석 (비동기)
  ↓
agenda_sections 생성 (통합 회의록 기반)

1.6 ai_summaries (AI 처리 결과 - V3)

구성:

id (PK, UUID)
├─ meeting_id (FK)
├─ summary_type (CONSOLIDATED, TODO_EXTRACTION)
├─ source_minutes_ids (JSON: 사용된 회의록 ID 배열)
├─ result (JSON: AI 응답 전체)
├─ processing_time_ms (처리 시간)
├─ model_version (claude-3.5-sonnet)
├─ keywords (JSON: 키워드 배열)
└─ statistics (JSON: {participants, agendas, todos})

용도:

  • AI 처리 결과 캐싱
  • 재처리 필요시 참조
  • 성능 통계 기록

1.7 todos (Todo 아이템)

기본 구조:

todo_id (PK)
├─ meeting_id (FK)
├─ minutes_id (FK)
├─ title
├─ description
├─ assignee_id
├─ due_date
├─ status (PENDING, COMPLETED)
├─ priority (HIGH, MEDIUM, LOW)
└─ completed_at

V3 추가 필드:

├─ extracted_by (AI / MANUAL) ← AI 자동 추출 vs 수동
├─ section_reference (안건 참조)
└─ extraction_confidence (0.00~1.00) ← AI 신뢰도

저장 전략:

  1. agenda_sections.todos (JSON): 간단한 Todo, 기본 저장 위치
  2. todos 테이블: 상세 관리 필요시만 추가 저장

1.8 meeting_participants (참석자 관리 - V2)

구성:

PK: (meeting_id, user_id)
├─ invitation_status (PENDING, ACCEPTED, DECLINED)
├─ attended (BOOLEAN)
└─ created_at, updated_at

V2 개선:

  • 이전: meetings.participants (CSV 문자열)
  • 현재: 별도 테이블 (정규화)
  • 복합 PK로 중복 방지

2. 회의록 작성 플로우 (전체)

단계별 데이터 변화

PHASE 1: 회의 준비
═════════════════════════════════════════════
1. CreateMeeting
   → INSERT meetings (status='SCHEDULED')
   → INSERT meeting_participants (5명)

PHASE 2: 회의 진행
═════════════════════════════════════════════
2. StartMeeting
   → UPDATE meetings SET status='IN_PROGRESS'

3. CreateMinutes (회의 중)
   → INSERT minutes (user_id=NULL) × 1 (통합)
   → INSERT minutes (user_id=user_id) × 5 (개인)
   → INSERT minutes_sections (초기 생성)

4. UpdateMinutes (여러 번)
   → UPDATE minutes_sections SET content='...'

PHASE 3: 회의 종료
═════════════════════════════════════════════
5. EndMeeting
   → UPDATE meetings SET 
       status='COMPLETED', 
       ended_at=NOW() [V3]

PHASE 4: 회의록 최종화
═════════════════════════════════════════════
6. FinalizeMinutes
   → UPDATE minutes SET 
       status='FINALIZED'
   → UPDATE minutes_sections SET 
       locked=TRUE, 
       verified=TRUE

PHASE 5: AI 분석 (비동기)
═════════════════════════════════════════════
7. MinutesAnalysisEventConsumer
   → Read minutes (user_id=NULL)
   → Read minutes_sections
   → Call AI Service
   → INSERT agenda_sections [V3]
   → INSERT ai_summaries [V3]
   → INSERT todos [V3 확장]

3. 사용자별 회의록 구조

데이터 분리 방식

1개 회의 (참석자 5명):

meetings: 1개
  ├─ meeting_id = 'meeting-001'
  └─ status = COMPLETED

meeting_participants: 5개
  ├─ (meeting-001, user1@example.com)
  ├─ (meeting-001, user2@example.com)
  ├─ (meeting-001, user3@example.com)
  ├─ (meeting-001, user4@example.com)
  └─ (meeting-001, user5@example.com)

minutes: 6개 [V3]
  ├─ (id=consol-1, meeting_id=meeting-001, user_id=NULL)
  │   → 통합 회의록 (AI 분석 대상)
  ├─ (id=user1-min, meeting_id=meeting-001, user_id=user1@example.com)
  │   → 사용자1 개인 회의록
  ├─ (id=user2-min, meeting_id=meeting-001, user_id=user2@example.com)
  │   → 사용자2 개인 회의록
  ├─ ... (user3, user4, user5)
  └─

minutes_sections: 수십 개 (6개 회의록 × N개 섹션)
  ├─ Group 1: consol-1의 섹션들 (AI 작성)
  ├─ Group 2: user1-min의 섹션들 (사용자1 작성)
  ├─ Group 3: user2-min의 섹션들 (사용자2 작성)
  └─ ... (user3, user4, user5)

agenda_sections: 5개 [V3]
  ├─ (id=ag-1, minutes_id=consol-1, agenda_number=1)
  ├─ (id=ag-2, minutes_id=consol-1, agenda_number=2)
  └─ ... (3, 4, 5)

핵심:

  • 참석자별 회의록은 minutes.user_id로 구분
  • 인덱스 활용: idx_minutes_meeting_user
  • AI 분석: 통합 회의록만 (user_id=NULL)

4. 마이그레이션 변경사항 요약

V2 (2025-10-27)

-- meeting_participants 테이블 생성
CREATE TABLE meeting_participants (
  meeting_id, user_id (복합 PK),
  invitation_status, attended
)

-- 데이터 마이그레이션
SELECT TRIM(participant) FROM meetings.participants (CSV)
   INSERT INTO meeting_participants

-- meetings.participants 컬럼 삭제
ALTER TABLE meetings DROP COLUMN participants

영향: 정규화 완료, 중복 데이터 제거


V3 (2025-10-28)

3-1. minutes 테이블 확장

ALTER TABLE minutes ADD COLUMN user_id VARCHAR(100);
CREATE INDEX idx_minutes_meeting_user ON minutes(meeting_id, user_id);

의미: 사용자별 회의록 지원


3-2. agenda_sections 테이블 신규

CREATE TABLE agenda_sections (
  id, minutes_id, meeting_id,
  agenda_number, agenda_title,
  ai_summary_short, discussions,
  decisions (JSON),
  pending_items (JSON),
  opinions (JSON)
)

의미: AI 요약을 구조화된 형식으로 저장


3-3. ai_summaries 테이블 신규

CREATE TABLE ai_summaries (
  id, meeting_id, summary_type,
  source_minutes_ids (JSON),
  result (JSON),
  processing_time_ms,
  model_version,
  keywords (JSON),
  statistics (JSON)
)

의미: AI 처리 결과 캐싱


3-4. todos 테이블 확장

ALTER TABLE todos ADD COLUMN extracted_by VARCHAR(50) DEFAULT 'AI';
ALTER TABLE todos ADD COLUMN section_reference VARCHAR(200);
ALTER TABLE todos ADD COLUMN extraction_confidence DECIMAL(3,2);

의미: AI 자동 추출 추적


V4 (2025-10-28)

ALTER TABLE agenda_sections ADD COLUMN todos JSON;

의미: 안건별 Todo를 JSON으로 저장


5. 성능 최적화

현재 인덱스

meetings:
  ├─ PK: meeting_id

minutes:
  ├─ PK: id
  └─ idx_minutes_meeting_user (meeting_id, user_id) [V3]

minutes_sections:
  ├─ PK: id
  └─ (minutes_id로 FK 지원)

agenda_sections: [V3]
  ├─ PK: id
  ├─ idx_sections_meeting (meeting_id)
  ├─ idx_sections_agenda (meeting_id, agenda_number)
  └─ idx_sections_minutes (minutes_id)

ai_summaries: [V3]
  ├─ PK: id
  ├─ idx_summaries_meeting (meeting_id)
  ├─ idx_summaries_type (meeting_id, summary_type)
  └─ idx_summaries_created (created_at)

todos:
  ├─ PK: todo_id
  ├─ idx_todos_extracted (extracted_by) [V3]
  └─ idx_todos_meeting (meeting_id) [V3]

meeting_participants: [V2]
  ├─ PK: (meeting_id, user_id)
  ├─ idx_user_id (user_id)
  └─ idx_invitation_status (invitation_status)

추천 추가 인덱스

-- 자주 조회하는 패턴
CREATE INDEX idx_minutes_status_created 
  ON minutes(status, created_at DESC);

CREATE INDEX idx_agenda_meeting_created 
  ON agenda_sections(meeting_id, created_at DESC);

CREATE INDEX idx_todos_meeting_assignee 
  ON todos(meeting_id, assignee_id);

6. 핵심 질문 답변

Q1: minutes 테이블에 content 필드가 있는가?

A: 없음

  • minutes: 메타데이터만 (title, status, version 등)
  • 실제 내용: minutes_sections.content

Q2: minutes_section과 agenda_sections의 차이점?

A:

항목 minutes_sections agenda_sections
목적 사용자 작성 AI 요약
모든 회의록 O X (통합만)
내용 저장 content (TEXT) JSON

Q3: 사용자별 회의록 저장 방식?

A:

  • minutes.user_id로 구분
  • NULL: AI 통합회의록
  • NOT NULL: 개인별 회의록
  • 인덱스: idx_minutes_meeting_user

Q4: V3, V4 주요 변경?

A:

  • V3: user_id, agenda_sections, ai_summaries, todos 확장
  • V4: agenda_sections.todos JSON 추가

7. 개발 시 주의사항

Do's ✓

  • minutes_sections.content에 실제 내용 저장
  • AI 분석시 user_id=NULL인 minutes만 처리
  • agenda_sections.todos와 todos 테이블 동시 저장 (필요시)
  • 복합 인덱스 활용 (meeting_id, user_id)

Don'ts ✗

  • minutes 테이블에 content 저장 (없음)
  • 참석자별 회의록(user_id NOT NULL)을 AI 분석 (통합만)
  • agenda_sections를 모든 minutes에 생성 (통합만)
  • 인덱스 무시한 풀 스캔

8. 파일 위치 및 참조

마이그레이션 파일:

  • /Users/jominseo/HGZero/meeting/src/main/resources/db/migration/V2__*.sql
  • /Users/jominseo/HGZero/meeting/src/main/resources/db/migration/V3__*.sql
  • /Users/jominseo/HGZero/meeting/src/main/resources/db/migration/V4__*.sql

엔티티:

  • MeetingEntity, MinutesEntity, MinutesSectionEntity
  • AgendaSectionEntity, TodoEntity, MeetingParticipantEntity

서비스:

  • MinutesService, MinutesSectionService
  • MinutesAnalysisEventConsumer (비동기)

9. 결론

핵심 설계 원칙

  1. 메타데이터 vs 내용 분리: minutes (메타) vs minutes_sections (내용)
  2. 사용자별 격리: user_id 컬럼으로 개인 회의록 관리
  3. AI 결과 구조화: JSON으로 유연성과 성능 확보
  4. 정규화 완료: 참석자 정보 테이블화

검증 사항

  • V3, V4 마이그레이션 정상 적용
  • 모든 인덱스 생성됨
  • 관계 설정 정상 (FK, 1:N)

다음 단계

  • 성능 모니터링 (쿼리 실행 계획)
  • 추가 인덱스 검토
  • AI 분석 결과 검증
  • 참석자별 회의록 사용성 테스트

문서 정보:

  • 작성자: Database Architecture Analysis
  • 대상 서비스: Meeting Service (AI 통합 회의록)
  • 최종 버전: 2025-10-28