외부/내부 시퀀스 설계 일관성 개선 및 표준화

주요 변경사항:

[Critical]
- API 엔드포인트 통일: POST /api/minutes/{minutesId}/finalize
- 이벤트 이름 표준화: MinutesFinalized

[Warning]
- API Gateway 라우팅 규칙 문서화 (외부 시퀀스 7개 파일)
- 대시보드 API 경로 통일: GET /api/dashboard
- AI 제안 병합 프로세스 상세 문서화
- 회의록 확정 검증 로직 5단계 상세화

[Minor]
- Redis 캐시 TTL 명시 (7개 파일, TTL 정책 표준화)
- 대시보드 페이지네이션 파라미터 추가
- 에러 응답 포맷 표준화 (14개 에러 응답)

총 31개 파일 수정, 34건의 개선 사항 적용

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ondal 2025-10-23 09:48:06 +09:00
parent f86973c93b
commit 715add4dbc
43 changed files with 505 additions and 156 deletions

View File

@ -0,0 +1 @@
404: Not Found

View File

@ -11,6 +11,8 @@
- **외부시퀀스설계서에서 설계한 플로우와 일치**해야 함
- UI/UX설계서의 '사용자 플로우'참조하여 설계
- 마이크로서비스 내부의 처리 흐름을 표시
- 요청/응답을 **한글로 표시**
- Repository CRUD 처리를 한글로 설명하고 SQL은 사용하지 말것
- **각 서비스-시나리오별로 분리하여 각각 작성**
- 각 서비스별 주요 시나리오마다 독립적인 시퀀스 설계 수행
- 프론트엔드와 백엔드 책임 분리: 프론트엔드에서 할 수 있는 것은 백엔드로 요청 안하게 함

View File

@ -27,7 +27,7 @@ activate Service
Service -> Repo: getFinalTranscript(meetingId)
activate Repo
Repo -> DB: SELECT content FROM ai_transcripts\nWHERE meeting_id = {meetingId}\nORDER BY created_at DESC LIMIT 1
Repo -> DB: 최종 회의록 조회
activate DB
DB --> Repo: 최종 회의록 내용
@ -41,7 +41,7 @@ Service -> Service: 참석자 정보 조회 준비
Service -> Repo: getMeetingParticipants(meetingId)
activate Repo
Repo -> DB: SELECT participants FROM meeting_context
Repo -> DB: 참석자 정보 조회
activate DB
DB --> Repo: 참석자 목록
@ -126,7 +126,7 @@ loop 각 Todo 항목마다
Service -> Repo: saveTodo(meetingId, todoData)
activate Repo
Repo -> DB: INSERT INTO ai_extracted_todos
Repo -> DB: Todo 정보 저장
activate DB
note right
저장 데이터:
@ -195,7 +195,7 @@ deactivate MeetingClient
Service -> Repo: updateExtractionStatus(meetingId, "COMPLETED")
activate Repo
Repo -> DB: UPDATE ai_task_status\nSET status = 'COMPLETED',\ncompleted_at = NOW()
Repo -> DB: 추출 상태 업데이트
activate DB
DB --> Repo: 업데이트 완료

View File

@ -26,7 +26,7 @@ activate Service
Service -> TranscriptRepo: getMeetingContext(meetingId)
activate TranscriptRepo
TranscriptRepo -> DB: SELECT meeting_info, participants\nFROM meeting_context
TranscriptRepo -> DB: 회의 맥락 조회\n(회의정보, 참석자)
activate DB
DB --> TranscriptRepo: 회의 정보

View File

@ -28,7 +28,7 @@ activate Service
Service -> Repo: getTranscriptInfo(transcriptId)
activate Repo
Repo -> DB: SELECT content, meeting_id, created_at\nFROM ai_transcripts\nWHERE id = {transcriptId}
Repo -> DB: 회의록 정보 조회
activate DB
DB --> Repo: 회의록 정보
@ -153,7 +153,7 @@ loop 각 후보 회의록마다
Service -> Repo: getTranscriptDetails(candidateId)
activate Repo
Repo -> DB: SELECT participants, keywords, created_at
Repo -> DB: 회의록 상세 정보 조회
activate DB
DB --> Repo: 상세 정보
@ -188,7 +188,7 @@ loop 선택된 상위 5개
Service -> Repo: saveRelatedTranscript(transcriptId, relatedId, score, keywords)
activate Repo
Repo -> DB: INSERT INTO related_transcripts
Repo -> DB: 관련 회의록 연결 저장
activate DB
note right
저장 데이터:

View File

@ -26,7 +26,7 @@ activate Service
Service -> TranscriptRepo: getMeetingContext(meetingId)
activate TranscriptRepo
TranscriptRepo -> DB: SELECT meeting_info, agenda, participants\nFROM meeting_context
TranscriptRepo -> DB: 회의 맥락 정보 조회\n(회의정보, 안건, 참석자)
activate DB
DB --> TranscriptRepo: 회의 정보
@ -38,7 +38,7 @@ deactivate TranscriptRepo
Service -> TranscriptRepo: getPreviousDiscussions(meetingId)
activate TranscriptRepo
TranscriptRepo -> DB: SELECT discussed_topics\nFROM ai_transcripts\nWHERE meeting_id = {meetingId}
TranscriptRepo -> DB: 이미 논의한 주제 조회\n(회의ID 기준)
activate DB
DB --> TranscriptRepo: 이미 논의한 주제 목록

View File

@ -33,7 +33,7 @@ activate Service
Service -> Repo: getTermInfo(term)
activate Repo
Repo -> DB: SELECT definition, category\nFROM term_dictionary\nWHERE term = {term}
Repo -> DB: 용어 정보 조회\n(용어사전에서 정의 및 카테고리)
activate DB
DB --> Repo: 기본 용어 정의

View File

@ -30,7 +30,7 @@ par "조직별 용어 사전"
Service -> Repo: getOrganizationTerms(organizationId)
activate Repo
Repo -> DB: SELECT term, definition, category\nFROM term_dictionary\nWHERE org_id = {organizationId}
Repo -> DB: 조직 전문용어 조회\n(조직ID 기준, 용어/정의/카테고리)
activate DB
DB --> Repo: 조직 전문용어 목록
@ -43,7 +43,7 @@ else
Service -> Repo: getIndustryTerms(industry)
activate Repo
Repo -> DB: SELECT term, definition, category\nFROM standard_terms\nWHERE industry = {industry}
Repo -> DB: 산업 표준용어 조회\n(산업분류 기준, 용어/정의/카테고리)
activate DB
DB --> Repo: 산업 표준용어 목록

View File

@ -30,7 +30,7 @@ activate Service
Service -> Repo: getOriginalTranscript(meetingId)
activate Repo
Repo -> DB: SELECT content, version\nFROM ai_transcripts\nWHERE meeting_id = {meetingId}\nAND status = 'FINALIZED'\nORDER BY created_at DESC LIMIT 1
Repo -> DB: 원본 회의록 조회
activate DB
DB --> Repo: 원본 회의록 내용
@ -146,7 +146,7 @@ end note
Service -> Repo: saveImprovedTranscript(meetingId, improvedContent, metadata)
activate Repo
Repo -> DB: INSERT INTO ai_transcripts
Repo -> DB: 개선된 회의록 저장
activate DB
note right
저장 데이터:
@ -172,7 +172,7 @@ deactivate Repo
Service -> Repo: linkVersions(originalId, improvedId)
activate Repo
Repo -> DB: INSERT INTO transcript_versions
Repo -> DB: 버전 연결 정보 저장
activate DB
note right
버전 연결 정보:

View File

@ -30,7 +30,7 @@ Service -> Service: 회의 맥락 정보 조회 준비
par 회의 정보 조회
Service -> Repo: getMeetingContext(meetingId)
activate Repo
Repo -> DB: SELECT meeting_info
Repo -> DB: 회의 맥락 정보 조회
activate DB
DB --> Repo: 회의 정보 반환
deactivate DB
@ -39,7 +39,7 @@ par 회의 정보 조회
else 이전 내용 조회
Service -> Repo: getPreviousTranscripts(meetingId)
activate Repo
Repo -> DB: SELECT previous_content
Repo -> DB: 이전 회의록 내용 조회
activate DB
DB --> Repo: 이전 회의록
deactivate DB
@ -82,7 +82,7 @@ Service -> Service: 회의록 데이터 구조화
Service -> Repo: saveTranscriptDraft(meetingId, content)
activate Repo
Repo -> DB: INSERT INTO ai_transcripts
Repo -> DB: AI 회의록 초안 저장 (상태: DRAFT)
activate DB
note right
저장 데이터:
@ -222,6 +222,30 @@ note right
* decisions: []
end note
note over Controller, DB
AI 제안 병합 프로세스:
1. 자동 병합 (Auto-merge):
- confidence >= 0.8인 결정사항
→ 회의록 "결정사항" 섹션에 자동 추가
- priority: HIGH인 논의사항
→ "추가 논의 필요" 섹션에 자동 반영
2. 사용자 검토 (User Review):
- 0.5 <= confidence < 0.8
→ 제안 목록에 표시, 사용자 승인 대기
- priority: MEDIUM/LOW
→ 선택적 반영 가능
3. 병합 시점:
- 실시간: 회의 진행 중 자동 반영
- 최종: 회의록 확정 전 사용자 최종 검토
4. 병합 이력:
- 제안 ID, 병합 여부, 승인자, 시각 기록
- 감사 추적(Audit Trail) 유지
end note
note over Controller, DB
처리 시간:
- 맥락 조회: 100-200ms

