Merge branch 'wip/document-yabo'

This commit is contained in:
yabo0812 2025-10-29 18:01:04 +09:00
commit bca8d6e729
18 changed files with 858 additions and 2721 deletions

View File

@ -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 형식)

View File

@ -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 추천 채택 → 입력창에 시간 포함하여 추가 확인
- [ ] 용어 검색 → 키워드로 조직/회의 용어 찾기 확인
- [ ] 녹음 일시정지 → 타이머 정지 확인
- [ ] 녹음 재개 → 타이머 재개 확인
---
**문서 종료**

View File

@ -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 구현 완료 후

View File

@ -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개 마이크로서비스) |
---

View 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: 서버 오류가 발생했습니다

View File

@ -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

View File

@ -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

View File

@ -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: 기본 용어 정의

View File

@ -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: 산업 표준용어 목록

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 한줄요약 추가 |