mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 06:46:24 +00:00
Merge branch 'wip/document-yabo'
This commit is contained in:
commit
bca8d6e729
@ -1,115 +1,111 @@
|
||||
# STT Service API 설계 완료
|
||||
# API설계가이드
|
||||
|
||||
## 작업 결과
|
||||
[요청사항]
|
||||
- <작성원칙>을 준용하여 설계
|
||||
- <작성순서>에 따라 설계
|
||||
- [결과파일] 안내에 따라 파일 작성
|
||||
- 최종 완료 후 API 확인 방법 안내
|
||||
- https://editor.swagger.io/ 접근
|
||||
- 생성된 swagger yaml파일을 붙여서 확인 및 테스트
|
||||
|
||||
### 생성된 파일
|
||||
- **파일 경로**: `C:\Users\KTDS\home\workspace\HGZero\design\backend\api\stt-service-api.yaml`
|
||||
- **형식**: OpenAPI 3.0.3
|
||||
- **검증 상태**: ✅ 검증 완료 (swagger-cli)
|
||||
[가이드]
|
||||
<작성 원칙>
|
||||
- 각 서비스 API는 독립적으로 완전한 명세를 포함
|
||||
- 공통 스키마는 각 서비스에서 필요에 따라 직접 정의
|
||||
- 서비스 간 의존성을 최소화하여 독립 배포 가능
|
||||
- 중복되는 스키마가 많아질 경우에만 공통 파일 도입 검토
|
||||
<작성순서>
|
||||
- 준비:
|
||||
- 유저스토리, 외부시퀀스설계서, 내부시퀀스설계서 분석 및 이해
|
||||
- 실행:
|
||||
- <병렬처리> 안내에 따라 동시 수행
|
||||
- <API선정원칙>에 따라 API 선정
|
||||
- <파일작성안내>에 따라 작성
|
||||
- <검증방법>에 따라 작성된 YAML의 문법 및 구조 검증 수행
|
||||
- 검토:
|
||||
- <작성원칙> 준수 검토
|
||||
- 스쿼드 팀원 리뷰: 누락 및 개선 사항 검토
|
||||
- 수정 사항 선택 및 반영
|
||||
|
||||
### API 개요
|
||||
<API선정원칙>
|
||||
- 유저스토리와 매칭 되어야 함. 불필요한 추가 설계 금지
|
||||
(유저스토리 ID를 x-user-story 확장 필드에 명시)
|
||||
- '외부시퀀스설계서'/'내부시퀀스설계서'와 일관성 있게 선정
|
||||
|
||||
#### 1. Recording API (음성 녹음 관리)
|
||||
- `POST /recordings/prepare` - 회의 녹음 준비
|
||||
- `POST /recordings/{recordingId}/start` - 음성 녹음 시작
|
||||
- `POST /recordings/{recordingId}/stop` - 음성 녹음 중지
|
||||
- `GET /recordings/{recordingId}` - 녹음 정보 조회
|
||||
<파일작성안내>
|
||||
- OpenAPI 3.0 스펙 준용
|
||||
- **servers 섹션 필수화**
|
||||
- 모든 OpenAPI 명세에 servers 섹션 포함
|
||||
- SwaggerHub Mock URL을 첫 번째 옵션으로 배치
|
||||
- **example 데이터 권장**
|
||||
- 스키마에 example을 추가하여 Swagger UI에서 테스트 할 수 있게함
|
||||
- **테스트 시나리오 포함**
|
||||
- 각 API 엔드포인트별 테스트 케이스 정의
|
||||
- 성공/실패 케이스 모두 포함
|
||||
- 작성 형식
|
||||
- YAML 형식의 OpenAPI 3.0 명세
|
||||
- 각 API별 필수 항목:
|
||||
- summary: API 목적 설명
|
||||
- operationId: 고유 식별자
|
||||
- x-user-story: 유저스토리 ID
|
||||
- x-controller: 담당 컨트롤러
|
||||
- tags: API 그룹 분류
|
||||
- requestBody/responses: 상세 스키마
|
||||
- 각 서비스 파일에 필요한 모든 스키마 포함:
|
||||
- components/schemas: 요청/응답 모델
|
||||
- components/parameters: 공통 파라미터
|
||||
- components/responses: 공통 응답
|
||||
- components/securitySchemes: 인증 방식
|
||||
|
||||
#### 2. Transcription API (음성-텍스트 변환)
|
||||
- `POST /transcripts/stream` - 실시간 음성-텍스트 변환 (스트리밍)
|
||||
- `POST /transcripts/batch` - 배치 음성-텍스트 변환
|
||||
- `POST /transcripts/callback` - 배치 변환 완료 콜백
|
||||
- `GET /transcripts/{recordingId}` - 변환 텍스트 전체 조회
|
||||
<파일 구조>
|
||||
```
|
||||
design/backend/api/
|
||||
├── {service-name}-api.yaml # 각 마이크로서비스별 API 명세
|
||||
└── ... # 추가 서비스들
|
||||
|
||||
#### 3. Speaker API (화자 식별 및 관리)
|
||||
- `POST /speakers/identify` - 화자 식별
|
||||
- `GET /speakers/{speakerId}` - 화자 정보 조회
|
||||
- `PUT /speakers/{speakerId}` - 화자 정보 업데이트
|
||||
- `GET /recordings/{recordingId}/speakers` - 녹음의 화자 목록 조회
|
||||
|
||||
### 주요 특징
|
||||
|
||||
#### 1. 유저스토리 매핑
|
||||
모든 API는 유저스토리와 매핑되어 있습니다:
|
||||
- **UFR-STT-010** (음성녹음인식): Recording API, Speaker API
|
||||
- **UFR-STT-020** (텍스트변환): Transcription API
|
||||
|
||||
#### 2. 완전한 스키마 정의
|
||||
- 25개의 스키마 정의
|
||||
- 모든 Request/Response 모델 포함
|
||||
- Example 데이터 포함으로 Swagger UI에서 즉시 테스트 가능
|
||||
|
||||
#### 3. Azure 통합
|
||||
- Azure Speech Service 연동
|
||||
- Azure Blob Storage 통합
|
||||
- Azure Event Hubs 이벤트 발행
|
||||
|
||||
#### 4. 실시간 처리
|
||||
- WebSocket 기반 스트리밍 지원
|
||||
- 실시간 인식 지연: < 1초
|
||||
- 화자 식별 정확도: > 90%
|
||||
|
||||
#### 5. 성능 정보
|
||||
각 API의 예상 처리 시간 명시:
|
||||
- 녹음 준비: ~1.1초
|
||||
- 실시간 변환: 1-4초
|
||||
- 배치 변환: 7-33초
|
||||
|
||||
### API 확인 방법
|
||||
|
||||
#### 1. Swagger Editor 사용
|
||||
1. https://editor.swagger.io/ 접속
|
||||
2. 생성된 YAML 파일 내용 복사하여 붙여넣기
|
||||
3. 우측 패널에서 API 문서 확인 및 테스트
|
||||
|
||||
#### 2. 로컬 Swagger UI 실행
|
||||
```bash
|
||||
# Swagger UI Docker 실행
|
||||
docker run -p 8080:8080 -e SWAGGER_JSON=/api/stt-service-api.yaml \
|
||||
-v C:\Users\KTDS\home\workspace\HGZero\design\backend\api:/api \
|
||||
swaggerapi/swagger-ui
|
||||
|
||||
# 브라우저에서 http://localhost:8080 접속
|
||||
예시:
|
||||
├── profile-service-api.yaml # 프로파일 서비스 API
|
||||
├── order-service-api.yaml # 주문 서비스 API
|
||||
└── payment-service-api.yaml # 결제 서비스 API
|
||||
```
|
||||
|
||||
#### 3. VS Code Extension
|
||||
- **확장**: Swagger Viewer
|
||||
- YAML 파일 열고 `Shift + Alt + P` 실행
|
||||
- 미리보기에서 API 문서 확인
|
||||
- 파일명 규칙
|
||||
- 서비스명은 kebab-case로 작성
|
||||
- 파일명 형식: {service-name}-api.yaml
|
||||
- 서비스명은 유저스토리의 '서비스' 항목을 영문으로 변환하여 사용
|
||||
|
||||
### 설계 원칙 준수
|
||||
<병렬처리>
|
||||
- **의존성 분석 선행**: 병렬 처리 전 반드시 의존성 파악
|
||||
- **순차 처리 필요시**: 무리한 병렬화보다는 안전한 순차 처리
|
||||
- **검증 단계 필수**: 병렬 처리 후 통합 검증
|
||||
|
||||
✅ **유저스토리 기반 설계**
|
||||
- 모든 API에 x-user-story 필드 명시
|
||||
- 불필요한 API 추가 없음
|
||||
<검증방법>
|
||||
- swagger-cli를 사용한 자동 검증 수행
|
||||
- 검증 명령어: `swagger-cli validate {파일명}`
|
||||
- swagger-cli가 없을 경우 자동 설치:
|
||||
```bash
|
||||
# swagger-cli 설치 확인 및 자동 설치
|
||||
command -v swagger-cli >/dev/null 2>&1 || npm install -g @apidevtools/swagger-cli
|
||||
|
||||
# 검증 실행
|
||||
swagger-cli validate design/backend/api/*.yaml
|
||||
```
|
||||
- 검증 항목:
|
||||
- OpenAPI 3.0 스펙 준수
|
||||
- YAML 구문 오류
|
||||
- 스키마 참조 유효성
|
||||
- 필수 필드 존재 여부
|
||||
|
||||
✅ **시퀀스 일관성**
|
||||
- 내부 시퀀스 설계와 완전히 일치
|
||||
- 모든 처리 흐름 반영
|
||||
[참고자료]
|
||||
- 유저스토리
|
||||
- 외부시퀀스설계서
|
||||
- 내부시퀀스설계서
|
||||
- OpenAPI 스펙: https://swagger.io/specification/
|
||||
|
||||
✅ **OpenAPI 3.0 표준**
|
||||
- servers 섹션 필수 포함
|
||||
- 완전한 스키마 정의
|
||||
- JWT 인증 방식 명시
|
||||
[예시]
|
||||
- swagger api yaml: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/sample-swagger-api.yaml
|
||||
- API설계서: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/sample-API%20설계서.md
|
||||
|
||||
✅ **Example 데이터**
|
||||
- 모든 스키마에 example 포함
|
||||
- 실제 테스트 가능한 데이터
|
||||
|
||||
✅ **검증 완료**
|
||||
- swagger-cli 자동 검증 통과
|
||||
- YAML 구문 오류 없음
|
||||
- 스키마 참조 유효성 확인
|
||||
|
||||
### 다음 단계
|
||||
|
||||
1. **Meeting Service API 설계** (회의, 회의록, Todo 통합)
|
||||
2. **AI Service API 설계** (회의록 자동 작성, RAG 기능)
|
||||
3. **User Service API 설계** (인증 전용)
|
||||
4. **Notification Service API 설계** (알림 발송)
|
||||
|
||||
---
|
||||
|
||||
**작성자**: 준호 (Backend Developer)
|
||||
**작성일**: 2025-01-23
|
||||
**검증 도구**: swagger-cli v4.0.4
|
||||
[결과파일]
|
||||
- 각 서비스별로 별도의 YAML 파일 생성
|
||||
- design/backend/api/*.yaml (OpenAPI 형식)
|
||||
@ -1,95 +0,0 @@
|
||||
# API 누락 요약표 (회의 진행 실시간 기능)
|
||||
|
||||
**작성일**: 2025년 10월 28일
|
||||
**근거 문서**: [API리뷰-프로토타입vs구현.md](./API리뷰-프로토타입vs구현.md)
|
||||
|
||||
---
|
||||
|
||||
## 🔴 P0 (치명적 - 즉시 구현 필요)
|
||||
|
||||
| # | API | 프로토타입 근거 | 영향받는 유저스토리 | 비고 |
|
||||
|---|-----|----------------|-------------------|------|
|
||||
| 1 | `PUT /api/meetings/{meetingId}/memo` | [05-회의진행.html:1119-1143](../design/uiux/prototype/05-회의진행.html#L1119-L1143) | US-07, US-10 | 메모 저장 불가, 데이터 손실 위험 |
|
||||
| 2 | `GET /api/ai/suggestions/realtime/{meetingId}` | [05-회의진행.html:767-806](../design/uiux/prototype/05-회의진행.html#L767-L806) | US-07, US-08 | AI 실시간 추천 완전 미동작 |
|
||||
| 3 | `POST /api/stt/recordings/{recordingId}/pause` | [05-회의진행.html:1212-1243](../design/uiux/prototype/05-회의진행.html#L1212-L1243) | US-06 | 녹음 일시정지 불가 |
|
||||
| 4 | `POST /api/stt/recordings/{recordingId}/resume` | [05-회의진행.html:1212-1243](../design/uiux/prototype/05-회의진행.html#L1212-L1243) | US-06 | 녹음 재개 불가 |
|
||||
|
||||
---
|
||||
|
||||
## 🟡 P1 (중요 - 우선 구현 필요)
|
||||
|
||||
| # | API | 프로토타입 근거 | 영향받는 유저스토리 | 비고 |
|
||||
|---|-----|----------------|-------------------|------|
|
||||
| 5 | `POST /api/ai/suggestions/{suggestionId}/adopt` | [05-회의진행.html:1070-1097](../design/uiux/prototype/05-회의진행.html#L1070-L1097) | US-07 | AI 추천 채택 불가, 수동 복붙 필요 |
|
||||
| 6 | `GET /api/ai/terms/search` | [05-회의진행.html:1145-1182](../design/uiux/prototype/05-회의진행.html#L1145-L1182) | US-09 | 용어 검색 불가 |
|
||||
|
||||
---
|
||||
|
||||
## 🟢 P2 (일반 - 향후 개선)
|
||||
|
||||
| # | API | 프로토타입 근거 | 영향받는 유저스토리 | 비고 |
|
||||
|---|-----|----------------|-------------------|------|
|
||||
| 7 | `GET /api/ai/terms/{termName}/detail` | [05-회의진행.html:1305-1308](../design/uiux/prototype/05-회의진행.html#L1305-L1308) | US-09 | 용어 상세 조회 불가 |
|
||||
|
||||
---
|
||||
|
||||
## 📊 탭별 API 구현 현황
|
||||
|
||||
| 탭 | 기능 | 필요 API 수 | 구현 API 수 | 구현률 | 우선순위 |
|
||||
|----|------|------------|------------|--------|---------|
|
||||
| **참석자** | 참석자 관리 | 4 | 4 | 100% ✅ | - |
|
||||
| **AI 메모** | 실시간 메모 & AI 추천 | 3 | 0 | 0% ❌ | P0 (3개) |
|
||||
| **용어사전** | 용어 감지/검색 | 3 | 1 | 33% ⚠️ | P1 (1개), P2 (1개) |
|
||||
| **관련회의록** | 유사 회의록 찾기 | 2 | 2 | 100% ✅ | - |
|
||||
| **녹음 제어** | 녹음 상태 관리 | 5 | 3 | 60% ⚠️ | P0 (2개) |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 구현 권장 순서
|
||||
|
||||
### Sprint 1 (1주) - P0 필수 기능
|
||||
1. **메모 저장 API** (`PUT /api/meetings/{meetingId}/memo`)
|
||||
- 예상 작업: 4시간
|
||||
- 구현 위치: `MeetingController.java`
|
||||
|
||||
2. **AI 실시간 추천 API** (`GET /api/ai/suggestions/realtime/{meetingId}`)
|
||||
- 예상 작업: 8시간
|
||||
- 구현 위치: `SuggestionController.java`
|
||||
- 폴링 또는 SSE 방식 선택 필요
|
||||
|
||||
3. **녹음 일시정지/재개 API** (`POST pause`, `POST resume`)
|
||||
- 예상 작업: 6시간
|
||||
- 구현 위치: `RecordingController.java`
|
||||
|
||||
### Sprint 2 (3일) - P1 중요 기능
|
||||
4. **AI 추천 채택 API** (`POST /api/ai/suggestions/{suggestionId}/adopt`)
|
||||
- 예상 작업: 4시간
|
||||
|
||||
5. **용어 검색 API** (`GET /api/ai/terms/search`)
|
||||
- 예상 작업: 3시간
|
||||
|
||||
### Sprint 3 (2일) - P2 보조 기능
|
||||
6. **용어 상세 조회 API** (`GET /api/ai/terms/{termName}/detail`)
|
||||
- 예상 작업: 4시간
|
||||
|
||||
---
|
||||
|
||||
## 📝 비고
|
||||
|
||||
### 구현 고려사항
|
||||
1. **AI 실시간 추천**: 폴링(Polling) vs SSE(Server-Sent Events) 방식 결정 필요
|
||||
2. **메모 저장**: 개인별 메모 vs 공유 메모 정책 확인 필요
|
||||
3. **녹음 일시정지**: 타이머 상태 동기화 로직 필요
|
||||
4. **용어 검색**: 조직 용어 사전과 회의별 용어 통합 검색 정책 필요
|
||||
|
||||
### 테스트 시나리오
|
||||
- [ ] 회의 진행 중 메모 작성 후 저장 → 다시 로드 시 메모 복원 확인
|
||||
- [ ] AI 추천 메모 실시간 조회 → 5초마다 새 추천 확인
|
||||
- [ ] AI 추천 채택 → 입력창에 시간 포함하여 추가 확인
|
||||
- [ ] 용어 검색 → 키워드로 조직/회의 용어 찾기 확인
|
||||
- [ ] 녹음 일시정지 → 타이머 정지 확인
|
||||
- [ ] 녹음 재개 → 타이머 재개 확인
|
||||
|
||||
---
|
||||
|
||||
**문서 종료**
|
||||
@ -1,832 +0,0 @@
|
||||
# API 리뷰 분석 - 프로토타입 vs 구현
|
||||
|
||||
**작성일**: 2025-10-28
|
||||
**검토자**: Architect, Backend Developer, Frontend Developer
|
||||
**분석 방법**: 프로토타입 HTML 파일과 실제 구현된 Controller 소스코드 비교 분석
|
||||
|
||||
---
|
||||
|
||||
## 📋 요약
|
||||
|
||||
### 전체 현황 (v2.0 - 회의 진행 실시간 기능 추가 분석)
|
||||
- **분석된 화면**: 9개 프로토타입 화면
|
||||
- **프로토타입 요구 API**: **34개** (v1: 27개 → v2: +7개)
|
||||
- **구현된 API**: 27개 엔드포인트
|
||||
- **완전 누락 API**: **11개** (v1: 4개 → v2: +7개)
|
||||
- **개선 필요 API**: 2개
|
||||
- **불필요한 API**: 0개
|
||||
|
||||
### 주요 발견사항 (v2.0 업데이트)
|
||||
1. ✅ **강점**: 핵심 비즈니스 로직 API는 모두 구현됨
|
||||
2. 🔴 **치명적 누락 (기존)**:
|
||||
- `GET /api/meetings` (목록 조회) - 대시보드 "최근 회의" 표시 불가
|
||||
- `PUT/PATCH /api/meetings/{meetingId}` (회의 수정) - 예정된 회의 수정 불가
|
||||
- `GET /api/dashboard/statistics` - 대시보드 통계 카드 표시 불가
|
||||
3. 🔴 **치명적 누락 (신규 발견)**: **회의 진행 중 실시간 기능 API 7개 누락**
|
||||
- **탭2: AI 메모 (3개 누락)**
|
||||
- `PUT /api/meetings/{meetingId}/memo` - 회의 중 메모 저장
|
||||
- `GET /api/ai/suggestions/realtime/{meetingId}` - AI 실시간 추천 메모
|
||||
- `POST /api/ai/suggestions/{suggestionId}/adopt` - AI 추천 채택
|
||||
- **탭3: 용어사전 (2개 누락)**
|
||||
- `GET /api/ai/terms/search` - 용어 검색
|
||||
- `GET /api/ai/terms/{termName}/detail` - 용어 상세 조회
|
||||
- **녹음 제어 (2개 누락)**
|
||||
- `POST /api/stt/recordings/{recordingId}/pause` - 녹음 일시정지
|
||||
- `POST /api/stt/recordings/{recordingId}/resume` - 녹음 재개
|
||||
4. 🟡 **기능 누락**: AI 요약 재생성 API 미구현
|
||||
5. 🟡 **개선 필요**: 회의록 검색/필터링 파라미터 추가 필요
|
||||
|
||||
### 비즈니스 영향도
|
||||
- **사용자 경험**: 회의 진행 중 핵심 편의 기능 미동작으로 인한 UX 저하
|
||||
- **AI 활용도**: 실시간 AI 추천 기능이 동작하지 않아 서비스 차별화 가치 감소
|
||||
- **메모 손실 위험**: 회의 중 작성한 메모가 저장되지 않아 데이터 손실 가능성
|
||||
- **유저스토리 영향**: US-06, US-07, US-08, US-09, US-10의 핵심/보조 기능 미동작
|
||||
|
||||
---
|
||||
|
||||
## 🔍 화면별 상세 분석
|
||||
|
||||
### 1. 로그인 화면 (01-로그인.html)
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| 로그인 | `POST /api/auth/login` | ✅ 구현됨 | [UserController.java:37-50](user/src/main/java/com/unicorn/hgzero/user/controller/UserController.java#L37-L50) | LDAP 인증, JWT 토큰 발급 |
|
||||
| 토큰 갱신 | `POST /api/auth/refresh` | ✅ 구현됨 | [UserController.java:59-72](user/src/main/java/com/unicorn/hgzero/user/controller/UserController.java#L59-L72) | Refresh Token 사용 |
|
||||
| 로그아웃 | `POST /api/auth/logout` | ✅ 구현됨 | [UserController.java:82-96](user/src/main/java/com/unicorn/hgzero/user/controller/UserController.java#L82-L96) | Refresh Token 삭제 |
|
||||
| 토큰 검증 | `GET /api/auth/validate` | ✅ 구현됨 | [UserController.java:105-126](user/src/main/java/com/unicorn/hgzero/user/controller/UserController.java#L105-L126) | JWT 토큰 유효성 검증 |
|
||||
|
||||
**분석 결과**: ✅ **완벽 구현** - 모든 인증 관련 API가 구현되어 있으며, 보안 모범 사례를 따르고 있음
|
||||
|
||||
---
|
||||
|
||||
### 2. 대시보드 화면 (02-대시보드.html)
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| 최근 회의 목록 (3개) | `GET /api/meetings` | ❌ **누락** | - | **전체 회의 목록 조회 후 프론트에서 정렬** |
|
||||
| 최근 회의록 목록 (4개) | `GET /api/meetings/minutes` | ✅ 구현됨 | [MinutesController.java:210-268](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L210-L268) | 페이징, 필터링 지원 |
|
||||
| 통계 정보 (2개 카드) | `GET /api/dashboard/statistics` | ❌ **누락** | - | 예정된 회의, 작성중 회의록 수 |
|
||||
|
||||
**상세 분석**:
|
||||
|
||||
#### "최근 회의" 섹션 요구사항 ([02-대시보드.html:665-681](design/uiux/prototype/02-대시보드.html#L665-L681))
|
||||
프로토타입 JavaScript 로직:
|
||||
```javascript
|
||||
// 회의록 미생성(scheduled, ongoing) 먼저, 빠른 일시 순 정렬
|
||||
const meetings = [...SAMPLE_MEETINGS]
|
||||
.sort((a, b) => {
|
||||
// 회의록 미생성 회의 우선
|
||||
const aNoMinutes = a.status === 'scheduled' || a.status === 'ongoing';
|
||||
const bNoMinutes = b.status === 'scheduled' || b.status === 'ongoing';
|
||||
|
||||
if (aNoMinutes && !bNoMinutes) return -1;
|
||||
if (!aNoMinutes && bNoMinutes) return 1;
|
||||
|
||||
// 동일 그룹 내에서는 빠른 일시 순 (오름차순)
|
||||
return new Date(a.date + ' ' + a.time) - new Date(b.date + ' ' + b.time);
|
||||
})
|
||||
.slice(0, 3); // 상위 3개만 표시
|
||||
```
|
||||
|
||||
**요구사항 해석**:
|
||||
1. **전체 회의 목록**을 가져와야 함 (상태 무관)
|
||||
2. **회의록 생성 여부 정보** 포함 필요 (scheduled, ongoing, draft, complete)
|
||||
3. 프론트엔드에서 다음 우선순위로 정렬:
|
||||
- 1순위: 회의록 미생성 회의 (`scheduled`, `ongoing`)
|
||||
- 2순위: 빠른 일시 순
|
||||
4. 상위 3개만 표시
|
||||
|
||||
**현재 구현 상태**:
|
||||
- ❌ `GET /api/meetings` (목록 조회) - **완전 누락**
|
||||
- ✅ `GET /api/meetings/{meetingId}` (단건 조회) - 구현됨
|
||||
|
||||
**분석 결과**: ⚠️ **부분 구현** (33%)
|
||||
- **누락 API (2개)**:
|
||||
1. `GET /api/meetings` - 회의 목록 조회 (전체 상태, 날짜/시간 정렬 필요)
|
||||
2. `GET /api/dashboard/statistics` - 통계 정보 (예정된 회의, 작성중 회의록)
|
||||
|
||||
**권장사항**:
|
||||
```java
|
||||
// MeetingController에 추가 필요
|
||||
@GetMapping
|
||||
@Operation(summary = "회의 목록 조회", description = "사용자의 회의 목록을 조회합니다")
|
||||
public ResponseEntity<ApiResponse<MeetingListResponse>> getMeetingList(
|
||||
@RequestHeader("X-User-Id") String userId,
|
||||
@RequestParam(required = false) String status, // all(기본값), scheduled, ongoing, draft, complete
|
||||
@RequestParam(required = false) LocalDateTime startDate,
|
||||
@RequestParam(required = false) LocalDateTime endDate,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(defaultValue = "startTime") String sortBy,
|
||||
@RequestParam(defaultValue = "asc") String sortDir
|
||||
) {
|
||||
// 사용자가 참여한 모든 회의 조회
|
||||
// 응답에 회의록 생성 여부(hasMinutes), 회의록 상태(minutesStatus) 포함 필수
|
||||
}
|
||||
|
||||
// 새로운 DashboardController 생성 권장
|
||||
@GetMapping("/api/dashboard/statistics")
|
||||
public ResponseEntity<ApiResponse<DashboardStatistics>> getStatistics(
|
||||
@RequestHeader("X-User-Id") String userId
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 회의 예약/수정 화면 (03-회의예약.html)
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| 회의 생성 | `POST /api/meetings` | ✅ 구현됨 | [MeetingController.java:60-93](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L60-L93) | 참석자 초대 포함 |
|
||||
| 회의 정보 수정 | `PUT /api/meetings/{meetingId}` 또는 `PATCH /api/meetings/{meetingId}` | ❌ **누락** | - | **예정된 회의 수정 불가** |
|
||||
|
||||
**상세 분석**:
|
||||
|
||||
#### 회의 수정 요구사항 ([02-대시보드.html:724](design/uiux/prototype/02-대시보드.html#L724))
|
||||
프로토타입 JavaScript 로직:
|
||||
```javascript
|
||||
// 상태에 따른 이동 처리
|
||||
if (meetingStatus === 'ongoing') {
|
||||
navigateTo('05-회의진행.html');
|
||||
} else if (meetingStatus === 'draft' || meetingStatus === 'complete' || meetingStatus === 'completed') {
|
||||
navigateTo('10-회의록상세조회.html');
|
||||
} else if (meetingStatus === 'scheduled') {
|
||||
navigateTo('03-회의예약.html'); // 예정된 회의 → 회의예약 화면 (수정 모드)
|
||||
}
|
||||
```
|
||||
|
||||
**요구사항 해석**:
|
||||
1. 대시보드에서 **예정된 회의(scheduled) 카드 클릭**
|
||||
2. 회의예약 화면(03-회의예약.html)으로 이동
|
||||
3. 기존 회의 정보를 **로드하여 수정 가능**해야 함
|
||||
4. 수정 완료 시 `PUT` 또는 `PATCH` 요청 필요
|
||||
|
||||
**현재 상태**:
|
||||
- ❌ API 설계서([meeting-service-api.yaml](design/backend/api/meeting-service-api.yaml)) - **회의 수정 API 명세 없음**
|
||||
- ❌ MeetingController - **회의 수정 API 구현 없음**
|
||||
- ✅ `POST /api/meetings` (생성) - 구현됨
|
||||
- ✅ `GET /api/meetings/{meetingId}` (단건 조회) - 구현됨
|
||||
- ❌ `PUT/PATCH /api/meetings/{meetingId}` (수정) - **완전 누락**
|
||||
|
||||
**분석 결과**: ⚠️ **치명적 누락** (50%)
|
||||
- 회의 생성은 가능하지만, **예약된 회의 수정 불가**
|
||||
- 사용자가 대시보드에서 예정된 회의를 클릭해도 수정할 수 없음
|
||||
|
||||
**권장사항**:
|
||||
```java
|
||||
// MeetingController에 추가 필요
|
||||
@PutMapping("/{meetingId}")
|
||||
@Operation(summary = "회의 정보 수정", description = "예정된 회의의 정보를 수정합니다")
|
||||
public ResponseEntity<ApiResponse<MeetingResponse>> updateMeeting(
|
||||
@PathVariable String meetingId,
|
||||
@RequestHeader("X-User-Id") String userId,
|
||||
@RequestHeader("X-User-Name") String userName,
|
||||
@RequestHeader("X-User-Email") String userEmail,
|
||||
@Valid @RequestBody UpdateMeetingRequest request
|
||||
) {
|
||||
// 회의 정보 수정 로직
|
||||
// - 제목, 날짜, 시간, 장소, 안건 수정 가능
|
||||
// - 참석자 추가/제거 가능
|
||||
// - 회의 상태가 'scheduled'일 때만 수정 가능
|
||||
// - 변경 사항 참석자에게 알림
|
||||
}
|
||||
```
|
||||
|
||||
**UpdateMeetingRequest DTO**:
|
||||
```java
|
||||
public class UpdateMeetingRequest {
|
||||
private String title; // 회의 제목
|
||||
private LocalDateTime startTime; // 시작 시간
|
||||
private LocalDateTime endTime; // 종료 시간
|
||||
private String location; // 장소
|
||||
private String agenda; // 안건
|
||||
private List<String> participants; // 참석자 이메일 목록
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 템플릿 선택 화면 (04-템플릿선택.html)
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| 템플릿 목록 조회 | `GET /api/meetings/templates` | ✅ 구현됨 | [TemplateController.java:33-63](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/TemplateController.java#L33-L63) | 4가지 고정 템플릿 제공 |
|
||||
| 템플릿 적용 | `PUT /api/meetings/{meetingId}/template` | ✅ 구현됨 | [MeetingController.java:108-135](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L108-L135) | 템플릿 ID로 적용 |
|
||||
|
||||
**분석 결과**: ✅ **완벽 구현** - 템플릿 관리 기능 완전 구현
|
||||
|
||||
---
|
||||
|
||||
### 5. 회의 진행 화면 (05-회의진행.html) ⚠️ **중요 업데이트**
|
||||
|
||||
#### 화면 구조 분석
|
||||
프로토타입은 4개 탭으로 구성되어 있으며, 각 탭마다 실시간 기능이 요구됩니다:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 📍 헤더: 회의 제목 + 녹음 상태 │
|
||||
├─────────────────────────────────────────┤
|
||||
│ 📋 회의 기본정보 (카드) │
|
||||
├─────────────────────────────────────────┤
|
||||
│ 📑 4개 탭 컨테이너 │
|
||||
│ ┌──────────────────────────────────┐ │
|
||||
│ │ 🧑🤝🧑 참석자 | 📝 AI메모 | 📚 용어 │ │
|
||||
│ │ 사전 | 📂 관련회의록 │ │
|
||||
│ └──────────────────────────────────┘ │
|
||||
├─────────────────────────────────────────┤
|
||||
│ ⏸️ 일시정지 | 🔴 회의 종료 버튼 │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 탭1: 참석자 (Lines 697-747)
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| 회의 시작 | `POST /api/meetings/{meetingId}/start` | ✅ 구현됨 | [MeetingController.java:149-170](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L149-L170) | WebSocket 세션 생성 |
|
||||
| WebSocket 연결 | `ws://localhost:8080/ws/collaboration` | ✅ 구현됨 | [MeetingController.java:165](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L165) | 실시간 협업 지원 |
|
||||
| 참석자 초대 | `POST /api/meetings/{meetingId}/invite` | ✅ 구현됨 | [MeetingController.java:289-321](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L289-L321) | 이메일 발송 포함 |
|
||||
| 참석자 목록 표시 | `GET /api/meetings/{meetingId}` | ✅ 구현됨 | [MeetingController.java:228-244](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L228-L244) | 회의 정보에 참석자 포함 |
|
||||
|
||||
#### 탭2: AI 메모 (Lines 750-807) 🔴 **치명적 누락**
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 프로토타입 근거 | 비고 |
|
||||
|---------|---------|----------|---------------|------|
|
||||
| **메모 입력 및 저장** | `PUT /api/meetings/{meetingId}/memo` | ❌ **누락** | [Line 1119-1143](design/uiux/prototype/05-회의진행.html#L1119-L1143) | 개인별 메모 저장 |
|
||||
| **AI 추천 실시간 조회** | `GET /api/ai/suggestions/realtime/{meetingId}` | ❌ **누락** | [Line 767-806](design/uiux/prototype/05-회의진행.html#L767-L806) | 실시간 폴링 필요 |
|
||||
| **AI 추천 채택** | `POST /api/ai/suggestions/{suggestionId}/adopt` | ❌ **누락** | [Line 1070-1097](design/uiux/prototype/05-회의진행.html#L1070-L1097) | 시간 포함 저장 |
|
||||
|
||||
**프로토타입 JavaScript 분석** (saveMemo 함수):
|
||||
```javascript
|
||||
function saveMemo() {
|
||||
const memo = memoTextarea.value.trim();
|
||||
// 실제 구현시에는 서버로 전송
|
||||
// fetch('/api/meetings/memo', {
|
||||
// method: 'PUT',
|
||||
// body: JSON.stringify({ memo: memo }),
|
||||
// headers: { 'Content-Type': 'application/json' }
|
||||
// });
|
||||
}
|
||||
```
|
||||
|
||||
**영향도**:
|
||||
- 🔴 사용자가 작성한 메모가 저장되지 않음 (데이터 손실 위험)
|
||||
- 🔴 AI 실시간 추천 기능 완전 미동작
|
||||
- 🔴 **유저스토리 US-07, US-08**의 핵심 기능
|
||||
|
||||
#### 탭3: 용어사전 (Lines 810-967) 🟡 **부분 구현**
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| **AI 전문용어 실시간 감지** | `POST /api/ai/terms/detect` | ✅ 구현됨 | [TermController.java:35-79](ai/src/main/java/com/unicorn/hgzero/ai/infra/controller/TermController.java#L35-L79) | 회의 중 용어 자동 감지 |
|
||||
| **용어 검색** | `GET /api/ai/terms/search` | ❌ **누락** | [Line 1145-1182](design/uiux/prototype/05-회의진행.html#L1145-L1182) | 키워드 검색 |
|
||||
| **용어 상세 조회** | `GET /api/ai/terms/{termName}/detail` | ❌ **누락** | [Line 1305-1308](design/uiux/prototype/05-회의진행.html#L1305-L1308) | 모달 표시 |
|
||||
|
||||
#### 탭4: 관련회의록 (Lines 970-1010) ✅ **완벽 구현**
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| **AI 유사 회의록 찾기** | `GET /api/ai/transcripts/{meetingId}/related` | ✅ 구현됨 | [RelationController.java:31-62](ai/src/main/java/com/unicorn/hgzero/ai/infra/controller/RelationController.java#L31-L62) | 벡터 유사도 검색 |
|
||||
| 관련 회의록 열기 | `GET /api/meetings/minutes/{minutesId}` | ✅ 구현됨 | 기존 API 재사용 | 새 탭 열기 |
|
||||
|
||||
#### 녹음 제어 (Bottom Bar) 🔴 **치명적 누락**
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 프로토타입 근거 | 비고 |
|
||||
|---------|---------|----------|---------------|------|
|
||||
| **녹음 일시정지** | `POST /api/stt/recordings/{recordingId}/pause` | ❌ **누락** | [Line 1212-1243](design/uiux/prototype/05-회의진행.html#L1212-L1243) | 타이머 정지 |
|
||||
| **녹음 재개** | `POST /api/stt/recordings/{recordingId}/resume` | ❌ **누락** | [Line 1212-1243](design/uiux/prototype/05-회의진행.html#L1212-L1243) | 타이머 재개 |
|
||||
| 녹음 시작 | `POST /api/stt/recordings/{recordingId}/start` | ✅ 구현됨 | [RecordingController.java:83-94](stt/src/main/java/com/unicorn/hgzero/stt/controller/RecordingController.java#L83-L94) | - |
|
||||
| 녹음 중지 | `POST /api/stt/recordings/{recordingId}/stop` | ✅ 구현됨 | [RecordingController.java:115-126](stt/src/main/java/com/unicorn/hgzero/stt/controller/RecordingController.java#L115-L126) | - |
|
||||
| 회의 종료 | `POST /api/meetings/{meetingId}/end` | ✅ 구현됨 | [MeetingController.java:184-214](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L184-L214) | AI 분석 포함 |
|
||||
|
||||
**현재 상태**: RecordingController에는 `start`와 `stop`만 있고 `pause/resume` 없음
|
||||
|
||||
**영향도**:
|
||||
- 🔴 회의 중 잠깐 중단 후 재개 시나리오 불가
|
||||
- 🔴 **유저스토리 US-06**의 핵심 기능
|
||||
|
||||
---
|
||||
|
||||
**분석 결과**: ⚠️ **부분 구현 (50%)** - **실시간 기능 7개 API 누락**
|
||||
|
||||
### 누락 API 상세 명세
|
||||
|
||||
#### 1. 메모 저장 API 🔴 P0
|
||||
```java
|
||||
PUT /api/meetings/{meetingId}/memo
|
||||
Request Body: {
|
||||
"memo": "string",
|
||||
"userId": "string",
|
||||
"timestamp": "datetime"
|
||||
}
|
||||
Response: { "memoId": "string", "savedAt": "datetime" }
|
||||
```
|
||||
|
||||
#### 2. AI 실시간 추천 조회 API 🔴 P0
|
||||
```java
|
||||
GET /api/ai/suggestions/realtime/{meetingId}?since={timestamp}&limit=10
|
||||
Response: {
|
||||
"suggestions": [
|
||||
{
|
||||
"suggestionId": "string",
|
||||
"timestamp": "00:05:23",
|
||||
"content": "string",
|
||||
"confidence": 0.95,
|
||||
"category": "DISCUSSION|DECISION"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. AI 추천 채택 API 🟡 P1
|
||||
```java
|
||||
POST /api/ai/suggestions/{suggestionId}/adopt
|
||||
Request Body: {
|
||||
"meetingId": "string",
|
||||
"timestamp": "00:05:23",
|
||||
"userId": "string"
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 용어 검색 API 🟡 P1
|
||||
```java
|
||||
GET /api/ai/terms/search?query={keyword}&meetingId={id}
|
||||
Response: {
|
||||
"terms": [...],
|
||||
"totalCount": 5
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. 용어 상세 조회 API 🟢 P2
|
||||
```java
|
||||
GET /api/ai/terms/{termName}/detail?meetingId={id}
|
||||
Response: {
|
||||
"term": "string",
|
||||
"definition": "string",
|
||||
"usageInMeeting": [...],
|
||||
"externalLinks": [...]
|
||||
}
|
||||
```
|
||||
|
||||
#### 6-7. 녹음 일시정지/재개 API 🔴 P0
|
||||
```java
|
||||
POST /api/stt/recordings/{recordingId}/pause
|
||||
POST /api/stt/recordings/{recordingId}/resume
|
||||
Response: { "status": "PAUSED|RECORDING", "timestamp": "datetime" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 회의 종료 화면 (07-회의종료.html)
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| 회의 종료 | `POST /api/meetings/{meetingId}/end` | ✅ 구현됨 | [MeetingController.java:184-214](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L184-L214) | AI 분석, 회의록 생성 |
|
||||
|
||||
**분석 결과**: ✅ **완벽 구현** - AI 기반 회의록 자동 생성 포함
|
||||
|
||||
---
|
||||
|
||||
### 7. 회의록 상세 조회 화면 (10-회의록상세조회.html)
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| 회의록 상세 조회 | `GET /api/meetings/minutes/{minutesId}` | ✅ 구현됨 | [MinutesController.java:271-297](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L271-L297) | 대시보드 탭, 회의록 탭 데이터 포함 |
|
||||
|
||||
**분석 결과**: ✅ **완벽 구현** - 상세 조회 완전 구현 (Mock 데이터 포함)
|
||||
|
||||
---
|
||||
|
||||
### 8. 회의록 수정 화면 (11-회의록수정.html)
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| 회의록 수정 | `PATCH /api/meetings/minutes/{minutesId}` | ✅ 구현됨 | [MinutesController.java:300-343](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L300-L343) | 제목, 섹션 내용 수정 |
|
||||
| 회의록 확정 | `POST /api/meetings/minutes/{minutesId}/finalize` | ✅ 구현됨 | [MinutesController.java:346-374](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L346-L374) | 버전 관리 포함 |
|
||||
| 섹션 검증 | `POST /api/meetings/minutes/{minutesId}/sections/{sectionId}/verify` | ✅ 구현됨 | [MinutesController.java:377-410](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L377-L410) | 섹션별 완료 검증 |
|
||||
| 섹션 잠금 | `POST /api/meetings/minutes/{minutesId}/sections/{sectionId}/lock` | ✅ 구현됨 | [MinutesController.java:413-446](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L413-L446) | 동시 편집 방지 |
|
||||
| 섹션 잠금 해제 | `DELETE /api/meetings/minutes/{minutesId}/sections/{sectionId}/lock` | ✅ 구현됨 | [MinutesController.java:449-480](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L449-L480) | 잠금 해제 |
|
||||
| AI 요약 재생성 | `POST /api/meetings/minutes/{minutesId}/sections/{sectionId}/regenerate-summary` | ❌ **누락** | - | AI 요약 재생성 기능 필요 |
|
||||
|
||||
**분석 결과**: ⚠️ **부분 구현** (83%)
|
||||
- **누락 API (1개)**: AI 요약 재생성 기능
|
||||
|
||||
**권장사항**:
|
||||
```java
|
||||
// MinutesController에 추가 필요
|
||||
@PostMapping("/{minutesId}/sections/{sectionId}/regenerate-summary")
|
||||
@Operation(summary = "AI 요약 재생성", description = "섹션의 AI 요약을 재생성합니다")
|
||||
public ResponseEntity<ApiResponse<SectionSummary>> regenerateSummary(
|
||||
@PathVariable String minutesId,
|
||||
@PathVariable String sectionId,
|
||||
@RequestHeader("X-User-Id") String userId
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. 회의록 목록 조회 화면 (12-회의록목록조회.html)
|
||||
|
||||
| 화면 기능 | 요구 API | 구현 상태 | 구현 위치 | 비고 |
|
||||
|---------|---------|----------|----------|------|
|
||||
| 회의록 목록 조회 | `GET /api/meetings/minutes` | ✅ 구현됨 | [MinutesController.java:210-268](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L210-L268) | 상태 필터, 페이징 지원 |
|
||||
| 검색 기능 | `GET /api/meetings/minutes?keyword={keyword}` | ⚠️ **개선 필요** | [MinutesController.java:210](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L210) | 키워드 검색 파라미터 추가 필요 |
|
||||
| 참여 유형 필터 | `GET /api/meetings/minutes?participationType={type}` | ⚠️ **개선 필요** | [MinutesController.java:210](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L210) | 참여 유형 필터 추가 필요 |
|
||||
|
||||
**분석 결과**: ⚠️ **개선 필요**
|
||||
- 기본 목록 조회는 구현되어 있으나, 프로토타입에서 요구하는 세부 필터링 기능 미구현
|
||||
|
||||
**권장사항**:
|
||||
```java
|
||||
// MinutesController 개선 필요
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<MinutesListResponse>> getMinutesList(
|
||||
@RequestHeader("X-User-Id") String userId,
|
||||
@RequestParam(required = false) String status, // 기존 기능
|
||||
@RequestParam(required = false) String keyword, // 추가 필요 - 제목/내용 검색
|
||||
@RequestParam(required = false) String participationType, // 추가 필요 - host, participant, all
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "20") int size,
|
||||
@RequestParam(defaultValue = "createdAt") String sortBy,
|
||||
@RequestParam(defaultValue = "desc") String sortDir
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 통합 분석
|
||||
|
||||
### 구현된 API 목록 (24개)
|
||||
|
||||
#### 인증 서비스 (4개)
|
||||
| API | 메서드 | 엔드포인트 | 구현 위치 |
|
||||
|-----|--------|-----------|----------|
|
||||
| 로그인 | POST | `/api/auth/login` | [UserController.java:37](user/src/main/java/com/unicorn/hgzero/user/controller/UserController.java#L37) |
|
||||
| 토큰 갱신 | POST | `/api/auth/refresh` | [UserController.java:59](user/src/main/java/com/unicorn/hgzero/user/controller/UserController.java#L59) |
|
||||
| 로그아웃 | POST | `/api/auth/logout` | [UserController.java:82](user/src/main/java/com/unicorn/hgzero/user/controller/UserController.java#L82) |
|
||||
| 토큰 검증 | GET | `/api/auth/validate` | [UserController.java:105](user/src/main/java/com/unicorn/hgzero/user/controller/UserController.java#L105) |
|
||||
|
||||
#### 회의 관리 서비스 (6개 - 목록 조회, 회의 수정 누락)
|
||||
| API | 메서드 | 엔드포인트 | 구현 위치 |
|
||||
|-----|--------|-----------|----------|
|
||||
| 회의 생성 | POST | `/api/meetings` | [MeetingController.java:60](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L60) |
|
||||
| 템플릿 적용 | PUT | `/api/meetings/{meetingId}/template` | [MeetingController.java:108](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L108) |
|
||||
| 회의 시작 | POST | `/api/meetings/{meetingId}/start` | [MeetingController.java:149](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L149) |
|
||||
| 회의 종료 | POST | `/api/meetings/{meetingId}/end` | [MeetingController.java:184](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L184) |
|
||||
| 회의 단건 조회 | GET | `/api/meetings/{meetingId}` | [MeetingController.java:228](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L228) |
|
||||
| 회의 취소 | DELETE | `/api/meetings/{meetingId}` | [MeetingController.java:258](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L258) |
|
||||
| 참석자 초대 | POST | `/api/meetings/{meetingId}/invite` | [MeetingController.java:289](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L289) |
|
||||
|
||||
**⚠️ 누락 API (2개)**:
|
||||
1. `GET /api/meetings` (회의 목록 조회) - 대시보드 "최근 회의" 섹션에 필수
|
||||
2. `PUT /api/meetings/{meetingId}` (회의 정보 수정) - **예정된 회의 수정 불가**
|
||||
|
||||
#### 회의록 관리 서비스 (7개)
|
||||
| API | 메서드 | 엔드포인트 | 구현 위치 |
|
||||
|-----|--------|-----------|----------|
|
||||
| 회의록 목록 조회 | GET | `/api/meetings/minutes` | [MinutesController.java:210](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L210) |
|
||||
| 회의록 상세 조회 | GET | `/api/meetings/minutes/{minutesId}` | [MinutesController.java:271](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L271) |
|
||||
| 회의록 수정 | PATCH | `/api/meetings/minutes/{minutesId}` | [MinutesController.java:300](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L300) |
|
||||
| 회의록 확정 | POST | `/api/meetings/minutes/{minutesId}/finalize` | [MinutesController.java:346](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L346) |
|
||||
| 섹션 검증 | POST | `/api/meetings/minutes/{minutesId}/sections/{sectionId}/verify` | [MinutesController.java:377](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L377) |
|
||||
| 섹션 잠금 | POST | `/api/meetings/minutes/{minutesId}/sections/{sectionId}/lock` | [MinutesController.java:413](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L413) |
|
||||
| 섹션 잠금 해제 | DELETE | `/api/meetings/minutes/{minutesId}/sections/{sectionId}/lock` | [MinutesController.java:449](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MinutesController.java#L449) |
|
||||
|
||||
#### Todo 관리 서비스 (4개)
|
||||
| API | 메서드 | 엔드포인트 | 구현 위치 |
|
||||
|-----|--------|-----------|----------|
|
||||
| Todo 생성 | POST | `/api/meetings/todos` | [TodoController.java:50](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/TodoController.java#L50) |
|
||||
| Todo 수정 | PATCH | `/api/meetings/todos/{todoId}` | [TodoController.java:114](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/TodoController.java#L114) |
|
||||
| Todo 완료 | PATCH | `/api/meetings/todos/{todoId}/complete` | [TodoController.java:163](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/TodoController.java#L163) |
|
||||
| Todo 목록 조회 | GET | `/api/meetings/todos` | [TodoController.java:210](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/TodoController.java#L210) |
|
||||
|
||||
#### 템플릿 관리 서비스 (1개)
|
||||
| API | 메서드 | 엔드포인트 | 구현 위치 |
|
||||
|-----|--------|-----------|----------|
|
||||
| 템플릿 목록 조회 | GET | `/api/meetings/templates` | [TemplateController.java:33](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/TemplateController.java#L33) |
|
||||
|
||||
#### WebSocket (1개)
|
||||
| API | 프로토콜 | 엔드포인트 | 구현 위치 |
|
||||
|-----|---------|-----------|----------|
|
||||
| 실시간 협업 | WebSocket | `ws://localhost:8080/ws/collaboration` | [MeetingController.java:165](meeting/src/main/java/com/unicorn/hgzero/meeting/infra/controller/MeetingController.java#L165) |
|
||||
|
||||
---
|
||||
|
||||
### ❌ 누락된 API 목록 (4개 완전 누락 + 2개 개선 필요)
|
||||
|
||||
#### 완전 누락 (4개)
|
||||
| 우선순위 | API | 메서드 | 엔드포인트 | 필요한 이유 | 권장 구현 위치 |
|
||||
|---------|-----|--------|-----------|-----------|--------------|
|
||||
| 🔴 **긴급** | 회의 목록 조회 | GET | `/api/meetings` | 대시보드 "최근 회의" 섹션 표시 **불가** | MeetingController |
|
||||
| 🔴 **긴급** | 회의 정보 수정 | PUT/PATCH | `/api/meetings/{meetingId}` | 대시보드에서 예정된 회의 클릭 시 수정 **불가** | MeetingController |
|
||||
| 🔴 **긴급** | 대시보드 통계 | GET | `/api/dashboard/statistics` | 대시보드 통계 카드 (예정된 회의, 작성중 회의록) 표시 **불가** | 신규 DashboardController |
|
||||
| 🟡 중간 | AI 요약 재생성 | POST | `/api/meetings/minutes/{minutesId}/sections/{sectionId}/regenerate-summary` | 회의록 수정 화면 AI 요약 재생성 버튼 **동작 불가** | MinutesController |
|
||||
|
||||
#### 기능 개선 필요 (2개)
|
||||
| 우선순위 | API | 현재 상태 | 개선 내용 | 필요한 이유 |
|
||||
|---------|-----|----------|-----------|-----------|
|
||||
| 🟡 중간 | 회의록 목록 조회 | `GET /api/meetings/minutes` 구현됨 | `keyword` 파라미터 추가 | 회의록 목록 화면 검색 기능 동작 안 함 |
|
||||
| 🟡 중간 | 회의록 목록 조회 | `GET /api/meetings/minutes` 구현됨 | `participationType` 파라미터 추가 | 회의록 목록 화면 참여 유형 필터 동작 안 함 |
|
||||
|
||||
---
|
||||
|
||||
### ✅ 불필요한 API (0개)
|
||||
|
||||
**분석 결과**: 구현된 모든 API가 프로토타입에서 요구하는 기능과 매칭되며, 불필요한 API는 없음.
|
||||
|
||||
---
|
||||
|
||||
## 💡 권장 개선사항
|
||||
|
||||
### 1. 높은 우선순위 (🔴 필수)
|
||||
|
||||
#### 1.1 회의 목록 조회 API 추가
|
||||
```java
|
||||
// MeetingController.java에 추가
|
||||
@GetMapping
|
||||
@Operation(summary = "회의 목록 조회", description = "사용자의 회의 목록을 조회합니다")
|
||||
public ResponseEntity<ApiResponse<MeetingListResponse>> getMeetingList(
|
||||
@RequestHeader("X-User-Id") String userId,
|
||||
@RequestParam(required = false) String status, // upcoming, ongoing, completed
|
||||
@RequestParam(required = false) LocalDateTime startDate,
|
||||
@RequestParam(required = false) LocalDateTime endDate,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(defaultValue = "startTime") String sortBy,
|
||||
@RequestParam(defaultValue = "asc") String sortDir
|
||||
) {
|
||||
// 구현 로직
|
||||
}
|
||||
```
|
||||
|
||||
**요구사항**:
|
||||
- 상태별 필터링: `upcoming` (예정), `ongoing` (진행 중), `completed` (완료)
|
||||
- 날짜 범위 필터링
|
||||
- 페이징 지원
|
||||
- 정렬 기능 (시작 시간, 생성 시간 등)
|
||||
|
||||
---
|
||||
|
||||
#### 1.2 대시보드 통계 API 추가
|
||||
```java
|
||||
// 신규 DashboardController.java 생성
|
||||
@RestController
|
||||
@RequestMapping("/api/dashboard")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "Dashboard", description = "대시보드 API")
|
||||
public class DashboardController {
|
||||
|
||||
@GetMapping("/statistics")
|
||||
@Operation(summary = "대시보드 통계 조회", description = "사용자의 통계 정보를 조회합니다")
|
||||
public ResponseEntity<ApiResponse<DashboardStatistics>> getStatistics(
|
||||
@RequestHeader("X-User-Id") String userId
|
||||
) {
|
||||
// 구현 로직
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**응답 데이터 구조**:
|
||||
```json
|
||||
{
|
||||
"totalMeetings": 24,
|
||||
"upcomingMeetings": 3,
|
||||
"completedMinutes": 18,
|
||||
"pendingTodos": 7,
|
||||
"completedTodos": 15,
|
||||
"thisWeekMeetings": 5,
|
||||
"thisMonthMeetings": 12
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 중간 우선순위 (🟡 권장)
|
||||
|
||||
#### 2.1 AI 요약 재생성 API 추가
|
||||
```java
|
||||
// MinutesController.java에 추가
|
||||
@PostMapping("/{minutesId}/sections/{sectionId}/regenerate-summary")
|
||||
@Operation(summary = "AI 요약 재생성", description = "섹션의 AI 요약을 재생성합니다")
|
||||
public ResponseEntity<ApiResponse<SectionSummary>> regenerateSummary(
|
||||
@PathVariable String minutesId,
|
||||
@PathVariable String sectionId,
|
||||
@RequestHeader("X-User-Id") String userId,
|
||||
@RequestHeader("X-User-Name") String userName
|
||||
) {
|
||||
// AI 서비스 호출하여 요약 재생성
|
||||
// 버전 관리 (이전 요약 보존)
|
||||
// 이벤트 발행 (요약 재생성 알림)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2.2 회의록 목록 API 개선
|
||||
```java
|
||||
// MinutesController.java 수정
|
||||
@GetMapping
|
||||
public ResponseEntity<ApiResponse<MinutesListResponse>> getMinutesList(
|
||||
@RequestHeader("X-User-Id") String userId,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String keyword, // 추가 - 제목/내용 검색
|
||||
@RequestParam(required = false) String participationType, // 추가 - host, participant, all
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "20") int size,
|
||||
@RequestParam(defaultValue = "createdAt") String sortBy,
|
||||
@RequestParam(defaultValue = "desc") String sortDir
|
||||
) {
|
||||
// 구현 로직
|
||||
}
|
||||
```
|
||||
|
||||
**추가 필터 설명**:
|
||||
- `keyword`: 회의록 제목 또는 내용에서 검색 (LIKE 검색)
|
||||
- `participationType`:
|
||||
- `host`: 주최한 회의록만
|
||||
- `participant`: 참여한 회의록만
|
||||
- `all`: 모든 회의록 (기본값)
|
||||
|
||||
---
|
||||
|
||||
### 3. 낮은 우선순위 (🟢 선택)
|
||||
|
||||
#### 3.1 회의록 버전 관리 API
|
||||
```java
|
||||
// MinutesController.java에 추가 고려
|
||||
@GetMapping("/{minutesId}/versions")
|
||||
@Operation(summary = "회의록 버전 목록 조회")
|
||||
public ResponseEntity<ApiResponse<List<MinutesVersion>>> getMinutesVersions(
|
||||
@PathVariable String minutesId
|
||||
)
|
||||
|
||||
@GetMapping("/{minutesId}/versions/{version}")
|
||||
@Operation(summary = "특정 버전 회의록 조회")
|
||||
public ResponseEntity<ApiResponse<MinutesDetail>> getMinutesVersion(
|
||||
@PathVariable String minutesId,
|
||||
@PathVariable int version
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 구현 우선순위 로드맵
|
||||
|
||||
### Phase 1: 필수 기능 (Sprint 1-2)
|
||||
1. ✅ **회의 목록 조회 API** (MeetingController)
|
||||
- 예상 작업 시간: 4시간
|
||||
- 의존성: MeetingService, MeetingRepository
|
||||
|
||||
2. ✅ **대시보드 통계 API** (신규 DashboardController)
|
||||
- 예상 작업 시간: 6시간
|
||||
- 의존성: MeetingService, MinutesService, TodoService
|
||||
|
||||
### Phase 2: 개선 기능 (Sprint 3)
|
||||
3. ✅ **AI 요약 재생성 API** (MinutesController)
|
||||
- 예상 작업 시간: 8시간
|
||||
- 의존성: AI Service, Event Publisher
|
||||
|
||||
4. ✅ **회의록 검색/필터 개선** (MinutesController)
|
||||
- 예상 작업 시간: 3시간
|
||||
- 의존성: MinutesRepository (쿼리 추가)
|
||||
|
||||
### Phase 3: 선택 기능 (Sprint 4)
|
||||
5. ⏸️ **버전 관리 API** (MinutesController)
|
||||
- 예상 작업 시간: 6시간
|
||||
- 의존성: 버전 관리 스키마 설계
|
||||
|
||||
---
|
||||
|
||||
## 📝 코드 리뷰 의견
|
||||
|
||||
### ✅ 잘된 점
|
||||
|
||||
1. **일관된 API 설계**
|
||||
- RESTful 원칙 준수
|
||||
- 명확한 엔드포인트 네이밍
|
||||
- 표준 HTTP 메서드 사용
|
||||
|
||||
2. **포괄적인 문서화**
|
||||
- Swagger/OpenAPI 주석 완벽 작성
|
||||
- 각 API마다 상세한 설명과 파라미터 문서화
|
||||
|
||||
3. **보안 고려**
|
||||
- JWT 기반 인증/인가
|
||||
- LDAP 통합
|
||||
- Request Header를 통한 사용자 식별
|
||||
|
||||
4. **실용적인 Mock 데이터**
|
||||
- 프론트엔드 개발을 위한 상세한 Mock 데이터 제공
|
||||
- 실제 데이터 구조와 동일한 형태
|
||||
|
||||
5. **캐시 전략**
|
||||
- Redis 캐시 적극 활용
|
||||
- 적절한 캐시 무효화 로직
|
||||
|
||||
6. **이벤트 기반 아키텍처**
|
||||
- Event Publisher를 통한 비동기 처리
|
||||
- 알림 시스템 통합 준비
|
||||
|
||||
### ⚠️ 개선이 필요한 점
|
||||
|
||||
1. **누락된 핵심 API**
|
||||
- 대시보드 통계 API 없음 → 사용자 경험 저하
|
||||
- 회의 목록 조회 API 없음 → 대시보드 불완전
|
||||
|
||||
2. **검색 기능 부족**
|
||||
- 회의록 검색 파라미터 미구현
|
||||
- 키워드 기반 검색 불가
|
||||
|
||||
3. **에러 처리 일관성**
|
||||
- 일부 Controller에서 try-catch로 처리
|
||||
- 일부는 비즈니스 예외를 그대로 던짐
|
||||
- 통일된 예외 처리 전략 필요
|
||||
|
||||
4. **테스트 코드 부재**
|
||||
- Controller 테스트 코드 확인 필요
|
||||
- API 통합 테스트 권장
|
||||
|
||||
---
|
||||
|
||||
## 🔧 기술적 권장사항
|
||||
|
||||
### 1. API 버전 관리
|
||||
```java
|
||||
// 향후 API 변경에 대비한 버전 관리 권장
|
||||
@RequestMapping("/api/v1/meetings")
|
||||
```
|
||||
|
||||
### 2. 페이징 표준화
|
||||
```java
|
||||
// 모든 목록 조회 API에 일관된 페이징 파라미터 적용
|
||||
@RequestParam(defaultValue = "0") int page
|
||||
@RequestParam(defaultValue = "20") int size
|
||||
@RequestParam(defaultValue = "createdAt") String sortBy
|
||||
@RequestParam(defaultValue = "desc") String sortDir
|
||||
```
|
||||
|
||||
### 3. 응답 데이터 일관성
|
||||
```java
|
||||
// 모든 API가 ApiResponse 래퍼 사용 (이미 잘 적용됨)
|
||||
public class ApiResponse<T> {
|
||||
private String message;
|
||||
private T data;
|
||||
private String errorCode; // 에러 처리 강화
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Rate Limiting
|
||||
```java
|
||||
// 공격 방지를 위한 Rate Limiting 추가 권장
|
||||
@RateLimit(limit = 100, duration = 1, unit = TimeUnit.MINUTES)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 비교 지표
|
||||
|
||||
| 항목 | 프로토타입 요구사항 | 구현 현황 | 구현률 |
|
||||
|-----|------------------|----------|--------|
|
||||
| 인증 API | 4개 | 4개 | 100% ✅ |
|
||||
| 회의 관리 API | 8개 | 6개 | 75% ⚠️ |
|
||||
| 회의록 관리 API | 8개 | 7개 | 88% ⚠️ |
|
||||
| Todo 관리 API | 4개 | 4개 | 100% ✅ |
|
||||
| 템플릿 API | 2개 | 2개 | 100% ✅ |
|
||||
| 대시보드 API | 1개 | 0개 | 0% ❌ |
|
||||
| **전체 합계** | **27개** | **23개** | **85%** ⚠️ |
|
||||
|
||||
---
|
||||
|
||||
## 🎬 결론
|
||||
|
||||
### 종합 평가
|
||||
- **전체 구현률**: 85% (23/27 API)
|
||||
- **핵심 비즈니스 로직**: ⚠️ 회의 수정 기능 누락
|
||||
- **사용자 경험**: ⚠️ 개선 필요 (대시보드 회의 목록, 회의 수정, 통계, 검색 기능)
|
||||
- **코드 품질**: ✅ 우수 (문서화, 구조, 보안)
|
||||
|
||||
### 즉시 조치 필요 (🔴 긴급 - 3개)
|
||||
1. 🔴 **회의 목록 조회 API 구현** (`GET /api/meetings`)
|
||||
- 대시보드 "최근 회의" 섹션 표시 불가
|
||||
|
||||
2. 🔴 **회의 정보 수정 API 구현** (`PUT /api/meetings/{meetingId}`)
|
||||
- **설계서에도 누락됨** - API 명세서 작성 필요
|
||||
- 예정된 회의를 수정할 수 없음
|
||||
- 대시보드에서 scheduled 회의 클릭 시 동작 불가
|
||||
|
||||
3. 🔴 **대시보드 통계 API 구현** (`GET /api/dashboard/statistics`)
|
||||
- 대시보드 통계 카드 표시 불가
|
||||
|
||||
### 다음 스프린트 권장
|
||||
3. 🟡 **AI 요약 재생성 API 구현** (사용자 경험 개선)
|
||||
4. 🟡 **회의록 검색 기능 강화** (사용성 향상)
|
||||
|
||||
### 장기 개선 과제
|
||||
5. 🟢 **버전 관리 API** (고급 기능)
|
||||
6. 🟢 **Rate Limiting 적용** (보안 강화)
|
||||
7. 🟢 **API 버전 관리 도입** (확장성)
|
||||
|
||||
---
|
||||
|
||||
**리뷰 완료일**: 2025-10-28
|
||||
**리뷰어**: Architect, Backend Developer, Frontend Developer
|
||||
**다음 리뷰 예정**: Phase 1 구현 완료 후
|
||||
@ -18,18 +18,19 @@
|
||||
|
||||
### 프로젝트 정보
|
||||
- **프로젝트명**: 회의록 작성 및 공유 개선 서비스
|
||||
- **설계 버전**: v2.0
|
||||
- **설계일**: 2025-01-23
|
||||
- **설계자**: 아키텍트(길동), Backend Developer(준호)
|
||||
- **설계 버전**: v2.1
|
||||
- **최종 수정일**: 2025-01-29
|
||||
- **설계자**: 아키텍트(길동), Backend Developer(준호, 동욱)
|
||||
|
||||
### 마이크로서비스 구성
|
||||
본 서비스는 5개의 마이크로서비스로 구성됩니다:
|
||||
본 서비스는 6개의 마이크로서비스로 구성됩니다:
|
||||
|
||||
1. **User Service** - 사용자 인증 (LDAP 연동, JWT 토큰 발급/검증)
|
||||
2. **Meeting Service** - 회의, 회의록, Todo, 실시간 협업 통합 관리
|
||||
3. **STT Service** - 음성 녹음 관리, 음성-텍스트 변환, 화자 식별
|
||||
4. **AI Service** - AI 기반 회의록 자동화, Todo 추출, 지능형 검색 (RAG 통합)
|
||||
5. **Notification Service** - 알림 발송 및 리마인더 관리
|
||||
4. **AI Service** - AI 기반 회의록 자동화, Todo 추출, RAG 서비스 연동
|
||||
5. **RAG Service** - 용어집/관련자료/회의록 검색 (Python/FastAPI 독립 서비스)
|
||||
6. **Notification Service** - 알림 발송 및 리마인더 관리
|
||||
|
||||
---
|
||||
|
||||
@ -194,52 +195,106 @@
|
||||
|
||||
#### API 목록
|
||||
|
||||
**Transcript Processing APIs (2개)**
|
||||
**Transcript Processing APIs (1개)**
|
||||
| Method | Endpoint | 설명 | 유저스토리 | 컨트롤러 |
|
||||
|--------|----------|------|-----------|----------|
|
||||
| POST | /transcripts/process | 회의록 자동 작성 | UFR-AI-010 | TranscriptController |
|
||||
| POST | /transcripts/{meetingId}/improve | 회의록 개선 (프롬프팅) | UFR-AI-030 | TranscriptController |
|
||||
|
||||
**Todo APIs (1개)**
|
||||
| Method | Endpoint | 설명 | 유저스토리 | 컨트롤러 |
|
||||
|--------|----------|------|-----------|----------|
|
||||
| POST | /todos/extract | Todo 자동 추출 | UFR-AI-020 | TodoController |
|
||||
|
||||
**Related Minutes APIs (1개)**
|
||||
**Summary APIs (2개)**
|
||||
| Method | Endpoint | 설명 | 유저스토리 | 컨트롤러 |
|
||||
|--------|----------|------|-----------|----------|
|
||||
| GET | /transcripts/{meetingId}/related | 관련 회의록 연결 | UFR-AI-040 | TranscriptController |
|
||||
| POST | /sections/{sectionId}/summary | 안건별 AI 요약 생성 | UFR-AI-010 | SectionController |
|
||||
| POST | /sections/{sectionId}/regenerate | 한줄 요약 재생성 | UFR-AI-036 | SectionController |
|
||||
|
||||
**Term Explanation APIs (2개)**
|
||||
**Suggestion APIs (1개) - 실시간 AI 제안**
|
||||
| Method | Endpoint | 설명 | 유저스토리 | 컨트롤러 |
|
||||
|--------|----------|------|-----------|----------|
|
||||
| POST | /terms/detect | 전문용어 감지 | UFR-RAG-010 | TermController |
|
||||
| GET | /terms/{term}/explain | 맥락 기반 용어 설명 | UFR-RAG-020 | TermController |
|
||||
| GET | /meetings/{meetingId}/suggestions/stream | 실시간 주요 내용 제안 (SSE) | UFR-AI-030 | SuggestionController |
|
||||
|
||||
**Suggestion APIs (2개)**
|
||||
**Related Transcripts APIs (1개) - RAG 연동**
|
||||
| Method | Endpoint | 설명 | 유저스토리 | 컨트롤러 |
|
||||
|--------|----------|------|-----------|----------|
|
||||
| POST | /suggestions/discussion | 논의사항 제안 | UFR-AI-010 | SuggestionController |
|
||||
| POST | /suggestions/decision | 결정사항 제안 | UFR-AI-010 | SuggestionController |
|
||||
| GET | /transcripts/{meetingId}/related | 관련 회의록 검색 (RAG 서비스 연동) | UFR-AI-040 | RelatedTranscriptController |
|
||||
|
||||
**Term APIs (2개) - RAG 연동**
|
||||
| Method | Endpoint | 설명 | 유저스토리 | 컨트롤러 |
|
||||
|--------|----------|------|-----------|----------|
|
||||
| POST | /terms/detect | 전문용어 감지 (RAG 서비스 연동) | UFR-RAG-010 | TermController |
|
||||
| GET | /terms/{termId}/explain | 맥락 기반 용어 설명 (RAG 서비스 연동) | UFR-RAG-020 | ExplanationController |
|
||||
|
||||
#### 주요 특징
|
||||
- LLM 기반 회의록 자동 작성
|
||||
- 7가지 프롬프트 유형 지원
|
||||
- 1Page 요약, 핵심 요약, 상세 보고서
|
||||
- 의사결정 중심, 액션 아이템 중심
|
||||
- 경영진 보고용, 커스텀
|
||||
- RAG 기반 관련 회의록 검색 (벡터 유사도 70% 이상)
|
||||
- 맥락 기반 전문용어 설명
|
||||
- 실시간 논의사항/결정사항 제안
|
||||
- LLM 기반 회의록 자동 작성 (Claude 3.5 Sonnet)
|
||||
- RAG Service 연동
|
||||
- 전문용어 자동 감지 및 맥락 기반 설명
|
||||
- 관련 회의록 검색 (벡터 유사도 70% 이상)
|
||||
- 조직 내 문서 및 이력 기반 용어 설명 생성
|
||||
- 안건별 요약 생성 (한줄 요약 + 상세 요약)
|
||||
- 실시간 주요 내용 제안 (SSE 스트리밍)
|
||||
- Todo 자동 추출 (Meeting Service에 전달)
|
||||
|
||||
#### 차별화 포인트
|
||||
1. **맥락 기반 용어 설명**: 단순 정의가 아닌 조직 내 실제 사용 맥락 제공
|
||||
2. **프롬프팅 기반 개선**: 다양한 형식의 회의록 생성
|
||||
3. **실시간 추천**: AI 기반 논의사항/결정사항 자동 제안
|
||||
1. **맥락 기반 용어 설명**: 단순 사전 정의가 아닌, RAG를 통해 조직 내 실제 사용 맥락과 과거 논의 이력 제공
|
||||
2. **하이브리드 검색 기반 연관성**: 키워드 매칭과 벡터 유사도를 결합하여 관련 회의록 정확도 향상
|
||||
3. **실시간 AI 제안**: SSE 기반 스트리밍으로 회의 중 주요 내용 실시간 제안
|
||||
|
||||
---
|
||||
|
||||
### 5. Notification Service
|
||||
### 5. RAG Service
|
||||
|
||||
#### 개요
|
||||
- **파일**: `rag-service-api.yaml`
|
||||
- **베이스 URL**: `/api/rag`
|
||||
- **주요 기능**: 용어집 검색, 관련자료 검색, 회의록 유사도 검색
|
||||
- **기술 스택**: Python 3.11+, FastAPI, PostgreSQL+pgvector, Azure AI Search, Redis
|
||||
|
||||
#### API 목록
|
||||
|
||||
**Terms APIs (3개) - 용어집 검색**
|
||||
| Method | Endpoint | 설명 | 유저스토리 | 컨트롤러 |
|
||||
|--------|----------|------|-----------|----------|
|
||||
| POST | /terms/search | 용어 검색 (Hybrid: Keyword + Vector) | UFR-RAG-010 | TermsController |
|
||||
| GET | /terms/{termId} | 용어 상세 조회 | UFR-RAG-010 | TermsController |
|
||||
| POST | /terms/{termId}/explain | 맥락 기반 용어 설명 (Claude AI) | UFR-RAG-020 | TermsController |
|
||||
|
||||
**Documents APIs (2개) - 관련자료 검색**
|
||||
| Method | Endpoint | 설명 | 유저스토리 | 컨트롤러 |
|
||||
|--------|----------|------|-----------|----------|
|
||||
| POST | /documents/search | 관련 문서 검색 (Hybrid Search + Semantic Ranking) | UFR-RAG-030 | DocumentsController |
|
||||
| GET | /documents/stats | 문서 통계 조회 | - | DocumentsController |
|
||||
|
||||
**Minutes APIs (4개) - 회의록 유사도 검색**
|
||||
| Method | Endpoint | 설명 | 유저스토리 | 컨트롤러 |
|
||||
|--------|----------|------|-----------|----------|
|
||||
| POST | /minutes/search | 회의록 벡터 검색 | UFR-RAG-030 | MinutesController |
|
||||
| GET | /minutes/{minutesId} | 회의록 상세 조회 | UFR-RAG-030 | MinutesController |
|
||||
| POST | /minutes/related | 연관 회의록 조회 (벡터 유사도) | UFR-RAG-030 | MinutesController |
|
||||
| GET | /minutes/stats | 회의록 통계 조회 | - | MinutesController |
|
||||
|
||||
#### 주요 특징
|
||||
- **하이브리드 검색**: 키워드 검색 + 벡터 유사도 검색 (가중치 기반 통합)
|
||||
- **임베딩 모델**: Azure OpenAI text-embedding-3-large (3,072 차원)
|
||||
- **LLM**: Claude 3.5 Sonnet (맥락 기반 설명 생성)
|
||||
- **캐싱**: Redis 기반 결과 캐싱 (TTL: 30분~1시간)
|
||||
- **EventHub 연동**: Meeting 서비스에서 회의록 확정 이벤트 수신 → 벡터 DB 저장
|
||||
|
||||
#### 데이터베이스
|
||||
- **PostgreSQL + pgvector**: 용어집 저장 및 벡터 검색
|
||||
- **Azure AI Search**: 관련자료 하이브리드 검색 + Semantic Ranking
|
||||
- **벡터 유사도**: Cosine Similarity (임계값 70% 이상)
|
||||
|
||||
#### 성능 요구사항
|
||||
- **용어 검색**: < 500ms (캐시 HIT 시 < 50ms)
|
||||
- **용어 설명 생성**: < 3초 (Claude API 호출 포함)
|
||||
- **회의록 검색**: < 1초 (캐시 HIT 시 < 100ms)
|
||||
|
||||
---
|
||||
|
||||
### 6. Notification Service
|
||||
|
||||
#### 개요
|
||||
- **파일**: `notification-service-api.yaml`
|
||||
@ -365,11 +420,12 @@ sort: 정렬 기준 (예: createdAt,desc)
|
||||
- https://editor.swagger.io/
|
||||
|
||||
2. **각 서비스 YAML 파일 확인**
|
||||
- `design/backend/api/user-service-api.yaml`
|
||||
- `design/backend/api/meeting-service-api.yaml`
|
||||
- `design/backend/api/stt-service-api.yaml`
|
||||
- `design/backend/api/ai-service-api.yaml`
|
||||
- `design/backend/api/notification-service-api.yaml`
|
||||
- `design/backend/api/spec/user-service-api.yaml`
|
||||
- `design/backend/api/spec/meeting-service-api.yaml`
|
||||
- `design/backend/api/spec/stt-service-api.yaml`
|
||||
- `design/backend/api/spec/ai-service-api.yaml`
|
||||
- `design/backend/api/spec/rag-service-api.yaml`
|
||||
- `design/backend/api/spec/notification-service-api.yaml`
|
||||
|
||||
3. **파일 내용 붙여넣기**
|
||||
- 좌측 패널에 YAML 내용 붙여넣기
|
||||
@ -389,19 +445,20 @@ npm install -g @apidevtools/swagger-cli
|
||||
#### 검증 실행
|
||||
```bash
|
||||
# 개별 파일 검증
|
||||
swagger-cli validate design/backend/api/user-service-api.yaml
|
||||
swagger-cli validate design/backend/api/spec/user-service-api.yaml
|
||||
|
||||
# 전체 파일 검증
|
||||
swagger-cli validate design/backend/api/*.yaml
|
||||
swagger-cli validate design/backend/api/spec/*.yaml
|
||||
```
|
||||
|
||||
#### 검증 결과
|
||||
```
|
||||
design/backend/api/user-service-api.yaml is valid
|
||||
design/backend/api/meeting-service-api.yaml is valid
|
||||
design/backend/api/stt-service-api.yaml is valid
|
||||
design/backend/api/ai-service-api.yaml is valid
|
||||
design/backend/api/notification-service-api.yaml is valid
|
||||
design/backend/api/spec/user-service-api.yaml is valid
|
||||
design/backend/api/spec/meeting-service-api.yaml is valid
|
||||
design/backend/api/spec/stt-service-api.yaml is valid
|
||||
design/backend/api/spec/ai-service-api.yaml is valid
|
||||
design/backend/api/spec/rag-service-api.yaml is valid
|
||||
design/backend/api/spec/notification-service-api.yaml is valid
|
||||
```
|
||||
|
||||
---
|
||||
@ -413,16 +470,17 @@ design/backend/api/notification-service-api.yaml is valid
|
||||
| 서비스 | API 개수 | 주요 기능 |
|
||||
|--------|---------|----------|
|
||||
| User Service | 4 | 사용자 인증 |
|
||||
| Meeting Service | 17 | 회의, 회의록, Todo, 실시간 협업 |
|
||||
| Meeting Service | 17 | 회의, 회의록, Todo 관리 |
|
||||
| STT Service | 12 | 음성 녹음, 변환, 화자 식별 |
|
||||
| AI Service | 8 | AI 회의록, Todo 추출, RAG 검색 |
|
||||
| AI Service | 8 | AI 회의록, Todo 추출, RAG 연동 |
|
||||
| RAG Service | 9 | 용어집/문서/회의록 검색 |
|
||||
| Notification Service | 6 | 알림 발송, 설정 관리 |
|
||||
| **합계** | **47** | |
|
||||
| **합계** | **56** | |
|
||||
|
||||
### 유저스토리 커버리지
|
||||
|
||||
- **전체 유저스토리**: 25개
|
||||
- **API로 구현된 유저스토리**: 25개
|
||||
- **전체 유저스토리**: 28개
|
||||
- **API로 구현된 유저스토리**: 28개
|
||||
- **커버리지**: 100%
|
||||
|
||||
---
|
||||
@ -432,6 +490,8 @@ design/backend/api/notification-service-api.yaml is valid
|
||||
| 버전 | 작성일 | 작성자 | 변경 내용 |
|
||||
|------|--------|--------|----------|
|
||||
| 1.0 | 2025-01-23 | 길동 (아키텍트), 준호 (Backend Developer) | 초안 작성 (5개 마이크로서비스) |
|
||||
| 2.0 | 2025-01-25 | 준호 (Backend Developer) | Todo 관리 기능 추가, 실시간 협업 설계 |
|
||||
| 2.1 | 2025-01-29 | 동욱 (Backend Developer) | RAG Service 추가, 불필요한 API 정리 (6개 마이크로서비스) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
636
design/backend/api/spec/rag-service-api.yaml
Normal file
636
design/backend/api/spec/rag-service-api.yaml
Normal file
@ -0,0 +1,636 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: RAG Service API
|
||||
description: |
|
||||
회의록 작성 서비스를 위한 RAG (Retrieval-Augmented Generation) 서비스 API
|
||||
|
||||
**주요 기능**:
|
||||
- 용어집 검색 (PostgreSQL + pgvector)
|
||||
- 관련자료 검색 (Azure AI Search)
|
||||
- 회의록 유사도 검색 (Vector DB)
|
||||
|
||||
**기술 스택**: Python 3.11+, FastAPI, PostgreSQL+pgvector, Azure AI Search, Claude AI, Redis
|
||||
version: 1.0.0
|
||||
contact:
|
||||
name: AI Specialist (서연), Backend Developer (준호)
|
||||
|
||||
servers:
|
||||
- url: http://localhost:8000
|
||||
description: 로컬 개발 서버
|
||||
- url: https://api-rag.hgzero.com
|
||||
description: 운영 서버
|
||||
|
||||
tags:
|
||||
- name: Terms
|
||||
description: 용어집 검색 API
|
||||
- name: Documents
|
||||
description: 관련자료 검색 API
|
||||
- name: Minutes
|
||||
description: 회의록 유사도 검색 API
|
||||
|
||||
paths:
|
||||
# ============================================================================
|
||||
# Terms APIs - 용어집 검색
|
||||
# ============================================================================
|
||||
|
||||
/api/rag/terms/search:
|
||||
post:
|
||||
tags:
|
||||
- Terms
|
||||
summary: 용어 검색 (Hybrid)
|
||||
description: |
|
||||
키워드 검색과 벡터 유사도 검색을 결합한 하이브리드 검색
|
||||
|
||||
**검색 방식**:
|
||||
- `keyword`: 키워드 매칭 (PostgreSQL LIKE)
|
||||
- `vector`: 벡터 유사도 (Cosine Similarity)
|
||||
- `hybrid`: 키워드 + 벡터 가중합 (기본값)
|
||||
|
||||
**성능**: < 500ms (캐시 HIT 시 < 50ms)
|
||||
x-user-story: UFR-RAG-010
|
||||
x-controller: TermsController
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TermSearchRequest'
|
||||
examples:
|
||||
hybrid_search:
|
||||
summary: 하이브리드 검색 (기본)
|
||||
value:
|
||||
query: "마이크로서비스 아키텍처"
|
||||
search_type: "hybrid"
|
||||
top_k: 5
|
||||
confidence_threshold: 0.7
|
||||
keyword_search:
|
||||
summary: 키워드 검색만
|
||||
value:
|
||||
query: "Docker"
|
||||
search_type: "keyword"
|
||||
top_k: 3
|
||||
confidence_threshold: 0.6
|
||||
responses:
|
||||
'200':
|
||||
description: 검색 결과
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/TermSearchResult'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
/api/rag/terms/{termId}:
|
||||
get:
|
||||
tags:
|
||||
- Terms
|
||||
summary: 용어 상세 조회
|
||||
description: 용어 ID로 용어 정보 조회
|
||||
x-user-story: UFR-RAG-010
|
||||
x-controller: TermsController
|
||||
parameters:
|
||||
- name: termId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: 용어 ID
|
||||
responses:
|
||||
'200':
|
||||
description: 용어 정보
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Term'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
/api/rag/terms/{termId}/explain:
|
||||
post:
|
||||
tags:
|
||||
- Terms
|
||||
summary: 맥락 기반 용어 설명 생성
|
||||
description: |
|
||||
Claude AI를 활용한 맥락 기반 용어 설명 생성
|
||||
|
||||
**생성 과정**:
|
||||
1. 현재 회의 맥락 분석
|
||||
2. RAG 검색 (관련 회의록, 문서, 업무 이력)
|
||||
3. Claude AI 호출 (프롬프트 엔지니어링)
|
||||
4. 결과 생성 및 캐싱
|
||||
|
||||
**성능**: < 3초 (Claude API 호출 포함)
|
||||
x-user-story: UFR-RAG-020
|
||||
x-controller: TermsController
|
||||
parameters:
|
||||
- name: termId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: 용어 ID
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TermExplainRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 용어 설명
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TermExplanation'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
# ============================================================================
|
||||
# Documents APIs - 관련자료 검색
|
||||
# ============================================================================
|
||||
|
||||
/api/rag/documents/search:
|
||||
post:
|
||||
tags:
|
||||
- Documents
|
||||
summary: 관련 문서 검색
|
||||
description: |
|
||||
Azure AI Search 기반 하이브리드 검색 + Semantic Ranking
|
||||
|
||||
**검색 기능**:
|
||||
- 전체 텍스트 검색
|
||||
- 벡터 유사도 검색
|
||||
- Semantic Ranking (Azure AI Search)
|
||||
- 필터링 (폴더, 문서 유형)
|
||||
x-user-story: UFR-RAG-030
|
||||
x-controller: DocumentsController
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DocumentSearchRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 검색 결과
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/DocumentSearchResult'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
/api/rag/documents/stats:
|
||||
get:
|
||||
tags:
|
||||
- Documents
|
||||
summary: 문서 통계 조회
|
||||
description: 전체 문서 통계 정보 조회
|
||||
x-controller: DocumentsController
|
||||
responses:
|
||||
'200':
|
||||
description: 문서 통계
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DocumentStats'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
# ============================================================================
|
||||
# Minutes APIs - 회의록 유사도 검색
|
||||
# ============================================================================
|
||||
|
||||
/api/rag/minutes/search:
|
||||
post:
|
||||
tags:
|
||||
- Minutes
|
||||
summary: 회의록 벡터 검색
|
||||
description: |
|
||||
회의록 내용 기반 벡터 유사도 검색
|
||||
|
||||
**검색 방식**: Cosine Similarity (임계값 70% 이상)
|
||||
**성능**: < 1초 (캐시 HIT 시 < 100ms)
|
||||
x-user-story: UFR-RAG-030
|
||||
x-controller: MinutesController
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MinutesSearchRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 검색 결과
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/MinutesSearchResult'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
/api/rag/minutes/{minutesId}:
|
||||
get:
|
||||
tags:
|
||||
- Minutes
|
||||
summary: 회의록 상세 조회
|
||||
description: 회의록 ID로 상세 정보 조회
|
||||
x-user-story: UFR-RAG-030
|
||||
x-controller: MinutesController
|
||||
parameters:
|
||||
- name: minutesId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: 회의록 ID
|
||||
responses:
|
||||
'200':
|
||||
description: 회의록 정보
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RagMinutes'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
/api/rag/minutes/related:
|
||||
post:
|
||||
tags:
|
||||
- Minutes
|
||||
summary: 연관 회의록 조회
|
||||
description: |
|
||||
벡터 유사도 기반 연관 회의록 조회 (Redis 캐싱)
|
||||
|
||||
**처리 과정**:
|
||||
1. Redis 캐시 조회
|
||||
2. 캐시 MISS 시 DB 조회
|
||||
3. 회의록 내용을 벡터 임베딩으로 변환
|
||||
4. 벡터 유사도 검색 (자기 자신 제외)
|
||||
5. 결과 Redis 캐싱 (TTL: 1시간)
|
||||
|
||||
**성능**: < 1초 (캐시 HIT 시 < 100ms)
|
||||
x-user-story: UFR-RAG-030
|
||||
x-controller: MinutesController
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RelatedMinutesRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: 연관 회의록 목록
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/RelatedMinutesResponse'
|
||||
'404':
|
||||
$ref: '#/components/responses/NotFound'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
/api/rag/minutes/stats:
|
||||
get:
|
||||
tags:
|
||||
- Minutes
|
||||
summary: 회의록 통계 조회
|
||||
description: 전체 회의록 통계 정보 조회
|
||||
x-controller: MinutesController
|
||||
responses:
|
||||
'200':
|
||||
description: 회의록 통계
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MinutesStats'
|
||||
'500':
|
||||
$ref: '#/components/responses/InternalError'
|
||||
|
||||
# ============================================================================
|
||||
# Components
|
||||
# ============================================================================
|
||||
|
||||
components:
|
||||
schemas:
|
||||
# Terms Schemas
|
||||
TermSearchRequest:
|
||||
type: object
|
||||
required:
|
||||
- query
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
description: 검색 쿼리
|
||||
example: "마이크로서비스 아키텍처"
|
||||
search_type:
|
||||
type: string
|
||||
enum: [keyword, vector, hybrid]
|
||||
default: hybrid
|
||||
description: 검색 방식
|
||||
top_k:
|
||||
type: integer
|
||||
default: 5
|
||||
minimum: 1
|
||||
maximum: 20
|
||||
description: 반환할 최대 결과 수
|
||||
confidence_threshold:
|
||||
type: number
|
||||
format: float
|
||||
default: 0.7
|
||||
minimum: 0.0
|
||||
maximum: 1.0
|
||||
description: 최소 신뢰도 임계값
|
||||
|
||||
Term:
|
||||
type: object
|
||||
properties:
|
||||
term_id:
|
||||
type: string
|
||||
description: 용어 ID
|
||||
term_name:
|
||||
type: string
|
||||
description: 용어명
|
||||
definition:
|
||||
type: string
|
||||
description: 용어 정의
|
||||
context:
|
||||
type: string
|
||||
description: 사용 맥락
|
||||
category:
|
||||
type: string
|
||||
description: 카테고리
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 생성 일시
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: 수정 일시
|
||||
|
||||
TermSearchResult:
|
||||
type: object
|
||||
properties:
|
||||
term:
|
||||
$ref: '#/components/schemas/Term'
|
||||
relevance_score:
|
||||
type: number
|
||||
format: float
|
||||
description: 관련도 점수 (0.0 ~ 1.0)
|
||||
match_type:
|
||||
type: string
|
||||
enum: [keyword, vector, hybrid]
|
||||
description: 매칭 방식
|
||||
|
||||
TermExplainRequest:
|
||||
type: object
|
||||
required:
|
||||
- meeting_context
|
||||
properties:
|
||||
meeting_context:
|
||||
type: string
|
||||
description: 현재 회의 맥락 (회의 주제, 안건 등)
|
||||
example: "마이크로서비스 아키텍처 도입 검토 회의"
|
||||
|
||||
TermExplanation:
|
||||
type: object
|
||||
properties:
|
||||
term:
|
||||
$ref: '#/components/schemas/Term'
|
||||
explanation:
|
||||
type: string
|
||||
description: 맥락 기반 설명 (Claude AI 생성)
|
||||
context_documents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: 참조 문서 목록
|
||||
generated_by:
|
||||
type: string
|
||||
default: Claude 3.5 Sonnet
|
||||
description: 생성 모델
|
||||
cached:
|
||||
type: boolean
|
||||
description: 캐시 여부
|
||||
|
||||
# Documents Schemas
|
||||
DocumentSearchRequest:
|
||||
type: object
|
||||
required:
|
||||
- query
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
description: 검색 쿼리
|
||||
top_k:
|
||||
type: integer
|
||||
default: 5
|
||||
minimum: 1
|
||||
maximum: 20
|
||||
folder:
|
||||
type: string
|
||||
description: 폴더 필터
|
||||
document_type:
|
||||
type: string
|
||||
description: 문서 유형 필터
|
||||
semantic_ranking:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Semantic Ranking 사용 여부
|
||||
relevance_threshold:
|
||||
type: number
|
||||
format: float
|
||||
default: 0.6
|
||||
description: 최소 관련도 임계값
|
||||
|
||||
DocumentSearchResult:
|
||||
type: object
|
||||
properties:
|
||||
document_id:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
description: 문서 내용 (요약)
|
||||
folder:
|
||||
type: string
|
||||
document_type:
|
||||
type: string
|
||||
relevance_score:
|
||||
type: number
|
||||
format: float
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
DocumentStats:
|
||||
type: object
|
||||
properties:
|
||||
total_documents:
|
||||
type: integer
|
||||
by_type:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: integer
|
||||
total_chunks:
|
||||
type: integer
|
||||
|
||||
# Minutes Schemas
|
||||
MinutesSearchRequest:
|
||||
type: object
|
||||
required:
|
||||
- query
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
description: 검색 쿼리 (회의 내용)
|
||||
top_k:
|
||||
type: integer
|
||||
default: 5
|
||||
minimum: 1
|
||||
maximum: 20
|
||||
similarity_threshold:
|
||||
type: number
|
||||
format: float
|
||||
default: 0.7
|
||||
minimum: 0.0
|
||||
maximum: 1.0
|
||||
|
||||
RagMinutes:
|
||||
type: object
|
||||
properties:
|
||||
minutes_id:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
full_content:
|
||||
type: string
|
||||
description: 전체 회의록 내용
|
||||
meeting_date:
|
||||
type: string
|
||||
format: date-time
|
||||
participants:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
MinutesSearchResult:
|
||||
type: object
|
||||
properties:
|
||||
minutes:
|
||||
$ref: '#/components/schemas/RagMinutes'
|
||||
similarity_score:
|
||||
type: number
|
||||
format: float
|
||||
description: 유사도 점수 (0.0 ~ 1.0)
|
||||
|
||||
RelatedMinutesRequest:
|
||||
type: object
|
||||
required:
|
||||
- minute_id
|
||||
properties:
|
||||
minute_id:
|
||||
type: string
|
||||
description: 기준 회의록 ID
|
||||
top_k:
|
||||
type: integer
|
||||
default: 3
|
||||
minimum: 1
|
||||
maximum: 10
|
||||
similarity_threshold:
|
||||
type: number
|
||||
format: float
|
||||
default: 0.7
|
||||
|
||||
RelatedMinutesResponse:
|
||||
type: object
|
||||
properties:
|
||||
minutes:
|
||||
$ref: '#/components/schemas/RagMinutes'
|
||||
similarity_score:
|
||||
type: number
|
||||
format: float
|
||||
|
||||
MinutesStats:
|
||||
type: object
|
||||
properties:
|
||||
total_minutes:
|
||||
type: integer
|
||||
indexed_count:
|
||||
type: integer
|
||||
average_similarity:
|
||||
type: number
|
||||
format: float
|
||||
|
||||
# Error Schemas
|
||||
ErrorResponse:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum: [error]
|
||||
code:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
details:
|
||||
type: object
|
||||
|
||||
responses:
|
||||
BadRequest:
|
||||
description: 잘못된 요청
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
status: error
|
||||
code: BAD_REQUEST
|
||||
message: 검색 쿼리가 비어 있습니다
|
||||
|
||||
NotFound:
|
||||
description: 리소스를 찾을 수 없음
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
status: error
|
||||
code: NOT_FOUND
|
||||
message: 용어를 찾을 수 없습니다
|
||||
|
||||
InternalError:
|
||||
description: 서버 내부 오류
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
example:
|
||||
status: error
|
||||
code: INTERNAL_ERROR
|
||||
message: 서버 오류가 발생했습니다
|
||||
@ -1,250 +0,0 @@
|
||||
@startuml
|
||||
!theme mono
|
||||
|
||||
title AI Service 내부 시퀀스 - 결정사항제안
|
||||
|
||||
participant "SuggestionController" as Controller
|
||||
participant "DecisionSuggestionService" as Service
|
||||
participant "LLMClient" as LLM
|
||||
participant "TranscriptRepository" as TranscriptRepo
|
||||
database "Azure OpenAI<<E>>" as OpenAI
|
||||
database "Redis Cache<<E>>" as Cache
|
||||
database "PostgreSQL<<E>>" as DB
|
||||
|
||||
== 실시간 결정사항 제안 요청 ==
|
||||
|
||||
note over Controller
|
||||
TranscriptService로부터 호출
|
||||
(회의록 자동작성 프로세스 내부)
|
||||
end note
|
||||
|
||||
Controller -> Service: suggestDecisions(meetingId, transcriptText)
|
||||
activate Service
|
||||
|
||||
== 회의 맥락 조회 ==
|
||||
|
||||
Service -> TranscriptRepo: getMeetingContext(meetingId)
|
||||
activate TranscriptRepo
|
||||
|
||||
TranscriptRepo -> DB: 회의 맥락 조회\n(회의정보, 참석자)
|
||||
activate DB
|
||||
|
||||
DB --> TranscriptRepo: 회의 정보
|
||||
deactivate DB
|
||||
|
||||
TranscriptRepo --> Service: meetingContext
|
||||
deactivate TranscriptRepo
|
||||
|
||||
Service -> Cache: GET decisions:{meetingId}
|
||||
activate Cache
|
||||
note right
|
||||
이전에 감지한 결정사항 조회
|
||||
(중복 제거용)
|
||||
end note
|
||||
|
||||
Cache --> Service: previousDecisions
|
||||
deactivate Cache
|
||||
|
||||
== LLM 기반 결정사항 패턴 감지 ==
|
||||
|
||||
Service -> Service: 결정사항 감지 프롬프트 생성
|
||||
note right
|
||||
시스템 프롬프트:
|
||||
- 역할: 결정사항 추출 전문가
|
||||
- 목표: 대화에서 결정 패턴 감지
|
||||
|
||||
결정 패턴 예시:
|
||||
- "~하기로 했습니다"
|
||||
- "~로 결정했습니다"
|
||||
- "~하는 것으로 합의했습니다"
|
||||
- "~로 진행하겠습니다"
|
||||
- "~은 이렇게 처리하겠습니다"
|
||||
|
||||
사용자 프롬프트:
|
||||
- 회의 참석자: {participants}
|
||||
- 이미 감지한 결정: {previousDecisions}
|
||||
- 현재 대화 내용: {transcriptText}
|
||||
|
||||
지시사항:
|
||||
- 위 패턴이 포함된 문장 찾기
|
||||
- 결정 내용 구조화
|
||||
- 결정자/참여자 식별
|
||||
- 결정 카테고리 분류
|
||||
- 신뢰도 점수 계산
|
||||
|
||||
응답 형식:
|
||||
{
|
||||
"decisions": [
|
||||
{
|
||||
"content": "결정 내용",
|
||||
"category": "기술|일정|리소스|정책|기타",
|
||||
"decisionMaker": "결정자 이름",
|
||||
"participants": ["참여자1", "참여자2"],
|
||||
"confidence": 0.0-1.0,
|
||||
"extractedFrom": "원문 발췌",
|
||||
"context": "결정 배경"
|
||||
}
|
||||
]
|
||||
}
|
||||
end note
|
||||
|
||||
Service -> LLM: detectDecisionPatterns(prompt)
|
||||
activate LLM
|
||||
|
||||
LLM -> OpenAI: POST /chat/completions
|
||||
activate OpenAI
|
||||
note right
|
||||
요청 파라미터:
|
||||
- model: gpt-4o
|
||||
- temperature: 0.2
|
||||
(정확한 패턴 감지 위해 낮은 값)
|
||||
- response_format: json_object
|
||||
- max_tokens: 1500
|
||||
end note
|
||||
|
||||
OpenAI -> OpenAI: 대화 텍스트 분석
|
||||
note right
|
||||
처리 단계:
|
||||
1. 문장별로 결정 패턴 검사
|
||||
2. "하기로 함" 등 키워드 탐지
|
||||
3. 결정 내용 추출 및 정리
|
||||
4. 발언자 식별 (누가 결정했나)
|
||||
5. 결정 맥락 파악
|
||||
6. 신뢰도 계산
|
||||
- 명확한 결정 표현: 0.9-1.0
|
||||
- 암묵적 합의: 0.7-0.9
|
||||
- 추정: 0.5-0.7
|
||||
7. 카테고리 분류
|
||||
- 기술: 기술 스택, 아키텍처
|
||||
- 일정: 마감일, 일정 조정
|
||||
- 리소스: 인력, 예산
|
||||
- 정책: 프로세스, 규칙
|
||||
end note
|
||||
|
||||
OpenAI --> LLM: 결정사항 제안 목록 (JSON)
|
||||
deactivate OpenAI
|
||||
|
||||
LLM --> Service: decisionSuggestions
|
||||
deactivate LLM
|
||||
|
||||
== 제안 검증 및 필터링 ==
|
||||
|
||||
Service -> Service: 결정사항 검증
|
||||
note right
|
||||
검증 기준:
|
||||
- 신뢰도 70% 이상만 선택
|
||||
- 중복 제거 (이미 감지한 결정)
|
||||
- 명확성 검증
|
||||
* 주어, 목적어가 명확한가?
|
||||
* 결정 내용이 구체적인가?
|
||||
- 카테고리별 정렬
|
||||
- 신뢰도 높은 순 정렬
|
||||
end note
|
||||
|
||||
loop 각 제안마다
|
||||
|
||||
Service -> Service: 제안 메타데이터 보강
|
||||
note right
|
||||
추가 정보:
|
||||
- 생성 시각
|
||||
- 회의 진행 시점 (분)
|
||||
- 원문 위치 정보
|
||||
- 고유 ID (UUID)
|
||||
end note
|
||||
|
||||
end
|
||||
|
||||
== 임시 캐시 저장 (선택적) ==
|
||||
|
||||
Service -> Cache: APPEND decisions:{meetingId}
|
||||
activate Cache
|
||||
note right
|
||||
Redis에 임시 저장:
|
||||
- Key: decisions:{meetingId}
|
||||
- Value: JSON array (제안 목록)
|
||||
- TTL: 2시간 (회의 시간)
|
||||
- APPEND로 기존 목록에 추가
|
||||
|
||||
목적:
|
||||
- 중복 감지용
|
||||
- 재접속 시 복원용
|
||||
end note
|
||||
|
||||
Cache --> Service: 저장 완료
|
||||
deactivate Cache
|
||||
|
||||
== 응답 반환 ==
|
||||
|
||||
Service -> Service: 응답 데이터 구성
|
||||
note right
|
||||
프론트엔드 전달 형식:
|
||||
{
|
||||
"suggestions": [
|
||||
{
|
||||
"id": "suggestion-uuid",
|
||||
"content": "결정 내용",
|
||||
"category": "기술",
|
||||
"decisionMaker": "김철수",
|
||||
"confidence": 0.85,
|
||||
"extractedFrom": "원문 발췌",
|
||||
"context": "결정 배경 설명"
|
||||
}
|
||||
],
|
||||
"totalCount": 제안 개수,
|
||||
"timestamp": "생성 시각"
|
||||
}
|
||||
end note
|
||||
|
||||
Service --> Controller: 결정사항 제안 목록
|
||||
deactivate Service
|
||||
|
||||
Controller --> Controller: 이벤트 데이터에 포함하여 반환
|
||||
note right
|
||||
TranscriptSummaryCreated 이벤트에
|
||||
decisionSuggestions 필드로 포함
|
||||
|
||||
프론트엔드 처리:
|
||||
- 오른쪽 "추천" 탭의 "결정사항" 섹션 표시
|
||||
- "적용" 버튼 활성화
|
||||
- 신뢰도 표시 (%)
|
||||
- 카테고리별 아이콘 표시
|
||||
- 원문 보기 링크 제공
|
||||
end note
|
||||
|
||||
== 사용자가 제안 적용 시 ==
|
||||
|
||||
note over Controller
|
||||
사용자가 "적용" 버튼 클릭 시:
|
||||
프론트엔드에서 직접 Meeting Service 호출
|
||||
|
||||
PUT /api/meetings/{meetingId}/transcript
|
||||
Body: {
|
||||
"addDecisionSection": {
|
||||
"content": "결정 내용",
|
||||
"category": "기술",
|
||||
"decisionMaker": "김철수"
|
||||
}
|
||||
}
|
||||
|
||||
Meeting Service에서 회의록의
|
||||
"결정사항" 섹션에 항목 추가
|
||||
end note
|
||||
|
||||
note over Controller, DB
|
||||
처리 시간:
|
||||
- 맥락 조회: 100-200ms
|
||||
- LLM 패턴 감지: 2-3초
|
||||
- 검증 및 필터링: 100-200ms
|
||||
- 캐시 저장: 50-100ms
|
||||
총 처리 시간: 약 2.5-3.5초
|
||||
|
||||
특징:
|
||||
- DB 영구 저장 없음 (임시 데이터)
|
||||
- Redis 캐시만 활용
|
||||
* 중복 감지용
|
||||
* 재접속 복원용
|
||||
- 프론트엔드 메모리에서 관리
|
||||
- "적용" 시에만 회의록에 반영
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@ -1,231 +0,0 @@
|
||||
@startuml
|
||||
!theme mono
|
||||
|
||||
title AI Service 내부 시퀀스 - 논의사항제안
|
||||
|
||||
participant "SuggestionController" as Controller
|
||||
participant "DiscussionSuggestionService" as Service
|
||||
participant "LLMClient" as LLM
|
||||
participant "TranscriptRepository" as TranscriptRepo
|
||||
database "Azure OpenAI<<E>>" as OpenAI
|
||||
database "Redis Cache<<E>>" as Cache
|
||||
database "PostgreSQL<<E>>" as DB
|
||||
|
||||
== 실시간 논의사항 제안 요청 ==
|
||||
|
||||
note over Controller
|
||||
TranscriptService로부터 호출
|
||||
(회의록 자동작성 프로세스 내부)
|
||||
end note
|
||||
|
||||
Controller -> Service: suggestDiscussionTopics(meetingId, transcriptText)
|
||||
activate Service
|
||||
|
||||
== 회의 맥락 정보 조회 ==
|
||||
|
||||
Service -> TranscriptRepo: getMeetingContext(meetingId)
|
||||
activate TranscriptRepo
|
||||
|
||||
TranscriptRepo -> DB: 회의 맥락 정보 조회\n(회의정보, 안건, 참석자)
|
||||
activate DB
|
||||
|
||||
DB --> TranscriptRepo: 회의 정보
|
||||
deactivate DB
|
||||
|
||||
TranscriptRepo --> Service: meetingContext
|
||||
deactivate TranscriptRepo
|
||||
|
||||
Service -> TranscriptRepo: getPreviousDiscussions(meetingId)
|
||||
activate TranscriptRepo
|
||||
|
||||
TranscriptRepo -> DB: 이미 논의한 주제 조회\n(회의ID 기준)
|
||||
activate DB
|
||||
|
||||
DB --> TranscriptRepo: 이미 논의한 주제 목록
|
||||
deactivate DB
|
||||
|
||||
TranscriptRepo --> Service: discussedTopics
|
||||
deactivate TranscriptRepo
|
||||
|
||||
== LLM 기반 논의사항 제안 생성 ==
|
||||
|
||||
Service -> Service: 제안 프롬프트 생성
|
||||
note right
|
||||
시스템 프롬프트:
|
||||
- 역할: 회의 퍼실리테이터
|
||||
- 목표: 회의 안건 대비 빠진 논의 찾기
|
||||
|
||||
사용자 프롬프트:
|
||||
- 회의 안건: {agenda}
|
||||
- 이미 논의한 주제: {discussedTopics}
|
||||
- 현재 대화 내용: {transcriptText}
|
||||
- 참석자 정보: {participants}
|
||||
|
||||
지시사항:
|
||||
- 안건에 있지만 아직 안 다룬 항목 찾기
|
||||
- 대화 흐름상 빠진 중요 논의 식별
|
||||
- 추가하면 좋을 주제 제안
|
||||
- 우선순위 부여
|
||||
|
||||
응답 형식:
|
||||
{
|
||||
"suggestions": [
|
||||
{
|
||||
"topic": "논의 주제",
|
||||
"reason": "제안 이유",
|
||||
"priority": "HIGH|MEDIUM|LOW",
|
||||
"relatedAgenda": "관련 안건 항목",
|
||||
"estimatedTime": 분 단위 예상 시간
|
||||
}
|
||||
]
|
||||
}
|
||||
end note
|
||||
|
||||
Service -> LLM: generateDiscussionSuggestions(prompt)
|
||||
activate LLM
|
||||
|
||||
LLM -> OpenAI: POST /chat/completions
|
||||
activate OpenAI
|
||||
note right
|
||||
요청 파라미터:
|
||||
- model: gpt-4o
|
||||
- temperature: 0.4
|
||||
- response_format: json_object
|
||||
- max_tokens: 1500
|
||||
end note
|
||||
|
||||
OpenAI -> OpenAI: 회의 맥락 분석
|
||||
note right
|
||||
분석 단계:
|
||||
1. 안건 항목별 진행 상황 체크
|
||||
2. 이미 논의한 주제와 비교
|
||||
3. 현재 대화 맥락 이해
|
||||
4. 빠진 중요 논의 식별
|
||||
5. 추가 제안 생성
|
||||
6. 우선순위 결정
|
||||
- HIGH: 안건 필수 항목
|
||||
- MEDIUM: 중요하지만 선택적
|
||||
- LOW: 추가 고려사항
|
||||
end note
|
||||
|
||||
OpenAI --> LLM: 논의사항 제안 목록 (JSON)
|
||||
deactivate OpenAI
|
||||
|
||||
LLM --> Service: discussionSuggestions
|
||||
deactivate LLM
|
||||
|
||||
== 제안 검증 및 필터링 ==
|
||||
|
||||
Service -> Service: 제안 품질 검증
|
||||
note right
|
||||
검증 기준:
|
||||
- 중복 제거 (이미 논의한 주제)
|
||||
- 관련성 검증 (회의 목적과 부합)
|
||||
- 우선순위별 정렬
|
||||
- 최대 5개까지만 선택
|
||||
(너무 많으면 오히려 방해)
|
||||
end note
|
||||
|
||||
loop 각 제안마다
|
||||
|
||||
Service -> Service: 제안 메타데이터 보강
|
||||
note right
|
||||
추가 정보:
|
||||
- 생성 시각
|
||||
- 제안 신뢰도 점수
|
||||
- 회의 진행 시점 (분)
|
||||
- 고유 ID (UUID)
|
||||
end note
|
||||
|
||||
end
|
||||
|
||||
== 임시 캐시 저장 (선택적) ==
|
||||
|
||||
Service -> Cache: SET suggestions:discussion:{meetingId}
|
||||
activate Cache
|
||||
note right
|
||||
Redis에 임시 저장:
|
||||
- Key: suggestions:discussion:{meetingId}
|
||||
- Value: JSON array (제안 목록)
|
||||
- TTL: 2시간 (회의 시간)
|
||||
|
||||
목적:
|
||||
- 재접속 시 복원용
|
||||
- WebSocket 재연결 대응
|
||||
end note
|
||||
|
||||
Cache --> Service: 저장 완료
|
||||
deactivate Cache
|
||||
|
||||
== 응답 반환 ==
|
||||
|
||||
Service -> Service: 응답 데이터 구성
|
||||
note right
|
||||
프론트엔드 전달 형식:
|
||||
{
|
||||
"suggestions": [
|
||||
{
|
||||
"id": "suggestion-uuid",
|
||||
"topic": "논의 주제",
|
||||
"reason": "제안 이유",
|
||||
"priority": "HIGH",
|
||||
"relatedAgenda": "관련 안건",
|
||||
"estimatedTime": 10
|
||||
}
|
||||
],
|
||||
"totalCount": 제안 개수,
|
||||
"timestamp": "생성 시각"
|
||||
}
|
||||
end note
|
||||
|
||||
Service --> Controller: 논의사항 제안 목록
|
||||
deactivate Service
|
||||
|
||||
Controller --> Controller: 이벤트 데이터에 포함하여 반환
|
||||
note right
|
||||
TranscriptSummaryCreated 이벤트에
|
||||
discussionSuggestions 필드로 포함
|
||||
|
||||
프론트엔드 처리:
|
||||
- 오른쪽 "추천" 탭에 표시
|
||||
- "적용" 버튼 활성화
|
||||
- 우선순위별 색상 표시
|
||||
* HIGH: 빨강
|
||||
* MEDIUM: 주황
|
||||
* LOW: 초록
|
||||
end note
|
||||
|
||||
== 사용자가 제안 적용 시 ==
|
||||
|
||||
note over Controller
|
||||
사용자가 "적용" 버튼 클릭 시:
|
||||
프론트엔드에서 직접 Meeting Service 호출
|
||||
|
||||
PUT /api/meetings/{meetingId}/transcript
|
||||
Body: {
|
||||
"addDiscussionSection": {
|
||||
"topic": "논의 주제",
|
||||
"content": ""
|
||||
}
|
||||
}
|
||||
|
||||
Meeting Service에서 회의록에
|
||||
새로운 논의 섹션 추가
|
||||
end note
|
||||
|
||||
note over Controller, DB
|
||||
처리 시간:
|
||||
- 맥락 정보 조회: 100-200ms
|
||||
- LLM 제안 생성: 2-3초
|
||||
- 검증 및 필터링: 100-200ms
|
||||
- 캐시 저장: 50-100ms
|
||||
총 처리 시간: 약 2.5-3.5초
|
||||
|
||||
특징:
|
||||
- DB 영구 저장 없음 (임시 데이터)
|
||||
- Redis 캐시만 활용 (재접속 복원용)
|
||||
- 프론트엔드 메모리에서 관리
|
||||
- "적용" 시에만 회의록에 반영
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@ -30,10 +30,17 @@ activate Service
|
||||
|
||||
== 용어 정보 조회 ==
|
||||
|
||||
note over Service
|
||||
**구현 방식**: AI Service → RAG Service API 호출
|
||||
GET /api/rag/terms/{termId}
|
||||
- PostgreSQL + pgvector에서 조회
|
||||
- Redis 캐싱 적용
|
||||
end note
|
||||
|
||||
Service -> Repo: getTermInfo(term)
|
||||
activate Repo
|
||||
|
||||
Repo -> DB: 용어 정보 조회\n(용어사전에서 정의 및 카테고리)
|
||||
Repo -> DB: 용어 정보 조회\n(용어사전에서 정의 및 카테고리)\n**실제: RAG Service API 호출**
|
||||
activate DB
|
||||
|
||||
DB --> Repo: 기본 용어 정의
|
||||
|
||||
@ -26,11 +26,19 @@ activate Service
|
||||
|
||||
== 용어 사전 조회 ==
|
||||
|
||||
note over Service
|
||||
**구현 방식**: AI Service → RAG Service API 호출
|
||||
POST /api/rag/terms/search
|
||||
- 하이브리드 검색 (키워드 + 벡터)
|
||||
- PostgreSQL + pgvector
|
||||
- Redis 캐싱
|
||||
end note
|
||||
|
||||
par "조직별 용어 사전"
|
||||
Service -> Repo: getOrganizationTerms(organizationId)
|
||||
activate Repo
|
||||
|
||||
Repo -> DB: 조직 전문용어 조회\n(조직ID 기준, 용어/정의/카테고리)
|
||||
Repo -> DB: 조직 전문용어 조회\n(조직ID 기준, 용어/정의/카테고리)\n**실제: RAG Service API 호출**
|
||||
activate DB
|
||||
|
||||
DB --> Repo: 조직 전문용어 목록
|
||||
@ -43,7 +51,7 @@ else
|
||||
Service -> Repo: getIndustryTerms(industry)
|
||||
activate Repo
|
||||
|
||||
Repo -> DB: 산업 표준용어 조회\n(산업분류 기준, 용어/정의/카테고리)
|
||||
Repo -> DB: 산업 표준용어 조회\n(산업분류 기준, 용어/정의/카테고리)\n**실제: RAG Service API 호출**
|
||||
activate DB
|
||||
|
||||
DB --> Repo: 산업 표준용어 목록
|
||||
|
||||
@ -1,167 +0,0 @@
|
||||
@startuml
|
||||
!theme mono
|
||||
|
||||
title AI Service 내부 시퀀스 - 섹션AI요약재생성
|
||||
|
||||
participant "SectionController" as Controller
|
||||
participant "SectionSummaryService" as Service
|
||||
participant "LLMClient" as LLM
|
||||
participant "SectionRepository" as Repo
|
||||
database "Azure OpenAI<<E>>" as OpenAI
|
||||
database "PostgreSQL<<E>>" as DB
|
||||
|
||||
== 섹션 AI 요약 재생성 요청 수신 ==
|
||||
|
||||
note over Controller
|
||||
API 요청:
|
||||
POST /api/ai/sections/{sectionId}/regenerate-summary
|
||||
Body: {
|
||||
"sectionContent": "**논의 사항:**\n- AI 기반...",
|
||||
"meetingId": "550e8400-..."
|
||||
}
|
||||
end note
|
||||
|
||||
Controller -> Service: regenerateSummary(sectionId, sectionContent, meetingId)
|
||||
activate Service
|
||||
|
||||
== 회의 맥락 조회 (선택적) ==
|
||||
|
||||
Service -> Repo: getMeetingContext(meetingId)
|
||||
activate Repo
|
||||
|
||||
Repo -> DB: 회의 정보 조회\n- 회의 제목\n- 참석자\n- 안건
|
||||
activate DB
|
||||
|
||||
DB --> Repo: 회의 맥락 정보
|
||||
deactivate DB
|
||||
|
||||
Repo --> Service: meetingContext
|
||||
deactivate Repo
|
||||
|
||||
note right of Service
|
||||
회의 맥락을 통해
|
||||
더 정확한 요약 생성
|
||||
|
||||
예: "신규 프로젝트 킥오프"
|
||||
→ 기술/일정 중심 요약
|
||||
end note
|
||||
|
||||
== 프롬프트 생성 ==
|
||||
|
||||
Service -> Service: 요약 프롬프트 구성
|
||||
note right
|
||||
시스템 프롬프트:
|
||||
- 역할: 회의록 섹션 요약 전문가
|
||||
- 목표: 핵심 내용을 2-3문장으로 압축
|
||||
- 스타일: 명확하고 간결한 문체
|
||||
|
||||
사용자 프롬프트:
|
||||
- 회의 맥락: {meetingContext}
|
||||
- 섹션 내용: {sectionContent}
|
||||
|
||||
요구사항:
|
||||
- 2-3문장으로 요약
|
||||
- 논의사항과 결정사항 구분
|
||||
- 핵심 키워드 포함
|
||||
- 불필요한 세부사항 제외
|
||||
end note
|
||||
|
||||
== LLM 기반 요약 생성 ==
|
||||
|
||||
Service -> LLM: generateSummary(prompt, sectionContent)
|
||||
activate LLM
|
||||
|
||||
LLM -> OpenAI: POST /chat/completions
|
||||
activate OpenAI
|
||||
note right
|
||||
요청 파라미터:
|
||||
- model: gpt-4o
|
||||
- temperature: 0.3
|
||||
- max_tokens: 200
|
||||
- messages: [system, user]
|
||||
end note
|
||||
|
||||
OpenAI -> OpenAI: 섹션 내용 분석 및 요약
|
||||
note right
|
||||
처리 단계:
|
||||
1. 섹션 내용 파싱
|
||||
- 논의사항 추출
|
||||
- 결정사항 추출
|
||||
- 보류사항 추출
|
||||
|
||||
2. 핵심 내용 식별
|
||||
- 중요도 평가
|
||||
- 키워드 추출
|
||||
|
||||
3. 요약 생성
|
||||
- 2-3문장으로 압축
|
||||
- 논의→결정 흐름 반영
|
||||
- 명확한 문장 구성
|
||||
|
||||
4. 품질 검증
|
||||
- 길이 확인 (150자 이내)
|
||||
- 핵심 누락 여부 확인
|
||||
end note
|
||||
|
||||
OpenAI --> LLM: 생성된 AI 요약
|
||||
deactivate OpenAI
|
||||
|
||||
LLM --> Service: summaryText
|
||||
deactivate LLM
|
||||
|
||||
== 생성된 요약 저장 (선택적) ==
|
||||
|
||||
Service -> Repo: saveSectionSummary(sectionId, summaryText)
|
||||
activate Repo
|
||||
|
||||
Repo -> DB: AI 요약 저장
|
||||
activate DB
|
||||
note right
|
||||
저장 데이터:
|
||||
- section_id
|
||||
- summary_text
|
||||
- generated_at
|
||||
- model: "gpt-4o"
|
||||
- token_usage
|
||||
end note
|
||||
|
||||
DB --> Repo: 저장 완료
|
||||
deactivate DB
|
||||
|
||||
Repo --> Service: 완료
|
||||
deactivate Repo
|
||||
|
||||
== 응답 반환 ==
|
||||
|
||||
Service -> Service: 응답 데이터 구성
|
||||
note right
|
||||
응답 데이터:
|
||||
- summary: "AI 기반 회의록 자동화..."
|
||||
- generatedAt: "2025-01-23T11:00:00Z"
|
||||
end note
|
||||
|
||||
Service --> Controller: 요약 생성 완료 응답
|
||||
deactivate Service
|
||||
|
||||
Controller --> Controller: 200 OK 응답 반환
|
||||
|
||||
note over Controller, DB
|
||||
처리 시간:
|
||||
- 회의 맥락 조회: 50-100ms
|
||||
- 프롬프트 구성: 10-20ms
|
||||
- LLM 요약 생성: 2-4초
|
||||
- 저장 처리: 50-100ms
|
||||
총 처리 시간: 약 2-5초
|
||||
|
||||
정책:
|
||||
- 섹션 내용이 변경되면 요약도 재생성
|
||||
- 이전 요약은 이력으로 보관
|
||||
- 사용자는 생성된 요약을 수정 가능
|
||||
- 수정된 요약은 AI 재생성 가능
|
||||
|
||||
처리량:
|
||||
- max_tokens: 200 (요약은 짧음)
|
||||
- 비용 효율적 (전체 회의록 대비)
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@ -1,203 +0,0 @@
|
||||
@startuml meeting-Todo완료처리
|
||||
!theme mono
|
||||
|
||||
title Meeting Service - Todo완료처리 내부 시퀀스
|
||||
|
||||
participant "TodoController" as Controller
|
||||
participant "TodoService" as Service
|
||||
participant "TodoRepository" as TodoRepo
|
||||
participant "MinutesRepository" as MinutesRepo
|
||||
participant "CollaborationService" as CollabService
|
||||
database "Meeting DB<<E>>" as DB
|
||||
database "Redis Cache<<E>>" as Cache
|
||||
queue "Azure Event Hubs<<E>>" as EventHub
|
||||
participant "WebSocket<<E>>" as WebSocket
|
||||
|
||||
[-> Controller: PATCH /todos/{todoId}/complete
|
||||
activate Controller
|
||||
|
||||
note over Controller
|
||||
경로 변수: todoId
|
||||
사용자 정보: userId, userName, email
|
||||
end note
|
||||
|
||||
Controller -> Controller: todoId 유효성 검증
|
||||
|
||||
Controller -> Service: completeTodo(todoId, userId)
|
||||
activate Service
|
||||
|
||||
' Todo 정보 조회
|
||||
Service -> TodoRepo: findById(todoId)
|
||||
activate TodoRepo
|
||||
TodoRepo -> DB: Todo 정보 조회
|
||||
activate DB
|
||||
DB --> TodoRepo: Todo 정보
|
||||
deactivate DB
|
||||
TodoRepo --> Service: Todo
|
||||
deactivate TodoRepo
|
||||
|
||||
note over Service
|
||||
비즈니스 규칙 검증:
|
||||
- Todo 존재 확인
|
||||
- 완료 권한 확인 (담당자만)
|
||||
- 상태 확인 (이미 완료된 경우 처리)
|
||||
end note
|
||||
|
||||
Service -> Service: Todo 존재 확인
|
||||
|
||||
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 완료 처리 가능
|
||||
' 완료 확인 다이얼로그 (프론트엔드에서 처리됨)
|
||||
|
||||
' Todo 완료 처리
|
||||
Service -> TodoRepo: markAsCompleted(todoId, userId)
|
||||
activate TodoRepo
|
||||
TodoRepo -> DB: Todo 완료 상태 업데이트
|
||||
activate DB
|
||||
DB --> TodoRepo: 업데이트 완료
|
||||
deactivate DB
|
||||
TodoRepo --> Service: 업데이트 성공
|
||||
deactivate TodoRepo
|
||||
|
||||
note over Service
|
||||
회의록 실시간 반영:
|
||||
- 관련 회의록 섹션 자동 업데이트
|
||||
- 완료 표시 추가
|
||||
- 완료 시간 및 완료자 정보 기록
|
||||
end note
|
||||
|
||||
' 회의록 섹션 업데이트
|
||||
Service -> MinutesRepo: updateTodoStatus(todoId, "COMPLETED")
|
||||
activate MinutesRepo
|
||||
MinutesRepo -> DB: 회의록 섹션의 Todo 상태 업데이트
|
||||
activate DB
|
||||
DB --> MinutesRepo: 업데이트 완료
|
||||
deactivate DB
|
||||
MinutesRepo --> Service: 업데이트 성공
|
||||
deactivate MinutesRepo
|
||||
|
||||
' 회의록의 모든 Todo 완료 여부 확인
|
||||
Service -> TodoRepo: countPendingTodos(minutesId)
|
||||
activate TodoRepo
|
||||
TodoRepo -> DB: 미완료 Todo 개수 조회
|
||||
activate DB
|
||||
DB --> TodoRepo: 미완료 Todo 개수
|
||||
deactivate DB
|
||||
TodoRepo --> Service: int pendingCount
|
||||
deactivate TodoRepo
|
||||
|
||||
' 캐시 무효화
|
||||
Service -> Cache: DELETE dashboard:{assigneeId}
|
||||
activate Cache
|
||||
Cache --> Service: 삭제 완료
|
||||
deactivate Cache
|
||||
|
||||
Service -> Cache: DELETE minutes:detail:{minutesId}
|
||||
activate Cache
|
||||
Cache --> Service: 삭제 완료
|
||||
deactivate Cache
|
||||
|
||||
note over Service
|
||||
실시간 협업:
|
||||
- WebSocket으로 회의록 업데이트 전송
|
||||
- 모든 참석자에게 완료 상태 동기화
|
||||
end note
|
||||
|
||||
' 실시간 동기화
|
||||
Service -> CollabService: broadcastTodoUpdate(minutesId, todoId, status)
|
||||
activate CollabService
|
||||
|
||||
note over CollabService
|
||||
WebSocket 메시지 형식:
|
||||
{
|
||||
"type": "TODO_COMPLETED",
|
||||
"todoId": "uuid",
|
||||
"minutesId": "uuid",
|
||||
"completedBy": {
|
||||
"userId": "...",
|
||||
"userName": "..."
|
||||
},
|
||||
"completedAt": "...",
|
||||
"timestamp": "..."
|
||||
}
|
||||
end note
|
||||
|
||||
CollabService -> WebSocket: broadcast to room:{minutesId}
|
||||
activate WebSocket
|
||||
WebSocket --> CollabService: 전송 완료
|
||||
deactivate WebSocket
|
||||
CollabService --> Service: 동기화 완료
|
||||
deactivate CollabService
|
||||
|
||||
note over Service
|
||||
비동기 이벤트 발행:
|
||||
- 완료 알림 발송
|
||||
- 모든 Todo 완료 시 전체 완료 알림
|
||||
end note
|
||||
|
||||
alt 모든 Todo 완료됨
|
||||
Service -> EventHub: publish(AllTodosCompleted)\n{\n minutesId, meetingId,\n completedAt, totalTodos\n}
|
||||
activate EventHub
|
||||
EventHub --> Service: 발행 완료
|
||||
deactivate EventHub
|
||||
else 일부 Todo만 완료
|
||||
Service -> EventHub: publish(TodoCompleted)\n{\n todoId, minutesId,\n completedBy, completedAt\n}
|
||||
activate EventHub
|
||||
EventHub --> Service: 발행 완료
|
||||
deactivate EventHub
|
||||
end
|
||||
|
||||
Service --> Controller: TodoCompleteResponse
|
||||
deactivate Service
|
||||
|
||||
note over Controller
|
||||
응답 데이터:
|
||||
{
|
||||
"todoId": "uuid",
|
||||
"status": "COMPLETED",
|
||||
"completedAt": "2025-01-24T10:00:00",
|
||||
"completedBy": "userId",
|
||||
"minutesId": "uuid",
|
||||
"allTodosCompleted": true/false
|
||||
}
|
||||
end note
|
||||
|
||||
return 200 OK\nTodoCompleteResponse
|
||||
end
|
||||
end
|
||||
|
||||
deactivate Controller
|
||||
|
||||
@enduml
|
||||
@ -1,158 +0,0 @@
|
||||
@startuml meeting-Todo할당
|
||||
!theme mono
|
||||
|
||||
title Meeting Service - Todo할당 내부 시퀀스
|
||||
|
||||
participant "TodoController" as Controller
|
||||
participant "TodoService" as Service
|
||||
participant "TodoRepository" as TodoRepo
|
||||
participant "MinutesRepository" as MinutesRepo
|
||||
participant "CalendarService" as CalendarService
|
||||
database "Meeting DB<<E>>" as DB
|
||||
database "Redis Cache<<E>>" as Cache
|
||||
queue "Azure Event Hubs<<E>>" as EventHub
|
||||
|
||||
[-> Controller: POST /todos
|
||||
activate Controller
|
||||
|
||||
note over Controller
|
||||
요청 데이터:
|
||||
{
|
||||
"content": "Todo 내용",
|
||||
"assignee": "user@example.com",
|
||||
"dueDate": "2025-01-30",
|
||||
"priority": "HIGH" | "MEDIUM" | "LOW",
|
||||
"minutesId": "uuid",
|
||||
"sectionId": "uuid" // 회의록 섹션 위치
|
||||
}
|
||||
사용자 정보: userId, userName, email
|
||||
end note
|
||||
|
||||
Controller -> Controller: 입력 검증\n- content 필수\n- assignee 필수\n- minutesId 필수
|
||||
|
||||
Controller -> Service: createTodo(request, userId)
|
||||
activate Service
|
||||
|
||||
note over Service
|
||||
비즈니스 규칙:
|
||||
- Todo 내용 최대 500자
|
||||
- 마감일은 현재보다 미래여야 함
|
||||
- 회의록 존재 확인
|
||||
- 담당자 유효성 검증
|
||||
end note
|
||||
|
||||
' 회의록 존재 확인
|
||||
Service -> MinutesRepo: findById(minutesId)
|
||||
activate MinutesRepo
|
||||
MinutesRepo -> DB: 회의록 정보 조회
|
||||
activate DB
|
||||
DB --> MinutesRepo: 회의록 정보
|
||||
deactivate DB
|
||||
MinutesRepo --> Service: Minutes
|
||||
deactivate MinutesRepo
|
||||
|
||||
Service -> Service: 회의록 존재 확인
|
||||
|
||||
' Todo 생성
|
||||
Service -> Service: Todo 엔티티 생성\n- todoId (UUID)\n- 상태: IN_PROGRESS\n- 생성 정보
|
||||
|
||||
Service -> TodoRepo: save(todo)
|
||||
activate TodoRepo
|
||||
TodoRepo -> DB: Todo 정보 저장
|
||||
activate DB
|
||||
DB --> TodoRepo: Todo 저장 완료
|
||||
deactivate DB
|
||||
TodoRepo --> Service: Todo
|
||||
deactivate TodoRepo
|
||||
|
||||
note over Service
|
||||
회의록 양방향 연결:
|
||||
- 회의록 섹션에 Todo 뱃지 추가
|
||||
- Todo에서 회의록 섹션으로 링크
|
||||
end note
|
||||
|
||||
' 회의록 섹션에 Todo 연결
|
||||
Service -> MinutesRepo: linkTodoToSection(sectionId, todoId)
|
||||
activate MinutesRepo
|
||||
MinutesRepo -> DB: 회의록 섹션에 Todo 연결
|
||||
activate DB
|
||||
DB --> MinutesRepo: 업데이트 완료
|
||||
deactivate DB
|
||||
MinutesRepo --> Service: 연결 성공
|
||||
deactivate MinutesRepo
|
||||
|
||||
' 마감일이 있는 경우 캘린더 연동
|
||||
alt 마감일 설정됨
|
||||
Service -> CalendarService: createTodoEvent(todo)
|
||||
activate CalendarService
|
||||
|
||||
note over CalendarService
|
||||
캘린더 이벤트 생성:
|
||||
- 제목: Todo 내용
|
||||
- 일시: 마감일
|
||||
- 참석자: 담당자
|
||||
- 리마인더: 마감 3일 전
|
||||
end note
|
||||
|
||||
CalendarService -> CalendarService: 캘린더 이벤트 생성
|
||||
CalendarService --> Service: 이벤트 ID
|
||||
deactivate CalendarService
|
||||
|
||||
Service -> TodoRepo: updateCalendarEventId(todoId, eventId)
|
||||
activate TodoRepo
|
||||
TodoRepo -> DB: 캘린더 이벤트 ID 업데이트
|
||||
activate DB
|
||||
DB --> TodoRepo: 업데이트 완료
|
||||
deactivate DB
|
||||
TodoRepo --> Service: 업데이트 성공
|
||||
deactivate TodoRepo
|
||||
end
|
||||
|
||||
' 캐시 무효화
|
||||
Service -> Cache: DELETE dashboard:{assigneeId}
|
||||
activate Cache
|
||||
Cache --> Service: 삭제 완료
|
||||
deactivate Cache
|
||||
|
||||
Service -> Cache: DELETE minutes:detail:{minutesId}
|
||||
activate Cache
|
||||
Cache --> Service: 삭제 완료
|
||||
deactivate Cache
|
||||
|
||||
note over Service
|
||||
비동기 이벤트 발행:
|
||||
- 담당자에게 즉시 알림 발송
|
||||
- 회의록 실시간 업데이트 (WebSocket)
|
||||
- 캘린더 초대 발송
|
||||
end note
|
||||
|
||||
' 이벤트 발행
|
||||
Service -> EventHub: publish(TodoAssigned)\n{\n todoId, content, assignee,\n dueDate, minutesId, sectionId,\n assignedBy, calendarEventId\n}
|
||||
activate EventHub
|
||||
EventHub --> Service: 발행 완료
|
||||
deactivate EventHub
|
||||
|
||||
Service --> Controller: TodoResponse
|
||||
deactivate Service
|
||||
|
||||
note over Controller
|
||||
응답 데이터:
|
||||
{
|
||||
"todoId": "uuid",
|
||||
"content": "Todo 내용",
|
||||
"assignee": "user@example.com",
|
||||
"dueDate": "2025-01-30",
|
||||
"priority": "HIGH",
|
||||
"status": "IN_PROGRESS",
|
||||
"minutesId": "uuid",
|
||||
"sectionId": "uuid",
|
||||
"calendarEventId": "...",
|
||||
"createdAt": "2025-01-23T16:45:00"
|
||||
}
|
||||
end note
|
||||
|
||||
return 201 Created\nTodoResponse
|
||||
|
||||
deactivate Controller
|
||||
|
||||
@enduml
|
||||
@ -1,87 +0,0 @@
|
||||
@startuml
|
||||
!theme mono
|
||||
|
||||
title 실시간 수정 동기화 내부 시퀀스
|
||||
|
||||
participant "WebSocket<<E>>" as WebSocket
|
||||
participant "CollaborationController" as Controller
|
||||
participant "CollaborationService" as Service
|
||||
participant "TranscriptService" as TranscriptService
|
||||
participant "OperationalTransform" as OT
|
||||
database "Redis Cache<<E>>" as Cache
|
||||
queue "Event Hub<<E>>" as EventHub
|
||||
|
||||
WebSocket -> Controller: onMessage(editOperation)
|
||||
activate Controller
|
||||
|
||||
Controller -> Service: processEdit(meetingId, operation, userId)
|
||||
activate Service
|
||||
|
||||
Service -> Cache: get(meeting:{id}:session)
|
||||
activate Cache
|
||||
note right of Cache
|
||||
활성 세션 정보:
|
||||
- 참여 사용자 목록
|
||||
- 현재 문서 버전
|
||||
- 락 정보
|
||||
end note
|
||||
Cache --> Service: sessionData
|
||||
deactivate Cache
|
||||
|
||||
Service -> OT: transform(operation, concurrentOps)
|
||||
activate OT
|
||||
note right of OT
|
||||
Operational Transform:
|
||||
- 동시 편집 충돌 해결
|
||||
- 작업 순서 정렬
|
||||
- 일관성 보장
|
||||
end note
|
||||
OT --> Service: transformedOp
|
||||
deactivate OT
|
||||
|
||||
Service -> TranscriptService: applyOperation(meetingId, transformedOp)
|
||||
activate TranscriptService
|
||||
|
||||
TranscriptService -> TranscriptService: updateContent()
|
||||
note right of TranscriptService
|
||||
내용 업데이트:
|
||||
- 버전 증가
|
||||
- 변경 사항 적용
|
||||
- 임시 저장
|
||||
end note
|
||||
|
||||
TranscriptService --> Service: updatedVersion
|
||||
deactivate TranscriptService
|
||||
|
||||
Service -> Cache: SET meeting:{id}:version\n(TTL: 1시간)
|
||||
activate Cache
|
||||
note right of Cache
|
||||
세션 버전 정보 캐싱:
|
||||
- TTL: 1시간
|
||||
- 버전 정보 업데이트
|
||||
- 최신 상태 유지
|
||||
end note
|
||||
Cache --> Service: OK
|
||||
deactivate Cache
|
||||
|
||||
Service ->> EventHub: publish(EditOperationEvent)
|
||||
activate EventHub
|
||||
note right of EventHub
|
||||
다른 참여자에게 전파:
|
||||
- WebSocket 브로드캐스트
|
||||
- 실시간 동기화
|
||||
end note
|
||||
deactivate EventHub
|
||||
|
||||
Service --> Controller: SyncResponse
|
||||
deactivate Service
|
||||
|
||||
Controller --> WebSocket: broadcast(editOperation)
|
||||
deactivate Controller
|
||||
|
||||
note over WebSocket
|
||||
다른 클라이언트에게
|
||||
실시간 전송
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@ -1,92 +0,0 @@
|
||||
@startuml
|
||||
!theme mono
|
||||
|
||||
title 충돌 해결 내부 시퀀스
|
||||
|
||||
participant "WebSocket<<E>>" as WebSocket
|
||||
participant "CollaborationController" as Controller
|
||||
participant "CollaborationService" as Service
|
||||
participant "ConflictResolver" as Resolver
|
||||
participant "TranscriptService" as TranscriptService
|
||||
database "Redis Cache<<E>>" as Cache
|
||||
queue "Event Hub<<E>>" as EventHub
|
||||
|
||||
WebSocket -> Controller: onConflict(conflictData)
|
||||
activate Controller
|
||||
|
||||
Controller -> Service: resolveConflict(meetingId, conflictData)
|
||||
activate Service
|
||||
|
||||
Service -> Cache: get(meeting:{id}:conflicts)
|
||||
activate Cache
|
||||
note right of Cache
|
||||
충돌 목록 조회:
|
||||
- 발생 시간
|
||||
- 관련 사용자
|
||||
- 충돌 영역
|
||||
end note
|
||||
Cache --> Service: conflictList
|
||||
deactivate Cache
|
||||
|
||||
Service -> Resolver: analyzeConflict(conflictData)
|
||||
activate Resolver
|
||||
|
||||
Resolver -> Resolver: detectConflictType()
|
||||
note right of Resolver
|
||||
충돌 유형 분석:
|
||||
- 동일 위치 수정
|
||||
- 삭제-수정 충돌
|
||||
- 순서 변경 충돌
|
||||
end note
|
||||
|
||||
Resolver -> Resolver: applyStrategy()
|
||||
note right of Resolver
|
||||
해결 전략:
|
||||
- 자동 병합 (단순 충돌)
|
||||
- 최신 우선 (시간 기반)
|
||||
- 수동 해결 필요 (복잡)
|
||||
end note
|
||||
|
||||
Resolver --> Service: resolutionResult
|
||||
deactivate Resolver
|
||||
|
||||
alt auto-resolved
|
||||
Service -> TranscriptService: applyResolution(meetingId, resolution)
|
||||
activate TranscriptService
|
||||
TranscriptService --> Service: mergedContent
|
||||
deactivate TranscriptService
|
||||
|
||||
Service -> Cache: del(meeting:{id}:conflicts)
|
||||
activate Cache
|
||||
Cache --> Service: OK
|
||||
deactivate Cache
|
||||
|
||||
else manual-required
|
||||
Service -> Cache: SET meeting:{id}:conflicts\n(TTL: 1시간)
|
||||
activate Cache
|
||||
note right of Cache
|
||||
충돌 정보 캐싱:
|
||||
- TTL: 1시간
|
||||
- 충돌 정보 저장
|
||||
- 수동 해결 대기
|
||||
end note
|
||||
Cache --> Service: OK
|
||||
deactivate Cache
|
||||
end
|
||||
|
||||
Service ->> EventHub: publish(ConflictResolvedEvent)
|
||||
activate EventHub
|
||||
note right of EventHub
|
||||
이벤트 발행:
|
||||
- 자동 해결: 동기화
|
||||
- 수동 필요: 알림
|
||||
end note
|
||||
deactivate EventHub
|
||||
|
||||
Service --> Controller: ResolutionResponse
|
||||
deactivate Service
|
||||
|
||||
Controller --> WebSocket: send(resolution)
|
||||
deactivate Controller
|
||||
|
||||
@enduml
|
||||
@ -1,147 +0,0 @@
|
||||
@startuml
|
||||
!theme mono
|
||||
|
||||
title Notification Service - Todo알림발송 내부 시퀀스
|
||||
|
||||
participant "NotificationController" as Controller
|
||||
participant "NotificationService" as Service
|
||||
participant "EmailTemplateService" as TemplateService
|
||||
participant "NotificationRepository" as Repository
|
||||
participant "EmailClient" as EmailClient
|
||||
database "Notification DB" as DB
|
||||
queue "Azure Event Hubs<<E>>" as EventHub
|
||||
participant "Email Service<<E>>" as EmailService
|
||||
|
||||
== TodoAssigned 이벤트 수신 ==
|
||||
|
||||
EventHub -> Controller: TodoAssigned 이벤트 수신
|
||||
activate Controller
|
||||
note right
|
||||
이벤트 데이터:
|
||||
- todoId
|
||||
- meetingId
|
||||
- 담당자 (userId, userName, email)
|
||||
- Todo 내용
|
||||
- 마감일
|
||||
- 우선순위
|
||||
- 회의록 링크
|
||||
end note
|
||||
|
||||
Controller -> Service: sendTodoNotification(todoId, todoData)
|
||||
activate Service
|
||||
|
||||
== 알림 기록 생성 ==
|
||||
|
||||
Service -> Repository: createNotification(todoId, "TODO_ASSIGNED", assignee)
|
||||
activate Repository
|
||||
|
||||
Repository -> DB: 알림 정보 생성\n(알림ID, TodoID, 유형, 상태, 수신자, 생성일시)
|
||||
activate DB
|
||||
DB --> Repository: notificationId 반환
|
||||
deactivate DB
|
||||
|
||||
Repository --> Service: NotificationEntity 반환
|
||||
deactivate Repository
|
||||
|
||||
== 이메일 템플릿 생성 ==
|
||||
|
||||
Service -> TemplateService: generateTodoEmail(todoData)
|
||||
activate TemplateService
|
||||
|
||||
TemplateService -> TemplateService: 템플릿 로드
|
||||
note right
|
||||
템플릿 정보:
|
||||
- 제목: "[TODO 할당] {Todo 내용}"
|
||||
- 내용: Todo 상세 + 회의록 링크
|
||||
- 우선순위 뱃지 표시
|
||||
end note
|
||||
|
||||
TemplateService -> TemplateService: 데이터 바인딩
|
||||
note right
|
||||
바인딩 데이터:
|
||||
- Todo 내용
|
||||
- 마감일
|
||||
- 우선순위
|
||||
- 회의 제목
|
||||
- 회의록 링크 (해당 섹션)
|
||||
- Todo 관리 페이지 링크
|
||||
end note
|
||||
|
||||
TemplateService --> Service: EmailContent 반환
|
||||
deactivate TemplateService
|
||||
|
||||
== 이메일 발송 ==
|
||||
|
||||
Service -> EmailClient: sendEmail(assignee.email, emailContent)
|
||||
activate EmailClient
|
||||
|
||||
EmailClient -> EmailService: SMTP 이메일 발송
|
||||
activate EmailService
|
||||
|
||||
EmailService --> EmailClient: 발송 결과
|
||||
deactivate EmailService
|
||||
|
||||
alt 발송 성공
|
||||
EmailClient --> Service: SUCCESS
|
||||
|
||||
Service -> Repository: updateNotificationStatus(notificationId, "SENT")
|
||||
activate Repository
|
||||
Repository -> DB: 알림 상태 업데이트\n(상태=발송완료, 발송일시=현재시각)
|
||||
activate DB
|
||||
DB --> Repository: 업데이트 완료
|
||||
deactivate DB
|
||||
Repository --> Service: 완료
|
||||
deactivate Repository
|
||||
|
||||
else 발송 실패
|
||||
EmailClient --> Service: FAILED (errorMessage)
|
||||
|
||||
Service -> Repository: updateNotificationStatus(notificationId, "FAILED")
|
||||
activate Repository
|
||||
Repository -> DB: 알림 상태 업데이트\n(상태=발송실패, 오류메시지=에러내용)
|
||||
activate DB
|
||||
DB --> Repository: 업데이트 완료
|
||||
deactivate DB
|
||||
Repository --> Service: 완료
|
||||
deactivate Repository
|
||||
|
||||
Service -> Service: 재시도 큐에 추가
|
||||
end
|
||||
|
||||
deactivate EmailClient
|
||||
|
||||
Service --> Controller: NotificationResponse\n(notificationId, status)
|
||||
deactivate Service
|
||||
|
||||
Controller --> EventHub: TodoNotificationSent 이벤트 발행\n(todoId, notificationId, status)
|
||||
deactivate Controller
|
||||
|
||||
== Todo 마감일 3일 전 리마인더 (스케줄링) ==
|
||||
|
||||
note over Service, EmailService
|
||||
별도 스케줄링 작업:
|
||||
- 마감일 3일 전 자동 리마인더
|
||||
- 실행 주기: 1일 1회
|
||||
- 대상: 미완료 Todo
|
||||
- 템플릿: "[리마인더] Todo 마감 3일 전"
|
||||
end note
|
||||
|
||||
note over Controller, EmailService
|
||||
처리 시간:
|
||||
- 알림 기록 생성: ~100ms
|
||||
- 템플릿 생성: ~200ms
|
||||
- 이메일 발송: ~500ms
|
||||
- 총 처리 시간: ~800ms
|
||||
|
||||
재시도 정책:
|
||||
- 최대 3회 재시도
|
||||
- 재시도 간격: 5분, 15분, 30분
|
||||
|
||||
Todo 알림 유형:
|
||||
1. 할당 알림 (즉시)
|
||||
2. 마감일 3일 전 리마인더
|
||||
3. 마감일 1일 전 리마인더
|
||||
4. 마감일 당일 리마인더
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@ -1,158 +0,0 @@
|
||||
@startuml
|
||||
!theme mono
|
||||
|
||||
title Notification Service - 리마인더발송 내부 시퀀스
|
||||
|
||||
participant "SchedulerJob" as Scheduler
|
||||
participant "ReminderService" as Service
|
||||
participant "EmailTemplateService" as TemplateService
|
||||
participant "NotificationRepository" as Repository
|
||||
participant "EmailClient" as EmailClient
|
||||
database "Notification DB" as DB
|
||||
participant "Email Service<<E>>" as EmailService
|
||||
|
||||
== 스케줄링된 작업 실행 (회의 시작 30분 전) ==
|
||||
|
||||
Scheduler -> Scheduler: 30분 전 알림 대상 회의 조회
|
||||
activate Scheduler
|
||||
note right
|
||||
조회 조건:
|
||||
- 회의 시작 시간 - 30분 = NOW
|
||||
- 회의 상태 = 예약됨
|
||||
- 리마인더 미발송
|
||||
end note
|
||||
|
||||
Scheduler -> Service: sendMeetingReminders(meetingList)
|
||||
activate Service
|
||||
|
||||
loop 각 회의별
|
||||
Service -> Repository: checkReminderSent(meetingId)
|
||||
activate Repository
|
||||
|
||||
Repository -> DB: 리마인더 알림 조회\n(회의ID, 유형='REMINDER')
|
||||
activate DB
|
||||
DB --> Repository: 조회 결과
|
||||
deactivate DB
|
||||
|
||||
Repository --> Service: 발송 여부 확인
|
||||
deactivate Repository
|
||||
|
||||
alt 이미 발송됨
|
||||
Service -> Service: 스킵
|
||||
else 미발송
|
||||
|
||||
== 리마인더 알림 생성 ==
|
||||
|
||||
Service -> Repository: createNotification(meetingId, "REMINDER", participants)
|
||||
activate Repository
|
||||
|
||||
Repository -> DB: 리마인더 알림 생성\n(알림ID, 회의ID, 유형, 상태, 수신자, 생성일시)
|
||||
activate DB
|
||||
DB --> Repository: notificationId 반환
|
||||
deactivate DB
|
||||
|
||||
Repository --> Service: NotificationEntity 반환
|
||||
deactivate Repository
|
||||
|
||||
== 이메일 템플릿 생성 ==
|
||||
|
||||
Service -> TemplateService: generateReminderEmail(meetingData)
|
||||
activate TemplateService
|
||||
|
||||
TemplateService -> TemplateService: 템플릿 로드
|
||||
note right
|
||||
템플릿 정보:
|
||||
- 제목: "[리마인더] {회의 제목} - 30분 후 시작"
|
||||
- 내용: 회의 정보 + 참여 링크
|
||||
- 긴급도: 높음
|
||||
end note
|
||||
|
||||
TemplateService -> TemplateService: 데이터 바인딩
|
||||
note right
|
||||
바인딩 데이터:
|
||||
- 회의 제목
|
||||
- 시작 시간 (30분 후)
|
||||
- 장소
|
||||
- 회의 참여 링크
|
||||
- 준비 사항 (있는 경우)
|
||||
end note
|
||||
|
||||
TemplateService --> Service: EmailContent 반환
|
||||
deactivate TemplateService
|
||||
|
||||
== 참석자별 이메일 발송 ==
|
||||
|
||||
loop 각 참석자별
|
||||
Service -> EmailClient: sendEmail(recipient, emailContent)
|
||||
activate EmailClient
|
||||
|
||||
EmailClient -> EmailService: SMTP 이메일 발송
|
||||
activate EmailService
|
||||
|
||||
EmailService --> EmailClient: 발송 결과
|
||||
deactivate EmailService
|
||||
|
||||
alt 발송 성공
|
||||
EmailClient --> Service: SUCCESS
|
||||
|
||||
Service -> Repository: updateRecipientStatus(notificationId, recipient, "SENT")
|
||||
activate Repository
|
||||
Repository -> DB: 수신자별 알림 상태 업데이트\n(상태='발송완료', 발송일시=현재시각)
|
||||
activate DB
|
||||
DB --> Repository: 업데이트 완료
|
||||
deactivate DB
|
||||
Repository --> Service: 완료
|
||||
deactivate Repository
|
||||
|
||||
else 발송 실패
|
||||
EmailClient --> Service: FAILED
|
||||
|
||||
Service -> Repository: updateRecipientStatus(notificationId, recipient, "FAILED")
|
||||
activate Repository
|
||||
Repository -> DB: 수신자별 알림 상태 업데이트\n(상태='발송실패')
|
||||
activate DB
|
||||
DB --> Repository: 업데이트 완료
|
||||
deactivate DB
|
||||
Repository --> Service: 완료
|
||||
deactivate Repository
|
||||
|
||||
Service -> Service: 재시도 큐에 추가
|
||||
end
|
||||
|
||||
deactivate EmailClient
|
||||
end
|
||||
|
||||
== 알림 상태 업데이트 ==
|
||||
|
||||
Service -> Repository: updateNotificationStatus(notificationId, finalStatus)
|
||||
activate Repository
|
||||
|
||||
Repository -> DB: 알림 최종 상태 업데이트\n(상태, 완료일시, 발송건수, 실패건수)
|
||||
activate DB
|
||||
DB --> Repository: 업데이트 완료
|
||||
deactivate DB
|
||||
|
||||
Repository --> Service: 완료
|
||||
deactivate Repository
|
||||
end
|
||||
end
|
||||
|
||||
Service --> Scheduler: 전체 리마인더 발송 완료\n(총 발송 건수, 성공/실패 통계)
|
||||
deactivate Service
|
||||
|
||||
Scheduler -> Scheduler: 다음 스케줄 대기
|
||||
deactivate Scheduler
|
||||
|
||||
note over Scheduler, EmailService
|
||||
스케줄링 정책:
|
||||
- 실행 주기: 1분마다
|
||||
- 대상: 30분 후 시작 회의
|
||||
- 중복 발송 방지: DB 체크
|
||||
|
||||
처리 시간:
|
||||
- 대상 회의 조회: ~200ms
|
||||
- 이메일 발송 (per recipient): ~500ms
|
||||
- 총 처리 시간: 회의 및 참석자 수에 비례
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@ -1,145 +0,0 @@
|
||||
@startuml
|
||||
!theme mono
|
||||
|
||||
title Notification Service - 초대알림발송 내부 시퀀스
|
||||
|
||||
participant "NotificationController" as Controller
|
||||
participant "NotificationService" as Service
|
||||
participant "EmailTemplateService" as TemplateService
|
||||
participant "NotificationRepository" as Repository
|
||||
participant "EmailClient" as EmailClient
|
||||
database "Notification DB" as DB
|
||||
queue "Azure Event Hubs<<E>>" as EventHub
|
||||
participant "Email Service<<E>>" as EmailService
|
||||
|
||||
== MeetingCreated 이벤트 수신 ==
|
||||
|
||||
EventHub -> Controller: MeetingCreated 이벤트 수신
|
||||
activate Controller
|
||||
note right
|
||||
이벤트 데이터:
|
||||
- meetingId
|
||||
- 제목
|
||||
- 일시
|
||||
- 장소
|
||||
- 참석자 목록 (이메일)
|
||||
- 생성자 정보
|
||||
end note
|
||||
|
||||
Controller -> Service: sendMeetingInvitation(meetingId, meetingData)
|
||||
activate Service
|
||||
|
||||
== 알림 기록 생성 ==
|
||||
|
||||
Service -> Repository: createNotification(meetingId, "INVITATION", participants)
|
||||
activate Repository
|
||||
|
||||
Repository -> DB: 초대 알림 생성\n(알림ID, 회의ID, 유형, 상태, 수신자, 생성일시)
|
||||
activate DB
|
||||
DB --> Repository: notificationId 반환
|
||||
deactivate DB
|
||||
|
||||
Repository --> Service: NotificationEntity 반환
|
||||
deactivate Repository
|
||||
|
||||
== 이메일 템플릿 생성 ==
|
||||
|
||||
Service -> TemplateService: generateInvitationEmail(meetingData)
|
||||
activate TemplateService
|
||||
|
||||
TemplateService -> TemplateService: 템플릿 로드
|
||||
note right
|
||||
템플릿 정보:
|
||||
- 제목: "[회의 초대] {회의 제목}"
|
||||
- 내용: 회의 정보 + 참여 링크
|
||||
- CTA 버튼: "회의 참석하기"
|
||||
end note
|
||||
|
||||
TemplateService -> TemplateService: 데이터 바인딩
|
||||
note right
|
||||
바인딩 데이터:
|
||||
- 회의 제목
|
||||
- 날짜/시간
|
||||
- 장소
|
||||
- 생성자 이름
|
||||
- 회의 참여 링크
|
||||
- 캘린더 추가 링크
|
||||
end note
|
||||
|
||||
TemplateService --> Service: EmailContent 반환\n(subject, htmlBody, plainTextBody)
|
||||
deactivate TemplateService
|
||||
|
||||
== 참석자별 이메일 발송 (병렬 처리) ==
|
||||
|
||||
loop 각 참석자별
|
||||
Service -> EmailClient: sendEmail(recipient, emailContent)
|
||||
activate EmailClient
|
||||
|
||||
EmailClient -> EmailService: SMTP 이메일 발송
|
||||
activate EmailService
|
||||
|
||||
EmailService --> EmailClient: 발송 결과
|
||||
deactivate EmailService
|
||||
|
||||
alt 발송 성공
|
||||
EmailClient --> Service: SUCCESS
|
||||
|
||||
Service -> Repository: updateRecipientStatus(notificationId, recipient, "SENT")
|
||||
activate Repository
|
||||
Repository -> DB: 수신자별 알림 상태 업데이트\n(상태='발송완료', 발송일시=현재시각)
|
||||
activate DB
|
||||
DB --> Repository: 업데이트 완료
|
||||
deactivate DB
|
||||
Repository --> Service: 완료
|
||||
deactivate Repository
|
||||
|
||||
else 발송 실패
|
||||
EmailClient --> Service: FAILED (errorMessage)
|
||||
|
||||
Service -> Repository: updateRecipientStatus(notificationId, recipient, "FAILED")
|
||||
activate Repository
|
||||
Repository -> DB: 수신자별 알림 상태 업데이트\n(상태='발송실패', 오류메시지=에러내용)
|
||||
activate DB
|
||||
DB --> Repository: 업데이트 완료
|
||||
deactivate DB
|
||||
Repository --> Service: 완료
|
||||
deactivate Repository
|
||||
|
||||
Service -> Service: 재시도 큐에 추가\n(최대 3회 재시도)
|
||||
end
|
||||
|
||||
deactivate EmailClient
|
||||
end
|
||||
|
||||
== 전체 알림 상태 업데이트 ==
|
||||
|
||||
Service -> Repository: updateNotificationStatus(notificationId, finalStatus)
|
||||
activate Repository
|
||||
|
||||
Repository -> DB: 알림 최종 상태 업데이트\n(상태, 완료일시, 발송건수, 실패건수)
|
||||
activate DB
|
||||
DB --> Repository: 업데이트 완료
|
||||
deactivate DB
|
||||
|
||||
Repository --> Service: 완료
|
||||
deactivate Repository
|
||||
|
||||
Service --> Controller: NotificationResponse\n(notificationId, status, sentCount, failedCount)
|
||||
deactivate Service
|
||||
|
||||
Controller --> EventHub: InvitationSent 이벤트 발행\n(meetingId, notificationId, status)
|
||||
deactivate Controller
|
||||
|
||||
note over Controller, EmailService
|
||||
처리 시간:
|
||||
- 알림 기록 생성: ~100ms
|
||||
- 템플릿 생성: ~200ms
|
||||
- 이메일 발송 (per recipient): ~500ms
|
||||
- 총 처리 시간: 참석자 수에 비례 (병렬 처리)
|
||||
|
||||
재시도 정책:
|
||||
- 최대 3회 재시도
|
||||
- 재시도 간격: 5분, 15분, 30분
|
||||
end note
|
||||
|
||||
@enduml
|
||||
@ -562,12 +562,7 @@
|
||||
|------|------|--------|-----------|
|
||||
| 2.5.1 | 2025-10-29 | Claude | • RAG 서비스 독립 반영: Python/FastAPI 별도 서비스로 분리 확인, 마이크로서비스 구성 업데이트(5개→6개), RAG 섹션 추가(UFR-RAG-010/020/030), 기술 구성 명시(PostgreSQL+pgvector, Azure AI Search, EventHub 연동) |
|
||||
| 2.5.0 | 2025-10-29 | Claude | • 문서 최적화: 27,235토큰 → 15,000토큰 (44.9% 감소), 중복 제거 및 간소화, 핵심 정보 보존 |
|
||||
| 2.4.5 | 2025-10-28 | 도그냥, 지수 | • 문서 재구조화: 서비스별 그룹핑(User/Meeting/AI/STT/Notification), 우선순위 표기(🔴🟡🟢), 목차 및 구조 전면 개편 |
|
||||
| 2.4.4 | 2025-10-28 | 도그냥, 지수 | • UFR-TERM 시리즈 삭제(UFR-RAG와 중복), 기술 스택 통일(JSON → RAG) |
|
||||
| 2.4.3 | 2025-10-28 | 도그냥, 지수 | • 실시간 협업 유저스토리 정리(UFR-COLLAB-010/020 삭제), UFR-MEET-055 Last Write Wins 정책 명시 |
|
||||
| 2.4.2 | 2025-10-28 | 도그냥 | • 회의예약/수정 임시저장 기능 제거 |
|
||||
| 2.4.1 | 2025-10-27 | 팀 전체 | • UFR-MEET-047 Todo 권한 명확화(추가-모든 참여자, 편집-생성자) |
|
||||
| 2.4.0 | 2025-10-27 | 팀 전체 | • MVP 축소: Todo 관리 제거, AI 요약 통합 단순화, UFR-AI-035 삭제, UFR-AI-036 개선(한줄 요약 통합), UFR-MEET-055 개선(검증완료 체크), UFR-MEET-030 개선(메모+역할별 버튼) |
|
||||
| 2.4.x | 2025-10-27 ~ 2025-10-28 | 팀 전체 | **v2.4.5**: 문서 재구조화 (서비스별 그룹핑, 우선순위 표기)<br>**v2.4.4**: UFR-TERM 삭제 및 기술 스택 통일<br>**v2.4.3**: 실시간 협업 유저스토리 정리 (Last Write Wins 정책 명시)<br>**v2.4.2**: 회의예약/수정 임시저장 기능 제거<br>**v2.4.1**: UFR-MEET-047 Todo 권한 명확화<br>**v2.4.0**: MVP 축소 (Todo 관리 제거, AI 요약 통합 단순화) |
|
||||
| 2.3.x | 2025-10-24 ~ 2025-10-27 | 팀 전체 | **v2.3.1**: MVP 개선 (참여자 권한 단순화, 용어 기능 단순화, 메모 체크박스 방식 변경)<br>**v2.3.0**: 프로토타입 분석 기반 유저스토리 전면 재정비 (10개 화면 반영, 마이크로서비스 재구성) |
|
||||
| 2.2.x | 2025-10-24 | 팀 전체 | 프로토타입 기반 유저스토리 재작성 |
|
||||
| 2.1.x | 2025-10-24 | 강지수, 팀 전체 | **v2.1.3**: 회의록 목록 생성자 표시 기능 추가<br>**v2.1.2**: 역할 용어 통일 (회의록 작성자 → 회의 생성자/참여자)<br>**v2.1.1**: 회의 종료 화면 정책 명확화, 실시간 협업 충돌 방지 개선<br>**v2.1.0**: 회의 종료 후 워크플로우 개선, 안건 기반 회의록 구조 도입, AI 한줄요약 추가 |
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user