View File

@ -29,7 +29,7 @@ activate Service
' Todo 정보 조회
Service -> TodoRepo: findById(todoId)
activate TodoRepo
TodoRepo -> DB: SELECT * FROM todos WHERE id = ?
TodoRepo -> DB: Todo 정보 조회
activate DB
DB --> TodoRepo: Todo 정보
deactivate DB
@ -49,10 +49,34 @@ Service -> Service: 완료 권한 검증\n(담당자만 가능)
alt 권한 없음
Service --> Controller: 403 Forbidden\n담당자만 완료 가능
note right
에러 응답 형식:
{
"error": {
"code": "INSUFFICIENT_PERMISSION",
"message": "Todo 완료 권한이 없습니다",
"details": "담당자만 Todo를 완료할 수 있습니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/todos/{todoId}/complete"
}
}
end note
return 403 Forbidden
else 권한 있음
alt Todo가 이미 완료됨
Service --> Controller: 409 Conflict\n이미 완료된 Todo
note right
에러 응답 형식:
{
"error": {
"code": "TODO_ALREADY_COMPLETED",
"message": "이미 완료된 Todo입니다",
"details": "해당 Todo는 이미 완료 처리되었습니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/todos/{todoId}/complete"
}
}
end note
return 409 Conflict
else 완료 처리 가능
' 완료 확인 다이얼로그 (프론트엔드에서 처리됨)
@ -60,7 +84,7 @@ else 권한 있음
' Todo 완료 처리
Service -> TodoRepo: markAsCompleted(todoId, userId)
activate TodoRepo
TodoRepo -> DB: UPDATE todos\nSET status = 'COMPLETED',\n completedAt = NOW(),\n completedBy = ?\nWHERE id = ?
TodoRepo -> DB: Todo 완료 상태 업데이트
activate DB
DB --> TodoRepo: 업데이트 완료
deactivate DB
@ -77,7 +101,7 @@ else 권한 있음
' 회의록 섹션 업데이트
Service -> MinutesRepo: updateTodoStatus(todoId, "COMPLETED")
activate MinutesRepo
MinutesRepo -> DB: UPDATE minutes_sections\nSET todoStatuses = JSON_SET(todoStatuses,\n CONCAT('$."', ?, '"'),\n JSON_OBJECT(\n 'status', 'COMPLETED',\n 'completedAt', NOW(),\n 'completedBy', ?\n )\n)\nWHERE JSON_CONTAINS(todoIds, CAST(? AS JSON))
MinutesRepo -> DB: 회의록 섹션의 Todo 상태 업데이트
activate DB
DB --> MinutesRepo: 업데이트 완료
deactivate DB
@ -87,7 +111,7 @@ else 권한 있음
' 회의록의 모든 Todo 완료 여부 확인
Service -> TodoRepo: countPendingTodos(minutesId)
activate TodoRepo
TodoRepo -> DB: SELECT COUNT(*) FROM todos\nWHERE minutesId = ?\nAND status != 'COMPLETED'
TodoRepo -> DB: 미완료 Todo 개수 조회
activate DB
DB --> TodoRepo: 미완료 Todo 개수
deactivate DB

View File

@ -44,7 +44,7 @@ end note
' 회의록 존재 확인
Service -> MinutesRepo: findById(minutesId)
activate MinutesRepo
MinutesRepo -> DB: SELECT * FROM minutes WHERE id = ?
MinutesRepo -> DB: 회의록 정보 조회
activate DB
DB --> MinutesRepo: 회의록 정보
deactivate DB
@ -58,7 +58,7 @@ Service -> Service: Todo 엔티티 생성\n- todoId (UUID)\n- 상태: IN_PROGRES
Service -> TodoRepo: save(todo)
activate TodoRepo
TodoRepo -> DB: INSERT INTO todos\n(id, content, assignee, dueDate,\npriority, status, minutesId, sectionId,\ncreatedBy, createdAt)
TodoRepo -> DB: Todo 정보 저장
activate DB
DB --> TodoRepo: Todo 저장 완료
deactivate DB
@ -74,7 +74,7 @@ end note
' 회의록 섹션에 Todo 연결
Service -> MinutesRepo: linkTodoToSection(sectionId, todoId)
activate MinutesRepo
MinutesRepo -> DB: UPDATE minutes_sections\nSET todoIds = JSON_ARRAY_APPEND(todoIds, '$', ?)\nWHERE id = ?
MinutesRepo -> DB: 회의록 섹션에 Todo 연결
activate DB
DB --> MinutesRepo: 업데이트 완료
deactivate DB
@ -100,7 +100,7 @@ alt 마감일 설정됨
Service -> TodoRepo: updateCalendarEventId(todoId, eventId)
activate TodoRepo
TodoRepo -> DB: UPDATE todos\nSET calendarEventId = ?\nWHERE id = ?
TodoRepo -> DB: 캘린더 이벤트 ID 업데이트
activate DB
DB --> TodoRepo: 업데이트 완료
deactivate DB

View File

@ -21,7 +21,7 @@ activate Service
Service -> Repository: findById(meetingId)
activate Repository
Repository -> DB: SELECT * FROM meetings WHERE id = ?
Repository -> DB: 회의 정보 조회\n(회의ID 기준)
activate DB
DB --> Repository: meeting_row
deactivate DB
@ -67,15 +67,20 @@ alt validation passed
Service -> Repository: save(meeting)
activate Repository
Repository -> DB: UPDATE meetings SET status = 'VALIDATED'
Repository -> DB: 회의 상태 업데이트\n(상태='검증완료')
activate DB
DB --> Repository: affected_rows
deactivate DB
Repository --> Service: savedMeeting
deactivate Repository
Service -> Cache: set(meeting:{id}, meetingData)
Service -> Cache: SET meeting:{id}\n(TTL: 10분)
activate Cache
note right of Cache
회의 정보 캐싱:
- TTL: 10분
- 자동 만료
end note
Cache --> Service: OK
deactivate Cache
@ -89,7 +94,19 @@ alt validation passed
Service --> Controller: success response
else validation failed
Service --> Controller: error response with details
Service --> Controller: 400 Bad Request
note right
에러 응답 형식:
{
"error": {
"code": "VALIDATION_FAILED",
"message": "회의록 검증에 실패했습니다",
"details": "필수 항목 누락 또는 형식 오류",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/meetings/{id}/validate"
}
}
end note
end
deactivate Service

View File

@ -34,7 +34,7 @@ else Cache Miss
' 예정된 회의 조회
Service -> MeetingRepo: findUpcomingMeetings(userId)
activate MeetingRepo
MeetingRepo -> DB: SELECT meetings\nWHERE userId = ? AND status = 'SCHEDULED'\nAND startTime > NOW()\nORDER BY startTime LIMIT 3
MeetingRepo -> DB: 예정된 회의 조회
activate DB
DB --> MeetingRepo: 예정된 회의 목록
deactivate DB
@ -44,7 +44,7 @@ else Cache Miss
' 진행 중 Todo 조회
Service -> TodoRepo: findActiveTodos(userId)
activate TodoRepo
TodoRepo -> DB: SELECT todos\nWHERE assignee = ? AND status = 'IN_PROGRESS'\nORDER BY dueDate LIMIT 3
TodoRepo -> DB: 진행 중 Todo 조회
activate DB
DB --> TodoRepo: 진행 중 Todo 목록
deactivate DB
@ -54,7 +54,7 @@ else Cache Miss
' 최근 회의록 조회
Service -> MinutesRepo: findRecentMinutes(userId)
activate MinutesRepo
MinutesRepo -> DB: SELECT minutes\nWHERE creatorId = ?\nORDER BY createdAt DESC LIMIT 3
MinutesRepo -> DB: 최근 회의록 조회
activate DB
DB --> MinutesRepo: 최근 회의록 목록
deactivate DB
@ -64,7 +64,7 @@ else Cache Miss
' 공유받은 회의록 조회
Service -> MinutesRepo: findSharedMinutes(userId)
activate MinutesRepo
MinutesRepo -> DB: SELECT minutes m JOIN shared_minutes sm\nON m.id = sm.minutesId\nWHERE sm.sharedWith = ?\nORDER BY sm.sharedAt DESC LIMIT 3
MinutesRepo -> DB: 공유받은 회의록 조회
activate DB
DB --> MinutesRepo: 공유받은 회의록 목록
deactivate DB
@ -74,7 +74,7 @@ else Cache Miss
' 통계 정보 조회
Service -> MeetingRepo: countUpcomingMeetings(userId)
activate MeetingRepo
MeetingRepo -> DB: SELECT COUNT(*)\nFROM meetings\nWHERE userId = ? AND status = 'SCHEDULED'
MeetingRepo -> DB: 예정된 회의 개수 조회
activate DB
DB --> MeetingRepo: 예정된 회의 개수
deactivate DB
@ -83,7 +83,7 @@ else Cache Miss
Service -> TodoRepo: countActiveTodos(userId)
activate TodoRepo
TodoRepo -> DB: SELECT COUNT(*)\nFROM todos\nWHERE assignee = ? AND status = 'IN_PROGRESS'
TodoRepo -> DB: 진행 중 Todo 개수 조회
activate DB
DB --> TodoRepo: 진행 중 Todo 개수
deactivate DB
@ -92,7 +92,7 @@ else Cache Miss
Service -> TodoRepo: calculateTodoCompletionRate(userId)
activate TodoRepo
TodoRepo -> DB: SELECT\n (COUNT(CASE WHEN status='COMPLETED' THEN 1 END) * 100.0 /\n COUNT(*)) as rate\nFROM todos WHERE assignee = ?
TodoRepo -> DB: Todo 완료율 조회
activate DB
DB --> TodoRepo: Todo 완료율
deactivate DB

