diff --git a/claude/sample-logical-architecture.mmd b/claude/sample-logical-architecture.mmd new file mode 100644 index 0000000..1becba2 --- /dev/null +++ b/claude/sample-logical-architecture.mmd @@ -0,0 +1 @@ +404: Not Found \ No newline at end of file diff --git a/claude/sequence-inner-design.md b/claude/sequence-inner-design.md index 586c62c..ea62f38 100644 --- a/claude/sequence-inner-design.md +++ b/claude/sequence-inner-design.md @@ -11,6 +11,8 @@ - **외부시퀀스설계서에서 설계한 플로우와 일치**해야 함 - UI/UX설계서의 '사용자 플로우'참조하여 설계 - 마이크로서비스 내부의 처리 흐름을 표시 +- 요청/응답을 **한글로 표시** +- Repository CRUD 처리를 한글로 설명하고 SQL은 사용하지 말것 - **각 서비스-시나리오별로 분리하여 각각 작성** - 각 서비스별 주요 시나리오마다 독립적인 시퀀스 설계 수행 - 프론트엔드와 백엔드 책임 분리: 프론트엔드에서 할 수 있는 것은 백엔드로 요청 안하게 함 diff --git a/design/backend/sequence/inner/ai-Todo자동추출.puml b/design/backend/sequence/inner/ai-Todo자동추출.puml index 1f99030..0caa217 100644 --- a/design/backend/sequence/inner/ai-Todo자동추출.puml +++ b/design/backend/sequence/inner/ai-Todo자동추출.puml @@ -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: 업데이트 완료 diff --git a/design/backend/sequence/inner/ai-결정사항제안.puml b/design/backend/sequence/inner/ai-결정사항제안.puml index 2acae10..9fb40d0 100644 --- a/design/backend/sequence/inner/ai-결정사항제안.puml +++ b/design/backend/sequence/inner/ai-결정사항제안.puml @@ -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: 회의 정보 diff --git a/design/backend/sequence/inner/ai-관련회의록연결.puml b/design/backend/sequence/inner/ai-관련회의록연결.puml index 2b4ca14..dc09a55 100644 --- a/design/backend/sequence/inner/ai-관련회의록연결.puml +++ b/design/backend/sequence/inner/ai-관련회의록연결.puml @@ -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 저장 데이터: diff --git a/design/backend/sequence/inner/ai-논의사항제안.puml b/design/backend/sequence/inner/ai-논의사항제안.puml index 1780ca5..64aa9f5 100644 --- a/design/backend/sequence/inner/ai-논의사항제안.puml +++ b/design/backend/sequence/inner/ai-논의사항제안.puml @@ -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: 이미 논의한 주제 목록 diff --git a/design/backend/sequence/inner/ai-맥락기반용어설명.puml b/design/backend/sequence/inner/ai-맥락기반용어설명.puml index d68c403..ada0a21 100644 --- a/design/backend/sequence/inner/ai-맥락기반용어설명.puml +++ b/design/backend/sequence/inner/ai-맥락기반용어설명.puml @@ -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: 기본 용어 정의 diff --git a/design/backend/sequence/inner/ai-전문용어감지.puml b/design/backend/sequence/inner/ai-전문용어감지.puml index b1c58c1..7865301 100644 --- a/design/backend/sequence/inner/ai-전문용어감지.puml +++ b/design/backend/sequence/inner/ai-전문용어감지.puml @@ -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: 산업 표준용어 목록 diff --git a/design/backend/sequence/inner/ai-회의록개선.puml b/design/backend/sequence/inner/ai-회의록개선.puml index 3e67364..a12683e 100644 --- a/design/backend/sequence/inner/ai-회의록개선.puml +++ b/design/backend/sequence/inner/ai-회의록개선.puml @@ -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 버전 연결 정보: diff --git a/design/backend/sequence/inner/ai-회의록자동작성.puml b/design/backend/sequence/inner/ai-회의록자동작성.puml index 79b1459..bf81ea2 100644 --- a/design/backend/sequence/inner/ai-회의록자동작성.puml +++ b/design/backend/sequence/inner/ai-회의록자동작성.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-Todo완료처리.puml b/design/backend/sequence/inner/meeting-Todo완료처리.puml index b48fff6..11d7517 100644 --- a/design/backend/sequence/inner/meeting-Todo완료처리.puml +++ b/design/backend/sequence/inner/meeting-Todo완료처리.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-Todo할당.puml b/design/backend/sequence/inner/meeting-Todo할당.puml index 2a9f0a2..39ef3cc 100644 --- a/design/backend/sequence/inner/meeting-Todo할당.puml +++ b/design/backend/sequence/inner/meeting-Todo할당.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-검증완료.puml b/design/backend/sequence/inner/meeting-검증완료.puml index 6664bbe..c7d5808 100644 --- a/design/backend/sequence/inner/meeting-검증완료.puml +++ b/design/backend/sequence/inner/meeting-검증완료.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-대시보드조회.puml b/design/backend/sequence/inner/meeting-대시보드조회.puml index a5690ac..a045256 100644 --- a/design/backend/sequence/inner/meeting-대시보드조회.puml +++ b/design/backend/sequence/inner/meeting-대시보드조회.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-실시간수정동기화.puml b/design/backend/sequence/inner/meeting-실시간수정동기화.puml index 85f1a2a..65a83bd 100644 --- a/design/backend/sequence/inner/meeting-실시간수정동기화.puml +++ b/design/backend/sequence/inner/meeting-실시간수정동기화.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-최종회의록확정.puml b/design/backend/sequence/inner/meeting-최종회의록확정.puml index fc82064..fcd5e27 100644 --- a/design/backend/sequence/inner/meeting-최종회의록확정.puml +++ b/design/backend/sequence/inner/meeting-최종회의록확정.puml @@ -4,7 +4,7 @@ title 최종 회의록 확정 내부 시퀀스 participant "API Gateway<>" 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<>" as DB database "Redis Cache<>" as Cache queue "Event Hub<>" 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 비동기 이벤트: diff --git a/design/backend/sequence/inner/meeting-충돌해결.puml b/design/backend/sequence/inner/meeting-충돌해결.puml index ab3e3c9..a46cf07 100644 --- a/design/backend/sequence/inner/meeting-충돌해결.puml +++ b/design/backend/sequence/inner/meeting-충돌해결.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-템플릿선택.puml b/design/backend/sequence/inner/meeting-템플릿선택.puml index 5f96a1d..8b22921 100644 --- a/design/backend/sequence/inner/meeting-템플릿선택.puml +++ b/design/backend/sequence/inner/meeting-템플릿선택.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-회의록공유.puml b/design/backend/sequence/inner/meeting-회의록공유.puml index 7fa7384..a9603ff 100644 --- a/design/backend/sequence/inner/meeting-회의록공유.puml +++ b/design/backend/sequence/inner/meeting-회의록공유.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-회의록목록조회.puml b/design/backend/sequence/inner/meeting-회의록목록조회.puml index 753982d..764fd31 100644 --- a/design/backend/sequence/inner/meeting-회의록목록조회.puml +++ b/design/backend/sequence/inner/meeting-회의록목록조회.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-회의록상세조회.puml b/design/backend/sequence/inner/meeting-회의록상세조회.puml index 4a0ad09..5d89426 100644 --- a/design/backend/sequence/inner/meeting-회의록상세조회.puml +++ b/design/backend/sequence/inner/meeting-회의록상세조회.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-회의록수정.puml b/design/backend/sequence/inner/meeting-회의록수정.puml index 6660d7f..fc536ac 100644 --- a/design/backend/sequence/inner/meeting-회의록수정.puml +++ b/design/backend/sequence/inner/meeting-회의록수정.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-회의록확정.puml b/design/backend/sequence/inner/meeting-회의록확정.puml index 58fcc86..34f9591 100644 --- a/design/backend/sequence/inner/meeting-회의록확정.puml +++ b/design/backend/sequence/inner/meeting-회의록확정.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-회의시작.puml b/design/backend/sequence/inner/meeting-회의시작.puml index 1ad0709..b77a769 100644 --- a/design/backend/sequence/inner/meeting-회의시작.puml +++ b/design/backend/sequence/inner/meeting-회의시작.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-회의예약.puml b/design/backend/sequence/inner/meeting-회의예약.puml index e1a35d1..f297424 100644 --- a/design/backend/sequence/inner/meeting-회의예약.puml +++ b/design/backend/sequence/inner/meeting-회의예약.puml @@ -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 diff --git a/design/backend/sequence/inner/meeting-회의종료.puml b/design/backend/sequence/inner/meeting-회의종료.puml index 0805d74..a28c022 100644 --- a/design/backend/sequence/inner/meeting-회의종료.puml +++ b/design/backend/sequence/inner/meeting-회의종료.puml @@ -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 회의록 완성도 표시: diff --git a/design/backend/sequence/inner/notification-Todo알림발송.puml b/design/backend/sequence/inner/notification-Todo알림발송.puml index 766af50..dbc32db 100644 --- a/design/backend/sequence/inner/notification-Todo알림발송.puml +++ b/design/backend/sequence/inner/notification-Todo알림발송.puml @@ -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 diff --git a/design/backend/sequence/inner/notification-리마인더발송.puml b/design/backend/sequence/inner/notification-리마인더발송.puml index edcd86c..db22aa3 100644 --- a/design/backend/sequence/inner/notification-리마인더발송.puml +++ b/design/backend/sequence/inner/notification-리마인더발송.puml @@ -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 diff --git a/design/backend/sequence/inner/notification-알림발송.puml b/design/backend/sequence/inner/notification-알림발송.puml index 6048097..fa71196 100644 --- a/design/backend/sequence/inner/notification-알림발송.puml +++ b/design/backend/sequence/inner/notification-알림발송.puml @@ -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 diff --git a/design/backend/sequence/inner/notification-초대알림발송.puml b/design/backend/sequence/inner/notification-초대알림발송.puml index e376809..29bea4b 100644 --- a/design/backend/sequence/inner/notification-초대알림발송.puml +++ b/design/backend/sequence/inner/notification-초대알림발송.puml @@ -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 diff --git a/design/backend/sequence/inner/stt-음성녹음시작.puml b/design/backend/sequence/inner/stt-음성녹음시작.puml index 6f54fee..5cc2ce4 100644 --- a/design/backend/sequence/inner/stt-음성녹음시작.puml +++ b/design/backend/sequence/inner/stt-음성녹음시작.puml @@ -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 diff --git a/design/backend/sequence/inner/stt-음성녹음인식.puml b/design/backend/sequence/inner/stt-음성녹음인식.puml index 137c63c..68d5ae8 100644 --- a/design/backend/sequence/inner/stt-음성녹음인식.puml +++ b/design/backend/sequence/inner/stt-음성녹음인식.puml @@ -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 diff --git a/design/backend/sequence/inner/stt-음성텍스트변환.puml b/design/backend/sequence/inner/stt-음성텍스트변환.puml index b8e817f..a377d46 100644 --- a/design/backend/sequence/inner/stt-음성텍스트변환.puml +++ b/design/backend/sequence/inner/stt-음성텍스트변환.puml @@ -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 diff --git a/design/backend/sequence/inner/stt-텍스트변환.puml b/design/backend/sequence/inner/stt-텍스트변환.puml index e593ca5..c234277 100644 --- a/design/backend/sequence/inner/stt-텍스트변환.puml +++ b/design/backend/sequence/inner/stt-텍스트변환.puml @@ -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 diff --git a/design/backend/sequence/inner/user-대시보드조회.puml b/design/backend/sequence/inner/user-대시보드조회.puml index 4f71532..0becaee 100644 --- a/design/backend/sequence/inner/user-대시보드조회.puml +++ b/design/backend/sequence/inner/user-대시보드조회.puml @@ -12,10 +12,16 @@ participant "UserRepository" as Repository database "PostgreSQL<>" as DB database "Redis Cache<>" 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 diff --git a/design/backend/sequence/inner/user-사용자인증.puml b/design/backend/sequence/inner/user-사용자인증.puml index c33e425..6d99068 100644 --- a/design/backend/sequence/inner/user-사용자인증.puml +++ b/design/backend/sequence/inner/user-사용자인증.puml @@ -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 diff --git a/design/backend/sequence/outer/Todo완료및회의록반영.puml b/design/backend/sequence/outer/Todo완료및회의록반영.puml index 06a2cb4..a9f4e80 100644 --- a/design/backend/sequence/outer/Todo완료및회의록반영.puml +++ b/design/backend/sequence/outer/Todo완료및회의록반영.puml @@ -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 완료 처리 == diff --git a/design/backend/sequence/outer/대시보드조회.puml b/design/backend/sequence/outer/대시보드조회.puml index 2f28e9c..7ccfd37 100644 --- a/design/backend/sequence/outer/대시보드조회.puml +++ b/design/backend/sequence/outer/대시보드조회.puml @@ -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- 통계 차트 표시 diff --git a/design/backend/sequence/outer/회의록공유.puml b/design/backend/sequence/outer/회의록공유.puml index eb09a83..9ee8d15 100644 --- a/design/backend/sequence/outer/회의록공유.puml +++ b/design/backend/sequence/outer/회의록공유.puml @@ -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 diff --git a/design/backend/sequence/outer/회의록상세조회및수정.puml b/design/backend/sequence/outer/회의록상세조회및수정.puml index 39e5d12..146fa68 100644 --- a/design/backend/sequence/outer/회의록상세조회및수정.puml +++ b/design/backend/sequence/outer/회의록상세조회및수정.puml @@ -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: 회의록 클릭 diff --git a/design/backend/sequence/outer/회의시작및실시간회의록작성.puml b/design/backend/sequence/outer/회의시작및실시간회의록작성.puml index d52726e..374225e 100644 --- a/design/backend/sequence/outer/회의시작및실시간회의록작성.puml +++ b/design/backend/sequence/outer/회의시작및실시간회의록작성.puml @@ -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: 회의 시작 버튼 클릭 diff --git a/design/backend/sequence/outer/회의예약및참석자초대.puml b/design/backend/sequence/outer/회의예약및참석자초대.puml index 4a1a463..58eebe2 100644 --- a/design/backend/sequence/outer/회의예약및참석자초대.puml +++ b/design/backend/sequence/outer/회의예약및참석자초대.puml @@ -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 diff --git a/design/backend/sequence/outer/회의종료및최종확정.puml b/design/backend/sequence/outer/회의종료및최종확정.puml index 25f5d18..49656df 100644 --- a/design/backend/sequence/outer/회의종료및최종확정.puml +++ b/design/backend/sequence/outer/회의종료및최종확정.puml @@ -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