View File

@ -53,11 +53,13 @@ end note
TranscriptService --> Service: updatedVersion
deactivate TranscriptService
Service -> Cache: set(meeting:{id}:version, versionData)
Service -> Cache: SET meeting:{id}:version\n(TTL: 1시간)
activate Cache
note right of Cache
버전 정보 업데이트
최신 상태 유지
세션 버전 정보 캐싱:
- TTL: 1시간
- 버전 정보 업데이트
- 최신 상태 유지
end note
Cache --> Service: OK
deactivate Cache

View File

@ -4,7 +4,7 @@
title 최종 회의록 확정 내부 시퀀스
participant "API Gateway<<E>>" as Gateway
participant "MeetingController" as Controller
participant "MinutesController" as Controller
participant "MeetingService" as Service
participant "Meeting" as Domain
participant "TranscriptService" as TranscriptService
@ -13,7 +13,7 @@ database "PostgreSQL<<E>>" as DB
database "Redis Cache<<E>>" as Cache
queue "Event Hub<<E>>" as EventHub
Gateway -> Controller: POST /api/meetings/{id}/confirm
Gateway -> Controller: POST /api/minutes/{minutesId}/finalize
activate Controller
Controller -> Service: confirmTranscript(meetingId)
@ -21,7 +21,7 @@ activate Service
Service -> Repository: findById(meetingId)
activate Repository
Repository -> DB: SELECT * FROM meetings WHERE id = ?
Repository -> DB: 회의 정보 조회\n(회의ID 기준)
activate DB
DB --> Repository: meeting_row
deactivate DB
@ -33,10 +33,37 @@ activate Domain
Domain -> Domain: validateCanConfirm()
note right of Domain
도메인 규칙:
- COMPLETED 상태 검증
- 작성자 권한 검증
- 회의록 존재 여부 확인
회의록 확정 검증 규칙:
1. 상태 검증:
- 회의 상태 = COMPLETED (종료됨)
- 회의록 상태 = DRAFT (작성중)
✗ IN_PROGRESS → 400 Bad Request
2. 권한 검증:
- 확정 요청자 = 회의 생성자 OR
- 확정 요청자 ∈ 참석자 목록
✗ 권한 없음 → 403 Forbidden
3. 필수 항목 검증:
- 회의록 제목: NOT NULL, 길이 >= 5
- 참석자 목록: 최소 1명 이상
- 주요 논의 내용: NOT NULL, 길이 >= 20
- 결정 사항: 최소 1개 이상 OR
"결정사항 없음" 명시적 표시
✗ 누락 → 400 Bad Request
(누락 항목 목록 반환)
4. 데이터 무결성:
- 섹션별 내용 완결성 확인
- 참조 링크 유효성 확인
- 첨부 파일 접근 가능 여부
✗ 무결성 위반 → 400 Bad Request
5. 이력 검증:
- 마지막 수정 후 24시간 경과 경고
- 미승인 AI 제안 존재 시 알림
⚠️ 경고만 표시, 진행 가능
end note
Domain -> Domain: changeStatus(CONFIRMED)
@ -56,19 +83,24 @@ deactivate TranscriptService
Service -> Repository: save(meeting)
activate Repository
Repository -> DB: UPDATE meetings SET status = 'CONFIRMED', confirmed_at = ?
Repository -> DB: 회의 상태 업데이트\n(상태='확정', 확정일시)
activate DB
DB --> Repository: affected_rows
deactivate DB
Repository --> Service: savedMeeting
deactivate Repository
Service -> Cache: set(meeting:{id}, meetingData)
Service -> Cache: SET meeting:{id}\n(TTL: 10분)
activate Cache
note right of Cache
회의 정보 캐싱:
- TTL: 10분
- 자동 만료
end note
Cache --> Service: OK
deactivate Cache
Service ->> EventHub: publish(TranscriptConfirmedEvent)
Service ->> EventHub: publish(MinutesFinalized)
activate EventHub
note right of EventHub
비동기 이벤트:

View File

@ -62,11 +62,13 @@ alt auto-resolved
deactivate Cache
else manual-required
Service -> Cache: set(meeting:{id}:conflicts, conflictData)
Service -> Cache: SET meeting:{id}:conflicts\n(TTL: 1시간)
activate Cache
note right of Cache
충돌 정보 저장
수동 해결 대기
충돌 정보 캐싱:
- TTL: 1시간
- 충돌 정보 저장
- 수동 해결 대기
end note
Cache --> Service: OK
deactivate Cache

View File

@ -25,7 +25,7 @@ deactivate Cache
Service -> Repository: findById(meetingId)
activate Repository
Repository -> DB: SELECT * FROM meetings WHERE id = ?
Repository -> DB: 회의 정보 조회\n(회의ID 기준)
activate DB
DB --> Repository: meeting_row
deactivate DB
@ -56,15 +56,20 @@ deactivate Domain
Service -> Repository: save(meeting)
activate Repository
Repository -> DB: UPDATE meetings SET template_id = ?
Repository -> DB: 회의 템플릿 업데이트\n(템플릿ID)
activate DB
DB --> Repository: affected_rows
deactivate DB
Repository --> Service: savedMeeting
deactivate Repository
Service -> Cache: set(meeting:{id}, meetingData)
Service -> Cache: SET meeting:{id}\n(TTL: 10분)
activate Cache
note right of Cache
회의 정보 캐싱:
- TTL: 10분
- 자동 만료
end note
Cache --> Service: OK
deactivate Cache

View File

@ -39,7 +39,7 @@ activate Service
' 회의록 정보 조회
Service -> MinutesRepo: findById(minutesId)
activate MinutesRepo
MinutesRepo -> DB: SELECT * FROM minutes WHERE id = ?
MinutesRepo -> DB: 회의록 정보 조회
activate DB
DB --> MinutesRepo: 회의록 정보
deactivate DB
@ -61,6 +61,18 @@ Service -> Service: 회의록 상태 확인
alt 회의록이 확정되지 않음
Service --> Controller: 400 Bad Request\n확정된 회의록만 공유 가능
note right
에러 응답 형식:
{
"error": {
"code": "MINUTES_NOT_FINALIZED",
"message": "확정된 회의록만 공유 가능합니다",
"details": "회의록을 먼저 확정해 주세요",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/minutes/{minutesId}/share"
}
}
end note
return 400 Bad Request
else 공유 가능
' 공유 링크 생성
@ -68,7 +80,7 @@ else 공유 가능
Service -> ShareRepo: createShareLink(minutesId, token, options)
activate ShareRepo
ShareRepo -> DB: INSERT INTO share_links\n(minutesId, token, permission,\nexpiresAt, requirePassword, passwordHash,\ncreatedBy, createdAt)
ShareRepo -> DB: 공유 링크 정보 저장
activate DB
DB --> ShareRepo: 링크 생성 완료
deactivate DB
@ -79,7 +91,7 @@ else 공유 가능
loop 각 공유 대상마다
Service -> ShareRepo: addSharedUser(minutesId, userEmail, permission)
activate ShareRepo
ShareRepo -> DB: INSERT INTO shared_minutes\n(minutesId, sharedWith, permission,\nsharedBy, sharedAt)
ShareRepo -> DB: 공유 대상 정보 저장
activate DB
DB --> ShareRepo: 공유 정보 저장 완료
deactivate DB

View File

@ -36,7 +36,7 @@ deactivate Cache
Service -> Repository: findAll(specification, pageable)
activate Repository
Repository -> DB: SELECT * FROM meetings WHERE ... ORDER BY ... LIMIT ? OFFSET ?
Repository -> DB: 회의록 목록 조회\n(필터 조건, 정렬, 페이징)
activate DB
DB --> Repository: meeting_rows
deactivate DB
@ -51,8 +51,13 @@ note right of Service
- 민감 정보 필터링
end note
Service -> Cache: set(meetings:list:{hash}, responseData)
Service -> Cache: SET meetings:list:{hash}\n(TTL: 5분)
activate Cache
note right of Cache
목록 데이터 캐싱:
- TTL: 5분
- 자동 만료
end note
Cache --> Service: OK
deactivate Cache

View File

@ -35,6 +35,18 @@ alt Cache Hit
alt 권한 없음
Service --> Controller: 403 Forbidden\n조회 권한 없음
note right
에러 응답 형식:
{
"error": {
"code": "ACCESS_DENIED",
"message": "조회 권한이 없습니다",
"details": "회의록 조회 권한이 없는 사용자입니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/minutes/{minutesId}"
}
}
end note
return 403 Forbidden
else 권한 있음
Service --> Controller: MinutesDetailResponse\n(캐시 데이터)
@ -44,7 +56,7 @@ else Cache Miss
' 회의록 기본 정보 조회
Service -> MinutesRepo: findByIdWithMeeting(minutesId)
activate MinutesRepo
MinutesRepo -> DB: SELECT m.*, mt.*\nFROM minutes m\nJOIN meetings mt ON m.meetingId = mt.id\nWHERE m.id = ?
MinutesRepo -> DB: 회의록 및 회의 정보 조회
activate DB
DB --> MinutesRepo: 회의록 및 회의 정보
deactivate DB
@ -65,7 +77,7 @@ else Cache Miss
' 권한 확인
Service -> MinutesRepo: hasAccessPermission(minutesId, userId)
activate MinutesRepo
MinutesRepo -> DB: SELECT COUNT(*) FROM (\n SELECT 1 FROM minutes WHERE id = ? AND creatorId = ?\n UNION\n SELECT 1 FROM participants p JOIN meetings m\n ON p.meetingId = m.id JOIN minutes mi\n ON mi.meetingId = m.id\n WHERE mi.id = ? AND p.userId = ?\n UNION\n SELECT 1 FROM shared_minutes\n WHERE minutesId = ? AND sharedWith = ?\n) AS access
MinutesRepo -> DB: 회의록 조회 권한 확인
activate DB
DB --> MinutesRepo: 권한 확인 결과
deactivate DB
@ -74,12 +86,24 @@ else Cache Miss
alt 권한 없음
Service --> Controller: 403 Forbidden\n조회 권한 없음
note right
에러 응답 형식:
{
"error": {
"code": "ACCESS_DENIED",
"message": "조회 권한이 없습니다",
"details": "회의록 조회 권한이 없는 사용자입니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/minutes/{minutesId}"
}
}
end note
return 403 Forbidden
else 권한 있음
' 참석자 목록 조회
Service -> MinutesRepo: findParticipants(minutesId)
activate MinutesRepo
MinutesRepo -> DB: SELECT p.*, u.name, u.email\nFROM participants p\nJOIN meetings m ON p.meetingId = m.id\nJOIN minutes mi ON mi.meetingId = m.id\nJOIN users u ON p.userId = u.id\nWHERE mi.id = ?
MinutesRepo -> DB: 참석자 목록 조회
activate DB
DB --> MinutesRepo: 참석자 목록
deactivate DB
@ -89,7 +113,7 @@ else Cache Miss
' 섹션별 상세 내용 조회
Service -> SectionRepo: findSectionsByMinutesId(minutesId)
activate SectionRepo
SectionRepo -> DB: SELECT * FROM minutes_sections\nWHERE minutesId = ?\nORDER BY sectionOrder
SectionRepo -> DB: 섹션 목록 조회
activate DB
DB --> SectionRepo: 섹션 목록
deactivate DB
@ -99,7 +123,7 @@ else Cache Miss
' AI 요약 정보 조회
Service -> SectionRepo: findAISummaries(minutesId)
activate SectionRepo
SectionRepo -> DB: SELECT * FROM ai_summaries\nWHERE minutesId = ?\nORDER BY sectionId
SectionRepo -> DB: AI 요약 정보 조회
activate DB
DB --> SectionRepo: AI 요약 목록
deactivate DB
@ -117,7 +141,7 @@ else Cache Miss
- 최대 3개
end note
RelatedService -> DB: SELECT * FROM related_minutes\nWHERE minutesId = ?\nAND similarityScore >= 0.7\nORDER BY similarityScore DESC\nLIMIT 3
RelatedService -> DB: 관련 회의록 조회
activate DB
DB --> RelatedService: 관련 회의록 목록
deactivate DB
@ -127,7 +151,7 @@ else Cache Miss
' 조회 이력 기록
Service -> MinutesRepo: recordViewHistory(minutesId, userId)
activate MinutesRepo
MinutesRepo -> DB: INSERT INTO view_history\n(minutesId, userId, viewedAt)\nVALUES (?, ?, NOW())
MinutesRepo -> DB: 조회 이력 기록
activate DB
DB --> MinutesRepo: 기록 완료
deactivate DB

View File

@ -40,7 +40,7 @@ activate Service
' 회의록 정보 조회
Service -> MinutesRepo: findById(minutesId)
activate MinutesRepo
MinutesRepo -> DB: SELECT * FROM minutes WHERE id = ?
MinutesRepo -> DB: 회의록 정보 조회
activate DB
DB --> MinutesRepo: 회의록 정보
deactivate DB
@ -61,6 +61,18 @@ Service -> Service: 수정 권한 검증\n(생성자만 가능)
alt 권한 없음
Service --> Controller: 403 Forbidden\n수정 권한 없음
note right
에러 응답 형식:
{
"error": {
"code": "ACCESS_DENIED",
"message": "회의록 수정 권한이 없습니다",
"details": "생성자만 회의록을 수정할 수 있습니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/minutes/{minutesId}"
}
}
end note
return 403 Forbidden
else 권한 있음
' 잠긴 섹션 확인
@ -77,12 +89,24 @@ else 권한 있음
alt 잠긴 섹션 수정 시도
Service --> Controller: 400 Bad Request\n잠긴 섹션은 수정 불가
note right
에러 응답 형식:
{
"error": {
"code": "SECTION_LOCKED",
"message": "잠긴 섹션은 수정할 수 없습니다",
"details": "해당 섹션은 잠금 상태로 수정이 제한됩니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/minutes/{minutesId}"
}
}
end note
return 400 Bad Request
else 수정 가능
' 수정 이력 저장 (버전 관리)
Service -> VersionService: createModificationHistory(minutesId, userId, changes)
activate VersionService
VersionService -> DB: INSERT INTO modification_history\n(minutesId, modifiedBy, modifiedAt,\nchangeType, oldValue, newValue)
VersionService -> DB: 수정 이력 저장
activate DB
DB --> VersionService: 이력 저장 완료
deactivate DB
@ -93,7 +117,7 @@ else 권한 있음
alt 제목 변경됨
Service -> MinutesRepo: updateTitle(minutesId, newTitle)
activate MinutesRepo
MinutesRepo -> DB: UPDATE minutes\nSET title = ?,\n updatedAt = NOW(),\n updatedBy = ?\nWHERE id = ?
MinutesRepo -> DB: 회의록 제목 수정
activate DB
DB --> MinutesRepo: 업데이트 완료
deactivate DB
@ -105,7 +129,7 @@ else 권한 있음
loop 각 섹션마다
Service -> SectionRepo: updateSection(sectionId, content, aiSummary)
activate SectionRepo
SectionRepo -> DB: UPDATE minutes_sections\nSET content = ?,\n aiSummary = ?,\n updatedAt = NOW(),\n updatedBy = ?\nWHERE id = ?
SectionRepo -> DB: 섹션 내용 및 AI 요약 수정
activate DB
DB --> SectionRepo: 업데이트 완료
deactivate DB
@ -117,7 +141,7 @@ else 권한 있음
alt 확정된 회의록 수정
Service -> MinutesRepo: updateStatus(minutesId, "DRAFT")
activate MinutesRepo
MinutesRepo -> DB: UPDATE minutes\nSET status = 'DRAFT'\nWHERE id = ?\nAND status = 'FINALIZED'
MinutesRepo -> DB: 확정된 회의록을 다시 작성 중(DRAFT)으로 변경
activate DB
DB --> MinutesRepo: 업데이트 완료
deactivate DB

View File

@ -28,7 +28,7 @@ activate Service
' 회의록 정보 조회
Service -> MinutesRepo: findById(minutesId)
activate MinutesRepo
MinutesRepo -> DB: SELECT * FROM minutes WHERE id = ?
MinutesRepo -> DB: 회의록 정보 조회
activate DB
DB --> MinutesRepo: 회의록 정보
deactivate DB
@ -49,6 +49,18 @@ Service -> Service: 회의록 상태 확인
alt 회의록이 이미 확정됨
Service --> Controller: 409 Conflict\n이미 확정된 회의록
note right
에러 응답 형식:
{
"error": {
"code": "MINUTES_ALREADY_FINALIZED",
"message": "이미 확정된 회의록입니다",
"details": "확정된 회의록은 다시 확정할 수 없습니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/minutes/{minutesId}/finalize"
}
}
end note
return 409 Conflict
else 확정 가능
note over Service
@ -71,6 +83,18 @@ else 확정 가능
alt 필수 항목 누락
ValidationService --> Service: ValidationException\n누락된 항목 목록
Service --> Controller: 400 Bad Request\n필수 항목 누락
note right
에러 응답 형식:
{
"error": {
"code": "MISSING_REQUIRED_FIELDS",
"message": "필수 항목이 누락되었습니다",
"details": "회의 제목, 참석자 목록, 논의 내용 중 일부가 누락되었습니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/minutes/{minutesId}/finalize"
}
}
end note
return 400 Bad Request
else 검증 통과
ValidationService --> Service: 검증 성공
@ -82,7 +106,7 @@ else 확정 가능
' 회의록 상태 업데이트
Service -> MinutesRepo: finalize(minutesId, version)
activate MinutesRepo
MinutesRepo -> DB: UPDATE minutes\nSET status = 'FINALIZED',\n version = ?,\n finalizedAt = NOW(),\n finalizedBy = ?\nWHERE id = ?
MinutesRepo -> DB: 회의록 확정 상태 업데이트
activate DB
DB --> MinutesRepo: 업데이트 완료
deactivate DB
@ -92,7 +116,7 @@ else 확정 가능
' 회의록 스냅샷 저장 (버전 관리)
Service -> MinutesRepo: saveSnapshot(minutesId, version, content)
activate MinutesRepo
MinutesRepo -> DB: INSERT INTO minutes_snapshots\n(minutesId, version, content, createdAt)
MinutesRepo -> DB: 회의록 스냅샷 저장
activate DB
DB --> MinutesRepo: 스냅샷 저장 완료
deactivate DB

View File

@ -34,15 +34,20 @@ deactivate Cache
alt Cache Miss
Service -> MeetingRepo: findById(meetingId)
activate MeetingRepo
MeetingRepo -> DB: SELECT * FROM meetings\nWHERE id = ?
MeetingRepo -> DB: 회의 정보 조회
activate DB
DB --> MeetingRepo: 회의 정보
deactivate DB
MeetingRepo --> Service: Meeting
deactivate MeetingRepo
Service -> Cache: SET meeting:info:{meetingId}
Service -> Cache: SET meeting:info:{meetingId}\n(TTL: 10분)
activate Cache
note right of Cache
회의 정보 캐싱:
- TTL: 10분
- 자동 만료
end note
Cache --> Service: 캐싱 완료
deactivate Cache
end
@ -61,6 +66,18 @@ Service -> Service: 회의 상태 확인
alt 회의가 이미 진행 중
Service --> Controller: 409 Conflict\n이미 진행 중인 회의
note right
에러 응답 형식:
{
"error": {
"code": "MEETING_ALREADY_IN_PROGRESS",
"message": "이미 진행 중인 회의입니다",
"details": "해당 회의는 현재 진행 중 상태입니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/meetings/{meetingId}/start"
}
}
end note
return 409 Conflict
else 시작 가능
Service -> Service: 회의 세션 생성
@ -78,7 +95,7 @@ else 시작 가능
- status: ACTIVE
end note
SessionRepo -> DB: INSERT INTO meeting_sessions\n(id, meetingId, startedBy, startedAt, status)
SessionRepo -> DB: 회의 세션 생성
activate DB
DB --> SessionRepo: 세션 생성 완료
deactivate DB
@ -88,7 +105,7 @@ else 시작 가능
' 회의 상태 업데이트
Service -> MeetingRepo: updateStatus(meetingId, "IN_PROGRESS")
activate MeetingRepo
MeetingRepo -> DB: UPDATE meetings\nSET status = 'IN_PROGRESS',\n actualStartTime = NOW()\nWHERE id = ?
MeetingRepo -> DB: 회의 상태를 'IN_PROGRESS'로 변경하고 실제 시작 시간 기록
activate DB
DB --> MeetingRepo: 업데이트 완료
deactivate DB
@ -106,7 +123,7 @@ else 시작 가능
Service -> MeetingRepo: createMinutesDraft(meetingId, sessionId)
activate MeetingRepo
MeetingRepo -> DB: INSERT INTO minutes\n(id, meetingId, sessionId, status, createdAt)\nVALUES (?, ?, ?, 'DRAFT', NOW())
MeetingRepo -> DB: 회의록 초안 생성 (상태: DRAFT)
activate DB
DB --> MeetingRepo: 회의록 생성 완료
deactivate DB

View File

@ -42,7 +42,7 @@ Service -> Service: 회의 시간 유효성 검사
Service -> Repo: checkConflictingMeetings(userId, startTime, endTime)
activate Repo
Repo -> DB: SELECT COUNT(*) FROM meetings\nWHERE creatorId = ?\nAND status = 'SCHEDULED'\nAND (startTime <= ? AND endTime >= ?)
Repo -> DB: 중복 회의 확인
activate DB
DB --> Repo: 중복 회의 개수
deactivate DB
@ -51,6 +51,18 @@ deactivate Repo
alt 중복 회의 존재
Service --> Controller: 409 Conflict\n중복된 회의 시간
note right
에러 응답 형식:
{
"error": {
"code": "MEETING_TIME_CONFLICT",
"message": "중복된 회의 시간이 존재합니다",
"details": "해당 시간대에 이미 예약된 회의가 있습니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/meetings"
}
}
end note
return 409 Conflict
else 중복 없음
Service -> Service: Meeting 엔티티 생성\n- 회의 ID 생성\n- 상태: SCHEDULED\n- 생성자 정보 설정
@ -58,7 +70,7 @@ else 중복 없음
' 회의 정보 저장
Service -> Repo: save(meeting)
activate Repo
Repo -> DB: INSERT INTO meetings\n(id, title, startTime, endTime,\nlocation, status, creatorId, createdAt)
Repo -> DB: 회의 정보 저장
activate DB
DB --> Repo: 회의 저장 완료
deactivate DB
@ -68,7 +80,7 @@ else 중복 없음
' 참석자 저장
Service -> ParticipantRepo: saveAll(participants)
activate ParticipantRepo
ParticipantRepo -> DB: INSERT INTO participants\n(meetingId, email, role, status)
ParticipantRepo -> DB: 참석자 정보 저장
activate DB
DB --> ParticipantRepo: 참석자 저장 완료
deactivate DB

View File

@ -33,7 +33,7 @@ activate Service
' 회의 정보 조회
Service -> MeetingRepo: findById(meetingId)
activate MeetingRepo
MeetingRepo -> DB: SELECT * FROM meetings WHERE id = ?
MeetingRepo -> DB: 회의 정보 조회\n(회의ID 기준)
activate DB
DB --> MeetingRepo: 회의 정보
deactivate DB
@ -53,12 +53,24 @@ Service -> Service: 회의 상태 확인
alt 회의가 진행 중이 아님
Service --> Controller: 409 Conflict\n진행 중인 회의가 아님
note right
에러 응답 형식:
{
"error": {
"code": "MEETING_NOT_IN_PROGRESS",
"message": "진행 중인 회의가 아닙니다",
"details": "회의 종료는 진행 중 상태에서만 가능합니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/meetings/{meetingId}/end"
}
}
end note
return 409 Conflict
else 종료 가능
' 세션 정보 조회
Service -> SessionRepo: findActiveSession(meetingId)
activate SessionRepo
SessionRepo -> DB: SELECT * FROM meeting_sessions\nWHERE meetingId = ?\nAND status = 'ACTIVE'
SessionRepo -> DB: 활성 세션 조회\n(회의ID, 상태='진행중')
activate DB
DB --> SessionRepo: 세션 정보
deactivate DB
@ -68,7 +80,7 @@ else 종료 가능
' 세션 종료
Service -> SessionRepo: endSession(sessionId)
activate SessionRepo
SessionRepo -> DB: UPDATE meeting_sessions\nSET status = 'ENDED',\n endedAt = NOW()\nWHERE id = ?
SessionRepo -> DB: 세션 종료 처리\n(상태='종료', 종료일시)
activate DB
DB --> SessionRepo: 업데이트 완료
deactivate DB
@ -78,7 +90,7 @@ else 종료 가능
' 회의 상태 업데이트
Service -> MeetingRepo: updateStatus(meetingId, "ENDED")
activate MeetingRepo
MeetingRepo -> DB: UPDATE meetings\nSET status = 'ENDED',\n actualEndTime = NOW()\nWHERE id = ?
MeetingRepo -> DB: 회의 상태 업데이트\n(상태='종료', 실제종료시각)
activate DB
DB --> MeetingRepo: 업데이트 완료
deactivate DB
@ -97,12 +109,12 @@ else 종료 가능
Service -> StatService: generateMeetingStatistics(sessionId)
activate StatService
StatService -> DB: SELECT\n COUNT(DISTINCT speakerId) as speakerCount,\n COUNT(*) as utteranceCount,\n TIMESTAMPDIFF(MINUTE, startedAt, endedAt) as duration\nFROM transcripts WHERE sessionId = ?
StatService -> DB: 회의 통계 데이터 조회\n(화자수, 발언횟수, 진행시간)
activate DB
DB --> StatService: 통계 데이터
deactivate DB
StatService -> DB: INSERT INTO meeting_statistics\n(meetingId, sessionId, duration,\nparticipantCount, utteranceCount, createdAt)
StatService -> DB: 회의 통계 저장\n(회의ID, 세션ID, 진행시간, 참석자수, 발언횟수)
activate DB
DB --> StatService: 통계 저장 완료
deactivate DB
@ -124,7 +136,7 @@ else 종료 가능
AISuggestion -> SuggestionRepo: findAppliedSuggestions(meetingId)
activate SuggestionRepo
SuggestionRepo -> DB: SELECT *\nFROM ai_suggestions\nWHERE meeting_id = ?\nAND status = 'APPLIED'\nORDER BY suggestion_type, applied_at
SuggestionRepo -> DB: 적용된 AI 제안 조회\n(회의ID, 상태='적용됨', 유형별 정렬)
activate DB
note right
조회 대상:
@ -144,7 +156,7 @@ else 종료 가능
' 2. 회의록 조회
Service -> MinutesRepo: findByMeetingId(meetingId)
activate MinutesRepo
MinutesRepo -> DB: SELECT * FROM minutes\nWHERE meeting_id = ?
MinutesRepo -> DB: 회의록 정보 조회\n(회의ID 기준)
activate DB
DB --> MinutesRepo: 회의록 정보
deactivate DB
@ -172,7 +184,7 @@ else 종료 가능
MinutesService -> MinutesRepo: getSectionContent(minutesId, sectionType)
activate MinutesRepo
MinutesRepo -> DB: SELECT section_content\nFROM minutes_sections\nWHERE minutes_id = ?\nAND section_type = ?
MinutesRepo -> DB: 회의록 섹션 내용 조회\n(회의록ID, 섹션유형)
activate DB
DB --> MinutesRepo: 기존 섹션 내용
deactivate DB
@ -191,7 +203,7 @@ else 종료 가능
MinutesService -> MinutesRepo: updateSectionContent(minutesId, sectionType, mergedContent)
activate MinutesRepo
MinutesRepo -> DB: UPDATE minutes_sections\nSET section_content = ?,\n updated_at = NOW(),\n ai_enhanced = true\nWHERE minutes_id = ?\nAND section_type = ?
MinutesRepo -> DB: 회의록 섹션 내용 업데이트\n(병합된 내용, AI보강 플래그, 갱신일시)
activate DB
note right
저장 내용:
@ -212,7 +224,7 @@ else 종료 가능
' 4. AI 제안 상태 업데이트 (APPLIED → MERGED)
Service -> SuggestionRepo: updateSuggestionsStatus(appliedSuggestions, "MERGED")
activate SuggestionRepo
SuggestionRepo -> DB: UPDATE ai_suggestions\nSET status = 'MERGED',\n merged_at = NOW()\nWHERE id IN (...)
SuggestionRepo -> DB: AI 제안 상태 업데이트\n(상태='병합완료', 병합일시)
activate DB
note right
상태 변경:
@ -228,7 +240,7 @@ else 종료 가능
' 회의록 상태 업데이트
Service -> MinutesRepo: updateMinutesStatus(meetingId, "DRAFT")
activate MinutesRepo
MinutesRepo -> DB: UPDATE minutes\nSET status = 'DRAFT',\n endedAt = NOW(),\n ai_suggestions_merged = true,\n merged_count = ?\nWHERE meetingId = ?\nAND status = 'IN_PROGRESS'
MinutesRepo -> DB: 회의록 상태 업데이트\n(상태='초안', 종료일시, AI제안병합 플래그, 병합건수)
activate DB
note right
회의록 완성도 표시:

View File

@ -35,7 +35,7 @@ activate Service
Service -> Repository: createNotification(todoId, "TODO_ASSIGNED", assignee)
activate Repository
Repository -> DB: INSERT INTO notifications\n(notification_id, todo_id,\ntype='TODO_ASSIGNED',\nstatus='PENDING',\nrecipients,\ncreated_at)
Repository -> DB: 알림 정보 생성\n(알림ID, TodoID, 유형, 상태, 수신자, 생성일시)
activate DB
DB --> Repository: notificationId 반환
deactivate DB
@ -86,7 +86,7 @@ alt 발송 성공
Service -> Repository: updateNotificationStatus(notificationId, "SENT")
activate Repository
Repository -> DB: UPDATE notifications\nSET status='SENT',\nsent_at=NOW()\nWHERE notification_id='{notificationId}'
Repository -> DB: 알림 상태 업데이트\n(상태=발송완료, 발송일시=현재시각)
activate DB
DB --> Repository: 업데이트 완료
deactivate DB
@ -98,7 +98,7 @@ else 발송 실패
Service -> Repository: updateNotificationStatus(notificationId, "FAILED")
activate Repository
Repository -> DB: UPDATE notifications\nSET status='FAILED',\nerror_message='{errorMessage}'\nWHERE notification_id='{notificationId}'
Repository -> DB: 알림 상태 업데이트\n(상태=발송실패, 오류메시지=에러내용)
activate DB
DB --> Repository: 업데이트 완료
deactivate DB

View File

@ -29,7 +29,7 @@ loop 각 회의별
Service -> Repository: checkReminderSent(meetingId)
activate Repository
Repository -> DB: SELECT * FROM notifications\nWHERE meeting_id='{meetingId}'\nAND type='REMINDER'
Repository -> DB: 리마인더 알림 조회\n(회의ID, 유형='REMINDER')
activate DB
DB --> Repository: 조회 결과
deactivate DB
@ -46,7 +46,7 @@ loop 각 회의별
Service -> Repository: createNotification(meetingId, "REMINDER", participants)
activate Repository
Repository -> DB: INSERT INTO notifications\n(notification_id, meeting_id,\ntype='REMINDER',\nstatus='PENDING',\nrecipients,\ncreated_at)
Repository -> DB: 리마인더 알림 생성\n(알림ID, 회의ID, 유형, 상태, 수신자, 생성일시)
activate DB
DB --> Repository: notificationId 반환
deactivate DB
@ -97,7 +97,7 @@ loop 각 회의별
Service -> Repository: updateRecipientStatus(notificationId, recipient, "SENT")
activate Repository
Repository -> DB: UPDATE notification_recipients\nSET status='SENT', sent_at=NOW()
Repository -> DB: 수신자별 알림 상태 업데이트\n(상태='발송완료', 발송일시=현재시각)
activate DB
DB --> Repository: 업데이트 완료
deactivate DB
@ -109,7 +109,7 @@ loop 각 회의별
Service -> Repository: updateRecipientStatus(notificationId, recipient, "FAILED")
activate Repository
Repository -> DB: UPDATE notification_recipients\nSET status='FAILED'
Repository -> DB: 수신자별 알림 상태 업데이트\n(상태='발송실패')
activate DB
DB --> Repository: 업데이트 완료
deactivate DB
@ -127,7 +127,7 @@ loop 각 회의별
Service -> Repository: updateNotificationStatus(notificationId, finalStatus)
activate Repository
Repository -> DB: UPDATE notifications\nSET status='{finalStatus}',\ncompleted_at=NOW(),\nsent_count={sentCount},\nfailed_count={failedCount}
Repository -> DB: 알림 최종 상태 업데이트\n(상태, 완료일시, 발송건수, 실패건수)
activate DB
DB --> Repository: 업데이트 완료
deactivate DB

View File

@ -27,7 +27,7 @@ activate Service
Service -> Repository: checkDuplicateNotification(eventId)
activate Repository
Repository -> DB: SELECT * FROM notifications\nWHERE event_id = ?
Repository -> DB: 알림 중복 확인 조회\n(이벤트ID 기준)
note right
중복 발송 방지:
- Event ID 기반
@ -58,7 +58,7 @@ else 신규 이벤트
Service -> Repository: getUserPreferences(userId)
activate Repository
Repository -> DB: SELECT * FROM user_notification_prefs\nWHERE user_id = ?
Repository -> DB: 사용자 알림 설정 조회\n(사용자ID 기준)
note right
사용자 설정 확인:
- 알림 채널 (email/sms)
@ -139,7 +139,7 @@ else 신규 이벤트
Service -> Repository: saveNotificationLog(notification)
activate Repository
Repository -> DB: INSERT INTO notifications\n(event_id, user_id, type, channel, status, sent_at)
Repository -> DB: 알림 이력 저장\n(이벤트ID, 사용자ID, 유형, 채널, 상태, 발송일시)
note right
알림 로그 저장:
- 발송 이력
@ -152,7 +152,7 @@ else 신규 이벤트
Service -> Repository: updateUserActivity(userId, "NOTIFICATION_SENT")
activate Repository
Repository -> DB: INSERT INTO user_activities\n(user_id, activity_type, details)
Repository -> DB: 사용자 활동 이력 저장\n(사용자ID, 활동유형, 상세내용)
DB --> Repository: saved
Repository --> Service: updated
deactivate Repository

View File

@ -34,7 +34,7 @@ activate Service
Service -> Repository: createNotification(meetingId, "INVITATION", participants)
activate Repository
Repository -> DB: INSERT INTO notifications\n(notification_id, meeting_id,\ntype='INVITATION',\nstatus='PENDING',\nrecipients,\ncreated_at)
Repository -> DB: 초대 알림 생성\n(알림ID, 회의ID, 유형, 상태, 수신자, 생성일시)
activate DB
DB --> Repository: notificationId 반환
deactivate DB
@ -86,7 +86,7 @@ loop 각 참석자별
Service -> Repository: updateRecipientStatus(notificationId, recipient, "SENT")
activate Repository
Repository -> DB: UPDATE notification_recipients\nSET status='SENT', sent_at=NOW()\nWHERE notification_id='{notificationId}'\nAND recipient='{email}'
Repository -> DB: 수신자별 알림 상태 업데이트\n(상태='발송완료', 발송일시=현재시각)
activate DB
DB --> Repository: 업데이트 완료
deactivate DB
@ -98,7 +98,7 @@ loop 각 참석자별
Service -> Repository: updateRecipientStatus(notificationId, recipient, "FAILED")
activate Repository
Repository -> DB: UPDATE notification_recipients\nSET status='FAILED',\nerror_message='{errorMessage}'\nWHERE notification_id='{notificationId}'\nAND recipient='{email}'
Repository -> DB: 수신자별 알림 상태 업데이트\n(상태='발송실패', 오류메시지=에러내용)
activate DB
DB --> Repository: 업데이트 완료
deactivate DB
@ -116,7 +116,7 @@ end
Service -> Repository: updateNotificationStatus(notificationId, finalStatus)
activate Repository
Repository -> DB: UPDATE notifications\nSET status='{finalStatus}',\ncompleted_at=NOW(),\nsent_count={sentCount},\nfailed_count={failedCount}\nWHERE notification_id='{notificationId}'
Repository -> DB: 알림 최종 상태 업데이트\n(상태, 완료일시, 발송건수, 실패건수)
activate DB
DB --> Repository: 업데이트 완료
deactivate DB

View File

@ -28,7 +28,7 @@ end note
Service -> Repository: createRecording(meetingId, sessionId)
activate Repository
Repository -> DB: INSERT INTO recordings\n(recording_id, meeting_id, session_id,\nstatus='READY', created_at)
Repository -> DB: 녹음 세션 생성\n(녹음ID, 회의ID, 세션ID, 상태, 생성일시)
activate DB
DB --> Repository: recordingId 반환
deactivate DB
@ -62,7 +62,7 @@ deactivate AzureClient
Service -> Repository: updateRecordingStatus(recordingId, "RECORDING")
activate Repository
Repository -> DB: UPDATE recordings\nSET status='RECORDING',\nstarted_at=NOW(),\nstorage_path='{blobUrl}'\nWHERE recording_id='{recordingId}'
Repository -> DB: 녹음 상태 업데이트\n(상태='녹음중', 시작일시, 저장경로)
activate DB
DB --> Repository: 업데이트 완료
deactivate DB

View File

@ -21,7 +21,7 @@ activate Service
Service -> Repository: findMeetingById(meetingId)
activate Repository
Repository -> DB: SELECT * FROM meetings\nWHERE meeting_id = ?
Repository -> DB: 회의 정보 조회\n(회의ID 기준)
DB --> Repository: meeting data
Repository --> Service: Meeting entity
deactivate Repository
@ -54,7 +54,7 @@ deactivate Speaker
Service -> Repository: saveSttSession(session)
activate Repository
Repository -> DB: INSERT INTO stt_sessions\n(meeting_id, status, started_at)
Repository -> DB: STT 세션 저장\n(회의ID, 상태, 시작일시)
DB --> Repository: session saved
Repository --> Service: SttSession entity
deactivate Repository
@ -103,7 +103,7 @@ deactivate Speaker
Service -> Repository: saveSttSegment(segment)
activate Repository
Repository -> DB: INSERT INTO stt_segments\n(session_id, text, speaker_id, timestamp)
Repository -> DB: STT 세그먼트 저장\n(세션ID, 텍스트, 화자ID, 타임스탬프)
DB --> Repository: segment saved
Repository --> Service: saved
deactivate Repository

View File

@ -55,7 +55,7 @@ end note
Service -> TranscriptRepo: createTranscript(recordingId, text, metadata)
activate TranscriptRepo
TranscriptRepo -> DB: INSERT INTO transcripts\n(transcript_id, recording_id, speaker_id,\ntext, confidence, timestamp, warning_flag,\ncreated_at)
TranscriptRepo -> DB: 변환 결과 저장\n(텍스트ID, 녹음ID, 화자ID, 텍스트, 신뢰도, 타임스탬프, 경고플래그)
activate DB
DB --> TranscriptRepo: transcriptId 반환
deactivate DB
@ -68,7 +68,7 @@ deactivate TranscriptRepo
Service -> RecordingRepo: updateSpeakerInfo(recordingId, speakerId)
activate RecordingRepo
RecordingRepo -> DB: INSERT INTO speakers\n(recording_id, speaker_id, segment_count)\nON CONFLICT UPDATE segment_count
RecordingRepo -> DB: 화자 정보 저장/업데이트\n(녹음ID, 화자ID, 세그먼트수)
activate DB
DB --> RecordingRepo: 업데이트 완료
deactivate DB

View File

@ -20,7 +20,7 @@ activate Service
Service -> Repository: findSessionById(sessionId)
activate Repository
Repository -> DB: SELECT * FROM stt_sessions\nWHERE session_id = ?
Repository -> DB: STT 세션 조회\n(세션ID 기준)
DB --> Repository: session data
Repository --> Service: SttSession entity
deactivate Repository
@ -62,7 +62,7 @@ alt 실시간 변환 모드
Service -> Repository: saveSttSegment(segment)
activate Repository
Repository -> DB: INSERT INTO stt_segments\n(session_id, text, confidence, timestamp)
Repository -> DB: STT 세그먼트 저장\n(세션ID, 텍스트, 신뢰도, 타임스탬프)
DB --> Repository: saved
Repository --> Service: segment saved
deactivate Repository
@ -99,7 +99,7 @@ else 배치 변환 모드
Service -> Repository: updateSessionStatus(sessionId, "PROCESSING")
activate Repository
Repository -> DB: UPDATE stt_sessions\nSET status = 'PROCESSING'
Repository -> DB: 세션 상태 업데이트\n(상태='처리중')
DB --> Repository: updated
Repository --> Service: updated
deactivate Repository
@ -107,7 +107,7 @@ end
Service -> Repository: aggregateTranscription(sessionId)
activate Repository
Repository -> DB: SELECT text, timestamp\nFROM stt_segments\nWHERE session_id = ?\nORDER BY timestamp
Repository -> DB: 세그먼트 목록 조회\n(세션ID 기준, 타임스탬프 순 정렬)
DB --> Repository: segments
Repository --> Service: ordered segments
deactivate Repository
@ -122,7 +122,7 @@ end note
Service -> Repository: saveTranscription(fullText)
activate Repository
Repository -> DB: UPDATE stt_sessions\nSET full_text = ?,\nstatus = 'COMPLETED'
Repository -> DB: 전체 텍스트 저장 및 상태 업데이트\n(전체텍스트, 상태='완료')
DB --> Repository: saved
Repository --> Service: updated session
deactivate Repository

View File

@ -12,10 +12,16 @@ participant "UserRepository" as Repository
database "PostgreSQL<<E>>" as DB
database "Redis Cache<<E>>" as Cache
Gateway -> Controller: GET /api/v1/dashboard\nAuthorization: Bearer {token}
Gateway -> Controller: GET /api/dashboard?\npage=1&size=10&sort=createdAt,desc\nAuthorization: Bearer {token}
note right
페이지네이션 파라미터:
- page: 페이지 번호 (기본값: 1)
- size: 페이지 크기 (기본값: 10)
- sort: 정렬 기준 (기본값: createdAt,desc)
end note
activate Controller
Controller -> Service: getDashboard(userId)
Controller -> Service: getDashboard(userId, page, size, sort)
activate Service
Service -> Cache: get("dashboard:" + userId)
@ -28,10 +34,10 @@ end note
alt 캐시 존재
Cache --> Service: cached dashboard data
Service --> Controller: DashboardResponse
Service --> Controller: DashboardResponse\n{meetings, todos, activities, stats, pagination}
deactivate Service
Controller --> Gateway: 200 OK\n{dashboard data}
Controller --> Gateway: 200 OK\n{dashboard data + pagination}
deactivate Controller
else 캐시 미존재
@ -62,14 +68,14 @@ else 캐시 미존재
else
Service -> Repository: getRecentActivities(userId)
activate Repository
Repository -> DB: SELECT * FROM user_activities\nWHERE user_id = ?\nORDER BY created_at DESC\nLIMIT 10
Repository -> DB: 최근 활동 내역 조회\n(사용자ID, 최신순 정렬, 10건)
DB --> Repository: activities
Repository --> Service: recent activities
deactivate Repository
else
Service -> Repository: getUserStatistics(userId)
activate Repository
Repository -> DB: SELECT\n COUNT(DISTINCT meeting_id) as total_meetings,\n COUNT(DISTINCT todo_id) as total_todos,\n AVG(meeting_duration) as avg_duration\nFROM user_statistics\nWHERE user_id = ?
Repository -> DB: 사용자 통계 조회\n(총 회의수, 총 할일수, 평균 회의시간)
DB --> Repository: statistics
Repository --> Service: user statistics
deactivate Repository
@ -100,10 +106,26 @@ else 캐시 미존재
end note
Cache --> Service: cached
Service --> Controller: DashboardResponse\n{meetings, todos, activities, stats}
Service --> Controller: DashboardResponse\n{meetings, todos, activities, stats, pagination}
deactivate Service
Controller --> Gateway: 200 OK\n{dashboard data}
Controller --> Gateway: 200 OK\n{dashboard data + pagination}
note right
응답 형식:
{
"upcomingMeetings": [...],
"activeTodos": [...],
"recentActivities": [...],
"statistics": {...},
"pagination": {
"page": 1,
"size": 10,
"totalElements": 45,
"totalPages": 5,
"hasNext": true
}
}
end note
deactivate Controller
end

View File

@ -49,12 +49,12 @@ alt 인증 성공
Service -> Repository: findByUsername(username)
activate Repository
Repository -> DB: SELECT * FROM users\nWHERE username = ?
Repository -> DB: 사용자 정보 조회\n(사용자명 기준)
alt 사용자 존재
DB --> Repository: user data
else 신규 사용자
Repository -> DB: INSERT INTO users\n(username, email, department)
Repository -> DB: 신규 사용자 등록\n(사용자명, 이메일, 부서)
DB --> Repository: user created
note right
LDAP 정보 동기화:
@ -107,7 +107,7 @@ alt 인증 성공
Service -> Repository: updateLastLogin(userId)
activate Repository
Repository -> DB: UPDATE users\nSET last_login_at = NOW()
Repository -> DB: 최종 로그인 일시 업데이트\n(현재시각)
DB --> Repository: updated
Repository --> Service: updated
deactivate Repository
@ -125,10 +125,10 @@ else 인증 실패
Service -> Repository: incrementFailedAttempts(username)
activate Repository
Repository -> DB: UPDATE users\nSET failed_attempts = failed_attempts + 1
Repository -> DB: 실패 횟수 증가\n(failed_attempts + 1)
alt 실패 횟수 초과 (5회)
Repository -> DB: UPDATE users\nSET locked_until = NOW() + INTERVAL '30 minutes'
Repository -> DB: 계정 잠금 설정\n(잠금시간=현재+30분)
note right
계정 잠금:
- 5회 실패 시
@ -143,7 +143,19 @@ else 인증 실패
Service --> Controller: AuthenticationException
deactivate Service
Controller --> Gateway: 401 Unauthorized\n{error: "Invalid credentials"}
Controller --> Gateway: 401 Unauthorized
note right
에러 응답 형식:
{
"error": {
"code": "AUTHENTICATION_FAILED",
"message": "인증에 실패했습니다",
"details": "사용자명 또는 비밀번호가 올바르지 않습니다",
"timestamp": "2025-10-23T12:00:00Z",
"path": "/api/v1/auth/login"
}
}
end note
deactivate Controller
end

View File

@ -10,6 +10,16 @@ participant "Meeting Service" as Meeting
participant "Redis Cache" as Redis
participant "Notification Service" as Notification
note over Gateway
라우팅 규칙:
/api/meetings/** → Meeting Service
/api/minutes/** → Meeting Service
/api/dashboard → User Service
/api/notifications/** → Notification Service
/api/auth/** → User Service
/api/todos/** → Meeting Service
end note
autonumber
== Todo 완료 처리 ==

View File

@ -6,43 +6,59 @@ title 대시보드조회 외부 시퀀스
actor "사용자" as User
participant "Web App" as Frontend
participant "API Gateway" as Gateway
participant "Meeting Service" as Meeting
participant "User Service" as UserService
database "Redis Cache" as Cache
database "Meeting DB" as MeetingDB
database "User DB" as UserDB
note over Gateway
라우팅 규칙:
/api/meetings/** → Meeting Service
/api/minutes/** → Meeting Service
/api/dashboard → User Service
/api/notifications/** → Notification Service
/api/auth/** → User Service
/api/todos/** → Meeting Service
end note
User -> Frontend: 대시보드 접근
activate Frontend
Frontend -> Gateway: GET /api/meetings/dashboard
Frontend -> Gateway: GET /api/dashboard?\npage=1&size=10&sort=createdAt,desc
note right
페이지네이션 파라미터:
- page: 페이지 번호 (기본값: 1)
- size: 페이지 크기 (기본값: 10)
- sort: 정렬 기준 (기본값: createdAt,desc)
end note
activate Gateway
Gateway -> Meeting: GET /dashboard
activate Meeting
Gateway -> UserService: GET /dashboard?\npage=1&size=10&sort=createdAt,desc
activate UserService
' 캐시 조회
Meeting -> Cache: GET dashboard:{userId}
UserService -> Cache: GET dashboard:{userId}
activate Cache
Cache --> Meeting: 캐시 조회 결과
Cache --> UserService: 캐시 조회 결과
deactivate Cache
alt Cache Hit
Meeting -> Meeting: 캐시 데이터 반환
UserService -> UserService: 캐시 데이터 반환
else Cache Miss
Meeting -> MeetingDB: 대시보드 데이터 조회\n- 예정된 회의 목록\n- 진행 중 Todo 목록\n- 최근 회의록 목록\n- 공유받은 회의록 목록\n- 통계 정보
activate MeetingDB
MeetingDB --> Meeting: 조회 결과
deactivate MeetingDB
UserService -> UserDB: 대시보드 데이터 조회\n- 예정된 회의 목록\n- 진행 중 Todo 목록\n- 최근 회의록 목록\n- 공유받은 회의록 목록\n- 통계 정보
activate UserDB
UserDB --> UserService: 조회 결과
deactivate UserDB
Meeting -> Cache: SET dashboard:{userId}\n(TTL: 5분)
UserService -> Cache: SET dashboard:{userId}\n(TTL: 5분)
activate Cache
Cache --> Meeting: 캐시 저장 완료
Cache --> UserService: 캐시 저장 완료
deactivate Cache
end
Meeting --> Gateway: 대시보드 데이터 응답\n{\n "upcomingMeetings": [...],\n "activeTodos": [...],\n "recentMinutes": [...],\n "sharedMinutes": [...],\n "statistics": {...}\n}
deactivate Meeting
UserService --> Gateway: 대시보드 데이터 응답\n{\n "upcomingMeetings": [...],\n "activeTodos": [...],\n "recentMinutes": [...],\n "sharedMinutes": [...],\n "statistics": {...},\n "pagination": {\n "page": 1,\n "size": 10,\n "totalElements": 45,\n "totalPages": 5,\n "hasNext": true\n }\n}
deactivate UserService
Gateway --> Frontend: 200 OK\n대시보드 데이터
Gateway --> Frontend: 200 OK\n대시보드 데이터 + 페이지네이션 정보
deactivate Gateway
Frontend -> Frontend: 대시보드 화면 렌더링\n- 예정된 회의 표시\n- Todo 목록 표시\n- 최근/공유 회의록 표시\n- 통계 차트 표시

View File

@ -11,6 +11,16 @@ participant "Redis Cache" as Redis
participant "Azure Event Hubs" as EventHub
participant "Notification Service" as Notification
note over Gateway
라우팅 규칙:
/api/meetings/** → Meeting Service
/api/minutes/** → Meeting Service
/api/dashboard → User Service
/api/notifications/** → Notification Service
/api/auth/** → User Service
/api/todos/** → Meeting Service
end note
== 회의록 공유 설정 ==
User -> Web: 공유 버튼 클릭
activate Web

View File

@ -12,6 +12,16 @@ database "Redis Cache" as Cache
database "Meeting DB" as MeetingDB
participant "WebSocket" as WS
note over Gateway
라우팅 규칙:
/api/meetings/** → Meeting Service
/api/minutes/** → Meeting Service
/api/dashboard → User Service
/api/notifications/** → Notification Service
/api/auth/** → User Service
/api/todos/** → Meeting Service
end note
== 회의록 상세 조회 ==
User -> Frontend: 회의록 클릭

View File

@ -12,6 +12,16 @@ participant "AI Service" as AI
database "Redis Cache" as Cache
queue "Azure Event Hubs" as EventHub
note over Gateway
라우팅 규칙:
/api/meetings/** → Meeting Service
/api/minutes/** → Meeting Service
/api/dashboard → User Service
/api/notifications/** → Notification Service
/api/auth/** → User Service
/api/todos/** → Meeting Service
end note
== 회의 시작 ==
User -> Frontend: 회의 시작 버튼 클릭

View File

@ -13,6 +13,16 @@ participant "Azure Event Hubs" as EventHub
participant "Notification Service" as Notification
participant "Email Service" as Email
note over Gateway
라우팅 규칙:
/api/meetings/** → Meeting Service
/api/minutes/** → Meeting Service
/api/dashboard → User Service
/api/notifications/** → Notification Service
/api/auth/** → User Service
/api/todos/** → Meeting Service
end note
== 회의 예약 ==
User -> WebApp: 회의 정보 입력\n(제목, 날짜/시간, 장소, 참석자)
activate WebApp

View File

@ -12,6 +12,16 @@ participant "AI Service" as AI
participant "Notification Service" as Notification
participant "Azure Event Hubs" as EventHub
note over Gateway
라우팅 규칙:
/api/meetings/** → Meeting Service
/api/minutes/** → Meeting Service
/api/dashboard → User Service
/api/notifications/** → Notification Service
/api/auth/** → User Service
/api/todos/** → Meeting Service
end note
== 회의 종료 ==
User -> WebApp: 회의 종료 버튼 클릭
WebApp -> Gateway: POST /meetings/{meetingId}/end