From 896589c5f4cb6b79feb2da9abef6ab89dbd98b91 Mon Sep 17 00:00:00 2001 From: Minseo-Jo Date: Tue, 21 Oct 2025 11:11:08 +0900 Subject: [PATCH] =?UTF-8?q?=EC=95=84=ED=82=A4=ED=85=8D=EC=B2=98=20?= =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=84=A4=EA=B3=84=EC=84=9C=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=B0=8F=20=EB=B0=B1=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 클라우드 아키텍처 패턴 적용 방안 재작성 - 기존 버전을 architecture-pattern_bk.md로 백업 - .claude/settings.local.json 설정 업데이트 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .claude/settings.local.json | 4 +- design/pattern/architecture-pattern.md | 2789 ++++++++++++--------- design/pattern/architecture-pattern_bk.md | 1274 ++++++++++ 3 files changed, 2943 insertions(+), 1124 deletions(-) create mode 100644 design/pattern/architecture-pattern_bk.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json index ca938e6..28d74d5 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -22,7 +22,9 @@ "Bash(git add \"design/uiux_다람지/\")", "Bash(git commit -m \"$(cat <<''EOF''\n프로토타입 개발 완료 (다람지팀)\n\n- 스타일 가이드 작성 (style-guide.md)\n - 14개 섹션으로 구성된 완전한 디자인 시스템\n - Mobile First 철학 및 접근성 기준 정의\n \n- 공통 리소스 개발\n - common.css: 700+ 라인 반응형 스타일시트\n - common.js: 400+ 라인 유틸리티 라이브러리\n \n- 9개 프로토타입 화면 개발\n - 01-로그인: 사용자 인증\n - 02-대시보드: 메인 대시보드\n - 03-회의예약: 회의 생성 폼\n - 04-템플릿선택: 회의록 템플릿 선택\n - 05-회의진행: 실시간 회의 진행\n - 06-검증완료: 섹션별 검증\n - 07-회의종료: 회의 통계\n - 08-회의록공유: 공유 설정\n - 09-Todo관리: Todo 목록 및 진행 관리\n \n- 주요 특징\n - Mobile First 반응형 디자인\n - WCAG 2.1 Level AA 접근성 준수\n - 실제 동작하는 인터랙션 구현\n - 일관된 예제 데이터 활용\n - Playwright 브라우저 테스트 완료\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude \nEOF\n)\")", "Bash(git commit -m \"UI/UX 프로토타입 디렉토리 정리\n\n- 기존 프로토타입 파일 삭제\n- 백업 디렉토리(uiux_bk) 추가\n- 프로젝트 구조 정리\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude \")", - "Bash(curl -s https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/architecture-patterns.md -o claude/architecture-patterns.md)" + "Bash(curl -s https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/guides/design/architecture-patterns.md -o claude/architecture-patterns.md)", + "Bash(cp:*)", + "Bash(git commit -m \"$(cat <<''EOF''\n아키텍처 패턴 설계서 업데이트 및 백업\n\n- 클라우드 아키텍처 패턴 적용 방안 재작성\n- 기존 버전을 architecture-pattern_bk.md로 백업\n- .claude/settings.local.json 설정 업데이트\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude \nEOF\n)\")" ], "deny": [], "ask": [] diff --git a/design/pattern/architecture-pattern.md b/design/pattern/architecture-pattern.md index c34c2ca..2884d2d 100644 --- a/design/pattern/architecture-pattern.md +++ b/design/pattern/architecture-pattern.md @@ -1,1274 +1,1817 @@ # 클라우드 아키텍처 패턴 적용 방안 -## 문서 정보 -- **작성일**: 2025-10-21 -- **작성자**: 아키텍트 팀 -- **버전**: 1.0 -- **프로젝트**: 회의록 작성 및 공유 개선 서비스 +## 1. 문서 개요 + +### 1.1 목적 +회의록 작성 및 공유 개선 서비스의 마이크로서비스 아키텍처에 적용할 클라우드 디자인 패턴을 정의합니다. + +### 1.2 범위 +본 문서는 다음 6개의 핵심 클라우드 디자인 패턴의 적용 방안을 다룹니다: +- API Gateway +- Queue-Based Load Leveling +- Cache-Aside +- Publisher-Subscriber +- Asynchronous Request-Reply +- Health Endpoint Monitoring + +### 1.3 참조 문서 +- [유저스토리](../userstory.md) +- [클라우드 디자인 패턴 요약표](../../claude/cloud-design-patterns.md) +- [논리 아키텍처](../backend/logical/) --- -## 1. 요구사항 분석 결과 +## 2. 적용 패턴 개요 -### 1.1 기능적 요구사항 +### 2.1 패턴 분류 -#### 핵심 기능 -1. **실시간 회의록 작성 및 협업** - - 여러 참석자의 동시 수정 및 실시간 동기화 - - 충돌 감지 및 해결 (Last Write Wins) - - 버전 관리 및 변경 이력 추적 - - WebSocket 기반 3-5초 간격 업데이트 +| 카테고리 | 패턴 | 적용 우선순위 | 주요 목적 | +|---------|------|--------------|----------| +| Design & Implementation | API Gateway | 높음 | 단일 진입점, 라우팅, 인증 | +| Messaging | Queue-Based Load Leveling | 높음 | 부하 분산, 비동기 처리 | +| Data Management | Cache-Aside | 높음 | 성능 최적화, DB 부하 감소 | +| Messaging | Publisher-Subscriber | 중간 | 이벤트 기반 통신 | +| Messaging | Asynchronous Request-Reply | 중간 | 장시간 작업 비동기 처리 | +| Management & Monitoring | Health Endpoint Monitoring | 높음 | 서비스 상태 모니터링 | -2. **AI 기반 회의록 자동 생성** - - STT 음성-텍스트 변환 (1초 이내 지연) - - LLM 기반 구조화된 회의록 작성 - - Todo 자동 추출 및 담당자 식별 - - 프롬프팅 기반 다양한 형식 변환 +### 2.2 서비스별 패턴 적용 매트릭스 -3. **맥락 기반 용어 설명 (RAG)** - - 전문용어 자동 감지 및 하이라이트 - - 과거 회의록/사내 문서 기반 실용적 설명 - - 벡터 유사도 검색 및 관련 문서 연결 - -4. **Todo 실시간 연동** - - AI 추출 Todo 자동 할당 - - 회의록과 양방향 실시간 연결 - - Todo 완료 시 회의록 자동 반영 - - 캘린더 자동 등록 및 리마인더 - -#### 서비스 간 연계 -- **Meeting ↔ STT**: 음성 녹음 → 텍스트 변환 -- **STT ↔ AI**: 텍스트 → 회의록 자동 생성 -- **AI ↔ RAG**: 용어 감지 → 맥락 기반 설명 -- **Meeting ↔ Todo**: 회의록 확정 → Todo 추출/할당 -- **Collaboration**: 모든 변경사항 실시간 동기화 - -### 1.2 비기능적 요구사항 - -#### 성능 요구사항 -| 지표 | 목표 | 근거 | -|------|------|------| -| STT 발언 인식 지연 | 1초 이내 | 유저스토리 UFR-STT-010 | -| STT 변환 정확도 | 60% 이상 | 유저스토리 UFR-STT-020 | -| 화자 식별 정확도 | 90% 이상 | 유저스토리 UFR-STT-010 | -| 실시간 동기화 간격 | 3-5초 | 유저스토리 UFR-AI-010 | -| AI 회의록 생성 | 실시간 업데이트 | 유저스토리 UFR-AI-010 | -| RAG 검색 응답시간 | 2초 이내 | 사용자 경험 | -| Todo 할당 알림 | 즉시 발송 | 유저스토리 UFR-TODO-010 | - -#### 가용성 및 신뢰성 -- **회의 중 서비스 중단 불가**: 99.9% uptime 목표 -- **장애 격리**: 한 서비스 장애가 전체 시스템에 영향 최소화 -- **데이터 손실 방지**: 모든 회의록 변경사항 영구 저장 - -#### 확장성 -- **동시 회의 처리**: MVP 100명 → Phase 2: 1,000명 → Phase 3: 10,000명 -- **대용량 데이터**: 장시간 회의 (3시간+) 안정적 처리 -- **글로벌 확장**: 다중 리전 배포 준비 (Phase 3) - -#### 보안 -- **사용자 인증**: LDAP 연동 SSO -- **권한 관리**: 회의록 접근 권한 제어 -- **데이터 보호**: 민감한 회의 내용 암호화 저장 - -#### 데이터 일관성 -- **Todo ↔ 회의록 동기화**: 실시간 양방향 연결 -- **버전 관리**: 모든 수정사항 이력 추적 -- **충돌 해결**: 동시 수정 시 일관성 보장 - -### 1.3 기술적 도전과제 - -#### 1. 실시간 협업 처리 -**문제**: -- 여러 참석자의 동시 수정으로 인한 충돌 -- WebSocket 연결 관리 및 메시지 전송 부하 -- 3-5초 간격 실시간 업데이트 요구 - -**영향**: -- 사용자 경험 저하 (수정 내용 손실, 충돌) -- 서버 부하 증가 (다수 WebSocket 연결) - -#### 2. 대용량 데이터 처리 -**문제**: -- 음성 녹음 파일 (장시간 회의 시 GB 단위) -- STT 변환 처리 시간 증가 -- 장시간 회의록 (수만 자 텍스트) - -**영향**: -- 응답 시간 지연 -- 저장소 비용 증가 - -#### 3. AI/LLM 통합 복잡성 -**문제**: -- LLM API 호출 시간 (수 초 ~ 수십 초) -- API 비용 (토큰 기반 과금) -- API 장애 시 서비스 중단 - -**영향**: -- 사용자 대기 시간 증가 -- 운영 비용 상승 -- 가용성 저하 - -#### 4. RAG 시스템 성능 -**문제**: -- 벡터 DB 검색 성능 (대량 회의록 축적 시) -- 관련 문서 추출 정확도 -- 맥락 기반 설명 생성 시간 - -**영향**: -- 용어 설명 응답 지연 -- 검색 정확도 저하 - -#### 5. 서비스 간 의존성 관리 -**문제**: -- Meeting → STT → AI 순차 의존성 -- Todo ↔ Meeting 양방향 의존성 -- 한 서비스 장애 시 전체 기능 마비 - -**영향**: -- 장애 전파 -- 가용성 저하 - -#### 6. 데이터 일관성 보장 -**문제**: -- Todo 완료 → 회의록 반영 시 동기화 -- 실시간 수정 충돌 해결 -- 분산 트랜잭션 관리 - -**영향**: -- 데이터 불일치 -- 사용자 혼란 +| 서비스 | API Gateway | Queue-Based | Cache-Aside | Pub-Sub | Async Request | Health Monitor | +|--------|-------------|-------------|-------------|---------|---------------|----------------| +| User Service | ✅ | - | ✅ | ✅ | - | ✅ | +| Meeting Service | ✅ | ✅ | ✅ | ✅ | - | ✅ | +| Transcript Service | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| AI Service | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Notification Service | ✅ | ✅ | - | ✅ | - | ✅ | +| Todo Service | ✅ | - | ✅ | ✅ | - | ✅ | --- -## 2. 패턴 선정 및 평가 +## 3. 패턴별 상세 적용 방안 -### 2.1 평가 기준 +### 3.1 API Gateway -| 기준 | 가중치 | 평가 내용 | -|------|--------|-----------| -| **기능 적합성** | 35% | 요구사항을 직접 해결하는 능력 | -| **성능 효과** | 25% | 응답시간 및 처리량 개선 효과 | -| **운영 복잡도** | 20% | 구현 및 운영의 용이성 (낮을수록 좋음) | -| **확장성** | 15% | 미래 요구사항에 대한 대응력 | -| **비용 효율성** | 5% | 개발/운영 비용 대비 효과 (ROI) | +#### 3.1.1 패턴 개요 +**문제**: 클라이언트가 여러 마이크로서비스와 직접 통신하면 복잡도가 증가하고 보안 관리가 어려움 -**평가 척도**: 1-10점 (10점이 가장 우수) +**해결**: 모든 클라이언트 요청을 처리하는 단일 진입점을 제공하여 라우팅, 인증, 로깅 등을 중앙화 -### 2.2 패턴 평가 매트릭스 +#### 3.1.2 적용 방안 -| 패턴 | 기능 적합성
(35%) | 성능 효과
(25%) | 운영 복잡도
(20%) | 확장성
(15%) | 비용 효율성
(5%) | **총점** | Phase | -|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:| -| **API Gateway** | 9 × 0.35 = 3.15 | 8 × 0.25 = 2.0 | 8 × 0.20 = 1.6 | 9 × 0.15 = 1.35 | 8 × 0.05 = 0.4 | **8.50** | MVP | -| **Queue-Based Load Leveling** | 9 × 0.35 = 3.15 | 9 × 0.25 = 2.25 | 7 × 0.20 = 1.4 | 8 × 0.15 = 1.2 | 7 × 0.05 = 0.35 | **8.35** | MVP | -| **Cache-Aside** | 8 × 0.35 = 2.8 | 9 × 0.25 = 2.25 | 9 × 0.20 = 1.8 | 7 × 0.15 = 1.05 | 9 × 0.05 = 0.45 | **8.35** | MVP | -| **Publisher-Subscriber** | 9 × 0.35 = 3.15 | 7 × 0.25 = 1.75 | 8 × 0.20 = 1.6 | 9 × 0.15 = 1.35 | 8 × 0.05 = 0.4 | **8.25** | MVP | -| **Asynchronous Request-Reply** | 9 × 0.35 = 3.15 | 8 × 0.25 = 2.0 | 7 × 0.20 = 1.4 | 8 × 0.15 = 1.2 | 7 × 0.05 = 0.35 | **8.10** | MVP | -| **Gateway Offloading** | 8 × 0.35 = 2.8 | 6 × 0.25 = 1.5 | 9 × 0.20 = 1.8 | 7 × 0.15 = 1.05 | 8 × 0.05 = 0.4 | **7.55** | MVP | -| **Federated Identity** | 8 × 0.35 = 2.8 | 5 × 0.25 = 1.25 | 9 × 0.20 = 1.8 | 6 × 0.15 = 0.9 | 8 × 0.05 = 0.4 | **7.15** | MVP | -| **Circuit Breaker** | 8 × 0.35 = 2.8 | 6 × 0.25 = 1.5 | 8 × 0.20 = 1.6 | 7 × 0.15 = 1.05 | 8 × 0.05 = 0.4 | **7.35** | Phase 2 | -| **CQRS** | 7 × 0.35 = 2.45 | 8 × 0.25 = 2.0 | 5 × 0.20 = 1.0 | 8 × 0.15 = 1.2 | 6 × 0.05 = 0.3 | **6.95** | Phase 2 | -| **Retry** | 7 × 0.35 = 2.45 | 6 × 0.25 = 1.5 | 8 × 0.20 = 1.6 | 6 × 0.15 = 0.9 | 8 × 0.05 = 0.4 | **6.85** | Phase 2 | -| **Materialized View** | 6 × 0.35 = 2.1 | 8 × 0.25 = 2.0 | 6 × 0.20 = 1.2 | 7 × 0.15 = 1.05 | 7 × 0.05 = 0.35 | **6.70** | Phase 2 | -| **Event Sourcing** | 7 × 0.35 = 2.45 | 7 × 0.25 = 1.75 | 4 × 0.20 = 0.8 | 9 × 0.15 = 1.35 | 5 × 0.05 = 0.25 | **6.60** | Phase 3 | -| **Saga** | 7 × 0.35 = 2.45 | 6 × 0.25 = 1.5 | 4 × 0.20 = 0.8 | 8 × 0.15 = 1.2 | 5 × 0.05 = 0.25 | **6.20** | Phase 3 | -| **Bulkhead** | 6 × 0.35 = 2.1 | 6 × 0.25 = 1.5 | 7 × 0.20 = 1.4 | 6 × 0.15 = 0.9 | 7 × 0.05 = 0.35 | **6.25** | Phase 2 | -| **Valet Key** | 5 × 0.35 = 1.75 | 7 × 0.25 = 1.75 | 8 × 0.20 = 1.6 | 5 × 0.15 = 0.75 | 8 × 0.05 = 0.4 | **6.25** | Phase 2 | -| **Priority Queue** | 5 × 0.35 = 1.75 | 6 × 0.25 = 1.5 | 7 × 0.20 = 1.4 | 6 × 0.15 = 0.9 | 6 × 0.05 = 0.3 | **5.85** | Phase 3 | -| **Health Endpoint Monitoring** | 7 × 0.35 = 2.45 | 4 × 0.25 = 1.0 | 9 × 0.20 = 1.8 | 5 × 0.15 = 0.75 | 8 × 0.05 = 0.4 | **6.40** | MVP | -| **External Configuration Store** | 6 × 0.35 = 2.1 | 4 × 0.25 = 1.0 | 8 × 0.20 = 1.6 | 6 × 0.15 = 0.9 | 8 × 0.05 = 0.4 | **6.00** | Phase 3 | +**구현 기술** +- Kong Gateway 또는 Spring Cloud Gateway +- JWT 기반 인증/인가 +- Rate Limiting 및 Throttling -### 2.3 선정 패턴 및 근거 +**주요 기능** +```yaml +API Gateway 역할: + 라우팅: + - /api/users/* → User Service + - /api/meetings/* → Meeting Service + - /api/transcripts/* → Transcript Service + - /api/ai/* → AI Service + - /api/notifications/* → Notification Service + - /api/todos/* → Todo Service -#### MVP Phase (총 8개 패턴) -1. **API Gateway (8.50점)** ⭐ 최고점 - - 단일 진입점으로 모든 클라이언트 요청 처리 - - 서비스 라우팅 중앙화 - - 높은 기능 적합성과 확장성 + 인증/인가: + - JWT 토큰 검증 + - 사용자 권한 확인 + - 회의 참여자 검증 -2. **Queue-Based Load Leveling (8.35점)** - - STT/AI 비동기 처리 필수 - - 부하 분산으로 성능 안정화 - - AI API 호출 최적화 - -3. **Cache-Aside (8.35점)** - - RAG 검색 결과 캐싱으로 성능 향상 - - 구현 용이, 높은 비용 효율성 - - 60% 검색 비용 절감 예상 - -4. **Publisher-Subscriber (8.25점)** - - Todo 상태 변경 이벤트 - - 회의록 공유 알림 - - 실시간 알림 발송 - -5. **Asynchronous Request-Reply (8.10점)** - - AI 회의록 생성 비동기 처리 - - 사용자 대기 시간 단축 - - 장시간 작업 효율적 처리 - -6. **Gateway Offloading (7.55점)** - - 인증(LDAP), 로깅, SSL 처리 - - 서비스 복잡도 감소 - - 핵심 비즈니스 로직에 집중 - -7. **Federated Identity (7.15점)** - - LDAP SSO 연동 - - 사용자 관리 효율화 - - 보안 강화 - -8. **Health Endpoint Monitoring (6.40점)** - - 서비스 상태 점검 - - 장애 조기 발견 - - 운영 안정성 - -#### Phase 2 - 확장 (총 6개 패턴 추가) -9. **Circuit Breaker (7.35점)** - - AI/STT API 장애 격리 - - 장애 복구 시간 90% 단축 - - 가용성 99.9% 달성 - -10. **CQRS (6.95점)** - - 회의록 읽기/쓰기 분리 - - 읽기 성능 70% 향상 - - 확장성 개선 - -11. **Retry (6.85점)** - - 일시적 오류 자동 복구 - - Circuit Breaker와 통합 - - 안정성 향상 - -12. **Materialized View (6.70점)** - - 회의록 목록, 요약 뷰 - - 조회 성능 80% 향상 - - 사용자 경험 개선 - -13. **Bulkhead (6.25점)** - - 서비스 격리로 장애 전파 방지 - - 리소스 풀 분리 - - 안정성 강화 - -14. **Valet Key (6.25점)** - - 음성 파일 직접 다운로드 - - 네트워크 대역폭 절감 - - Azure Storage SAS 토큰 활용 - -#### Phase 3 - 고도화 (총 4개 패턴 추가) -15. **Event Sourcing (6.60점)** - - 완벽한 변경 이력 추적 - - 감사 추적 (Audit Trail) - - 데이터 복원 가능 - -16. **Saga (6.20점)** - - Todo 할당 분산 트랜잭션 - - 데이터 일관성 보장 - - 보상 트랜잭션 - -17. **Priority Queue (5.85점)** - - VIP 회의 우선 처리 - - 중요 회의 응답시간 50% 단축 - - 비즈니스 가치 향상 - -18. **External Configuration Store (6.00점)** - - 동적 설정 관리 - - 재배포 없이 설정 변경 - - 운영 효율성 - ---- - -## 3. 서비스별 패턴 적용 설계 - -### 3.1 서비스별 패턴 매핑 - -#### API Gateway Layer -- **API Gateway**: 모든 요청 진입점, 라우팅 -- **Gateway Offloading**: LDAP 인증, 로깅, SSL 종료 -- **Rate Limiting**: 사용자당 100 req/min - -#### User Service -- **Federated Identity**: LDAP SSO 연동 -- **Cache-Aside**: 사용자 정보 캐싱 (TTL 30분) -- **Health Endpoint**: /actuator/health - -#### Meeting Service -- **CQRS**: 읽기(조회)/쓰기(작성) 모델 분리 -- **Materialized View**: 회의록 목록, 대시보드 요약 -- **Event Sourcing**: 회의록 변경 이력 (Phase 3) -- **Publisher-Subscriber**: 회의록 공유 이벤트 -- **Cache-Aside**: 회의록 목록 (TTL 5분) - -#### STT Service -- **Queue-Based Load Leveling**: 음성 변환 큐 처리 -- **Asynchronous Request-Reply**: 변환 결과 비동기 반환 -- **Valet Key**: Azure Blob Storage 음성 파일 직접 업로드 -- **Circuit Breaker**: Azure Speech API 장애 격리 -- **Retry**: STT API 일시적 오류 (최대 3회) -- **Priority Queue**: 중요 회의 우선 처리 (Phase 3) - -#### AI Service -- **Queue-Based Load Leveling**: LLM 요청 큐 (배치 처리) -- **Asynchronous Request-Reply**: AI 생성 비동기 응답 -- **Circuit Breaker**: OpenAI/Azure OpenAI API 장애 격리 -- **Retry**: LLM API 오류 재시도 (Exponential Backoff) -- **Priority Queue**: VIP 회의 우선 처리 (Phase 3) -- **Cache-Aside**: 프롬프트 템플릿 캐싱 - -#### RAG Service -- **Cache-Aside**: 벡터 검색 결과 캐싱 (TTL 1시간) -- **Materialized View**: 용어 사전 인덱스 -- **Circuit Breaker**: 벡터 DB 장애 격리 -- **Retry**: 검색 실패 재시도 - -#### Collaboration Service -- **Publisher-Subscriber**: 실시간 동기화 이벤트 (WebSocket) -- **Event Sourcing**: 수정 이력 관리 (Phase 3) -- **Bulkhead**: WebSocket 연결 풀 격리 - -#### Todo Service -- **Saga**: Todo 할당 → 알림 → 캘린더 등록 (Phase 3) -- **Publisher-Subscriber**: Todo 상태 변경 이벤트 -- **Cache-Aside**: Todo 목록 캐싱 (TTL 5분) -- **Asynchronous Request-Reply**: 캘린더 등록 비동기 - -#### Notification Service -- **Queue-Based Load Leveling**: 알림 발송 큐 -- **Competing Consumers**: 병렬 알림 처리 (이메일/푸시) -- **Retry**: 알림 발송 실패 재시도 - -#### 공통 적용 -- **Health Endpoint Monitoring**: 모든 서비스 -- **External Configuration Store**: 환경 설정 중앙 관리 (Phase 3) -- **Bulkhead**: 서비스별 리소스 풀 격리 - -### 3.2 전체 시스템 아키텍처 - -```mermaid -graph TB - subgraph "Client Layer" - Web[Web Client] - Mobile[Mobile App] - end - - subgraph "API Gateway Layer" - Gateway[API Gateway
+ Gateway Offloading
+ Rate Limiting] - end - - subgraph "Authentication" - LDAP[LDAP Server
Federated Identity] - end - - subgraph "Microservices" - User[User Service
Cache-Aside] - Meeting[Meeting Service
CQRS + Event Sourcing] - STT[STT Service
Queue + Async] - AI[AI Service
Queue + Circuit Breaker] - RAG[RAG Service
Cache-Aside] - Collab[Collaboration Service
Pub-Sub + WebSocket] - Todo[Todo Service
Saga + Pub-Sub] - Notif[Notification Service
Queue + Competing Consumers] - end - - subgraph "Data Layer" - Cache[(Redis Cache
Cache-Aside)] - UserDB[(User DB)] - MeetingDB[(Meeting DB)] - STTDB[(STT DB)] - AIDB[(AI DB)] - RAGDB[(Vector DB)] - TodoDB[(Todo DB)] - end - - subgraph "Messaging Layer" - Queue[Message Queue
RabbitMQ/Kafka
Queue-Based Load Leveling] - EventBus[Event Bus
Publisher-Subscriber] - end - - subgraph "External Services" - AzureSpeech[Azure Speech API
Circuit Breaker] - OpenAI[OpenAI API
Circuit Breaker] - AzureBlob[Azure Blob Storage
Valet Key] - Email[Email Service] - end - - Web --> Gateway - Mobile --> Gateway - - Gateway --> LDAP - Gateway --> User - Gateway --> Meeting - Gateway --> STT - Gateway --> AI - Gateway --> RAG - Gateway --> Collab - Gateway --> Todo - - User --> Cache - User --> UserDB - - Meeting --> Cache - Meeting --> MeetingDB - Meeting --> EventBus - - STT --> Queue - STT --> AzureSpeech - STT --> AzureBlob - STT --> STTDB - - AI --> Queue - AI --> OpenAI - AI --> Cache - AI --> AIDB - - RAG --> Cache - RAG --> RAGDB - - Collab --> EventBus - - Todo --> EventBus - Todo --> TodoDB - - Notif --> Queue - Notif --> Email - - Queue --> STT - Queue --> AI - Queue --> Notif - - EventBus --> Meeting - EventBus --> Collab - EventBus --> Todo - EventBus --> Notif + 부가 기능: + - Request/Response 로깅 + - Rate Limiting (사용자당 요청 제한) + - 요청/응답 변환 + - CORS 처리 ``` -### 3.3 실시간 협업 플로우 +**적용 시나리오** +``` +사용자 로그인 플로우: +1. Frontend → API Gateway: POST /api/users/login +2. API Gateway → User Service: 요청 라우팅 +3. User Service → API Gateway: JWT 토큰 반환 +4. API Gateway → Frontend: 토큰 전달 + 로깅 -```mermaid -sequenceDiagram - participant Client1 as 참석자1 - participant Client2 as 참석자2 - participant Gateway as API Gateway - participant Collab as Collaboration Service - participant EventBus as Event Bus
(Pub-Sub) - participant Meeting as Meeting Service - participant Cache as Redis Cache - - Note over Client1,Client2: WebSocket 연결 - Client1->>Gateway: WebSocket Connect - Gateway->>Collab: Establish Connection - Client2->>Gateway: WebSocket Connect - Gateway->>Collab: Establish Connection - - Note over Client1,EventBus: 회의록 수정 - Client1->>Collab: 회의록 수정 (Delta) - Collab->>Meeting: 수정 내용 저장 - Meeting->>Cache: 캐시 무효화 - Meeting->>EventBus: 수정 이벤트 발행
(Publisher) - - Note over EventBus,Client2: 실시간 동기화 - EventBus->>Collab: 수정 이벤트 전달
(Subscriber) - Collab->>Client2: WebSocket Push (Delta) - Client2->>Client2: 화면 실시간 업데이트 - - Note over Client1,Meeting: 충돌 처리 (Last Write Wins) - Client1->>Collab: 동시 수정 시도 - Client2->>Collab: 동시 수정 시도 - Collab->>Meeting: 충돌 감지 - Meeting->>Meeting: Last Write Wins 적용 - Meeting->>EventBus: 최종 수정 이벤트 - EventBus->>Collab: 동기화 - Collab->>Client1: 최종 버전 전송 - Collab->>Client2: 최종 버전 전송 +회의 생성 플로우: +1. Frontend → API Gateway: POST /api/meetings (with JWT) +2. API Gateway: JWT 검증 +3. API Gateway → Meeting Service: 요청 라우팅 +4. Meeting Service → API Gateway: 회의 정보 반환 +5. API Gateway → Frontend: 응답 전달 ``` -### 3.4 AI/STT 비동기 처리 플로우 +#### 3.1.3 구현 예시 -```mermaid -sequenceDiagram - participant Client as Web Client - participant Gateway as API Gateway - participant Meeting as Meeting Service - participant STTQueue as STT Queue
(Queue-Based) - participant STT as STT Service
(Circuit Breaker) - participant AzureSpeech as Azure Speech API - participant AIQueue as AI Queue
(Queue-Based) - participant AI as AI Service
(Circuit Breaker) - participant OpenAI as OpenAI API - participant RAG as RAG Service
(Cache-Aside) - participant Cache as Redis Cache +**Kong Gateway 설정 (YAML)** +```yaml +services: + - name: user-service + url: http://user-service:8080 + routes: + - name: user-routes + paths: + - /api/users + methods: + - GET + - POST + - PUT + - DELETE + plugins: + - name: jwt + - name: rate-limiting + config: + minute: 100 + hour: 1000 - Note over Client,Meeting: 음성 녹음 시작 - Client->>Gateway: 음성 녹음 업로드 - Gateway->>Meeting: 녹음 데이터 전달 - Meeting->>STTQueue: 변환 요청 적재
(Async Request-Reply) - Meeting-->>Client: 202 Accepted (즉시 응답) - - Note over STTQueue,AzureSpeech: STT 비동기 처리 - STTQueue->>STT: 큐에서 메시지 가져오기 - STT->>AzureSpeech: 음성-텍스트 변환 요청 - - alt API 정상 - AzureSpeech-->>STT: 변환 결과 - STT->>AIQueue: AI 회의록 작성 요청 - else API 장애 - AzureSpeech--xSTT: 오류 - STT->>STT: Circuit Breaker OPEN - STT->>STT: Retry (최대 3회) - alt Retry 성공 - STT->>AIQueue: AI 요청 - else Retry 실패 - STT->>Meeting: 실패 알림 - Meeting->>Client: WebSocket Push (오류) - end - end - - Note over AIQueue,OpenAI: AI 비동기 처리 - AIQueue->>AI: 큐에서 메시지 가져오기 - AI->>Cache: 프롬프트 템플릿 조회 - Cache-->>AI: 캐시된 템플릿 - AI->>OpenAI: 회의록 생성 요청 - - alt API 정상 - OpenAI-->>AI: 회의록 초안 - AI->>RAG: 용어 설명 요청 - RAG->>Cache: 검색 결과 조회 - - alt 캐시 적중 - Cache-->>RAG: 캐시된 설명 - else 캐시 미스 - RAG->>RAG: 벡터 검색 수행 - RAG->>Cache: 결과 캐싱 (TTL 1시간) - end - - RAG-->>AI: 용어 설명 - AI->>Meeting: 최종 회의록 전달 - Meeting->>Client: WebSocket Push (결과) - else API 장애 - OpenAI--xAI: 오류 - AI->>AI: Circuit Breaker OPEN - AI->>AI: Retry (Exponential Backoff) - end + - name: meeting-service + url: http://meeting-service:8080 + routes: + - name: meeting-routes + paths: + - /api/meetings + methods: + - GET + - POST + - PUT + - DELETE + plugins: + - name: jwt + - name: rate-limiting + config: + minute: 200 + hour: 2000 ``` -### 3.5 Todo 실시간 연동 플로우 (Saga 패턴) - -```mermaid -sequenceDiagram - participant Client as Web Client - participant Gateway as API Gateway - participant Meeting as Meeting Service - participant AI as AI Service - participant Todo as Todo Service
(Saga Orchestrator) - participant EventBus as Event Bus
(Pub-Sub) - participant Notif as Notification Service - participant Calendar as Calendar API - - Note over Client,AI: 회의록 확정 및 Todo 추출 - Client->>Gateway: 회의록 최종 확정 - Gateway->>Meeting: 확정 요청 - Meeting->>AI: Todo 자동 추출 요청 - AI->>AI: LLM 분석
(액션 아이템 추출) - AI-->>Meeting: 추출된 Todo 목록 - - Note over Meeting,Todo: Saga 시작 (Phase 3) - Meeting->>Todo: Todo 할당 요청 (Saga Start) - - Note over Todo,Calendar: Saga Step 1: Todo 등록 - Todo->>Todo: Todo 데이터 저장 - Todo->>EventBus: TodoCreated 이벤트
(Publisher) - - Note over Todo,Calendar: Saga Step 2: 알림 발송 - EventBus->>Notif: TodoCreated 수신
(Subscriber) - Notif->>Notif: 이메일 발송 - - alt 알림 성공 - Notif->>EventBus: NotificationSent 이벤트 - else 알림 실패 - Notif->>EventBus: NotificationFailed 이벤트 - EventBus->>Todo: 보상 트랜잭션
(Todo 상태 변경: 알림 실패) - end - - Note over Todo,Calendar: Saga Step 3: 캘린더 등록 - EventBus->>Todo: NotificationSent 수신 - Todo->>Calendar: 일정 등록 요청 (비동기) - - alt 캘린더 등록 성공 - Calendar-->>Todo: 성공 - Todo->>EventBus: TodoCompleted 이벤트
(Saga 완료) - else 캘린더 등록 실패 - Calendar--xTodo: 실패 - Todo->>Todo: 보상 트랜잭션
(Todo 상태: 캘린더 미등록) - Todo->>EventBus: SagaCompensated 이벤트 - end - - Note over EventBus,Meeting: 회의록 실시간 반영 - EventBus->>Meeting: Todo 이벤트 수신 - Meeting->>Meeting: 회의록에 Todo 링크 추가 - Meeting->>EventBus: MeetingUpdated 이벤트 - EventBus->>Client: WebSocket Push (Todo 상태) - Client->>Client: 화면 업데이트 - - Note over Client,Meeting: Todo 완료 처리 - Client->>Todo: Todo 완료 요청 - Todo->>Todo: 완료 시간 기록 - Todo->>EventBus: TodoCompleted 이벤트 - EventBus->>Meeting: Todo 완료 수신 - Meeting->>Meeting: 회의록에 완료 표시 - Meeting->>EventBus: MeetingUpdated 이벤트 - EventBus->>Client: WebSocket Push (완료 상태) -``` - ---- - -## 4. Phase별 구현 로드맵 - -### 4.1 Phase 1: MVP (3개월) - -#### 목표 -- 핵심 기능 구현 (회의록 작성, STT, AI, Todo) -- 안정적 서비스 출시 -- 100명 동시 사용자 지원 - -#### 적용 패턴 (8개) -1. API Gateway -2. Gateway Offloading -3. Queue-Based Load Leveling -4. Cache-Aside -5. Publisher-Subscriber -6. Asynchronous Request-Reply -7. Federated Identity -8. Health Endpoint Monitoring - -#### 마일스톤 -| 주차 | 작업 내용 | 산출물 | -|------|-----------|--------| -| 1-2주 | 인프라 구축 | API Gateway, Redis, RabbitMQ 설정 | -| 3-4주 | User/Meeting 서비스 개발 | 인증, 회의 관리 API | -| 5-6주 | STT 서비스 개발 | 음성 녹음, 변환 API | -| 7-8주 | AI 서비스 개발 | 회의록 자동 생성, Todo 추출 | -| 9-10주 | RAG/Collaboration 서비스 | 용어 설명, 실시간 동기화 | -| 11-12주 | 통합 테스트 및 배포 | 프로덕션 배포, 모니터링 | - -#### 예상 성과 -- **성능**: - - STT 변환: 1초 이내 ✓ - - AI 회의록 생성: 10초 이내 - - RAG 검색: 2초 (캐시 적중 0.1초) - - 실시간 동기화: 3-5초 간격 -- **가용성**: 99% uptime -- **동시 사용자**: 100명 -- **비용**: 월 $500 (Azure 인프라) - -### 4.2 Phase 2: 확장 (6개월, MVP 이후) - -#### 목표 -- 성능 최적화 (CQRS, Materialized View) -- 안정성 강화 (Circuit Breaker, Retry, Bulkhead) -- 1,000명 동시 사용자 지원 - -#### 추가 패턴 (6개) -9. CQRS -10. Circuit Breaker -11. Retry -12. Materialized View -13. Bulkhead -14. Valet Key - -#### 마일스톤 -| 월 | 작업 내용 | 성과 지표 | -|----|-----------|-----------| -| 1-2개월 | CQRS 적용 (Meeting 서비스) | 읽기 성능 70% 향상 | -| 3-4개월 | Circuit Breaker/Retry 적용 | 장애 복구 시간 90% 단축 | -| 5개월 | Materialized View 구현 | 목록 조회 80% 빠름 | -| 6개월 | 부하 테스트 및 최적화 | 1,000명 동시 사용자 | - -#### 예상 성과 -- **성능 개선**: - - 읽기 응답시간: 70% 단축 (CQRS) - - 목록 조회: 80% 빠름 (Materialized View) - - RAG 캐시 적중률: 80% -- **가용성**: 99.9% uptime (Circuit Breaker) -- **동시 사용자**: 1,000명 (10배 증가) -- **비용**: 월 $2,000 (Auto-scaling) - -### 4.3 Phase 3: 고도화 (12개월, Phase 2 이후) - -#### 목표 -- 대규모 확장 (10,000명 동시 사용자) -- 완벽한 감사 추적 (Event Sourcing) -- 글로벌 배포 (다중 리전) - -#### 추가 패턴 (4개) -15. Event Sourcing -16. Saga -17. Priority Queue -18. External Configuration Store - -#### 마일스톤 -| 분기 | 작업 내용 | 성과 지표 | -|------|-----------|-----------| -| Q1 | Event Sourcing 구현 | 완벽한 변경 이력 | -| Q2 | Saga 패턴 적용 (Todo) | 분산 트랜잭션 일관성 | -| Q3 | Priority Queue 도입 | VIP 회의 50% 빠름 | -| Q4 | 다중 리전 배포 | 글로벌 확장 | - -#### 예상 성과 -- **성능**: - - Priority Queue: VIP 회의 응답시간 50% 단축 - - Event Sourcing: 완벽한 감사 추적 -- **가용성**: 99.99% uptime (다중 리전) -- **동시 사용자**: 10,000명 -- **글로벌**: 3개 리전 (한국, 미국, 유럽) -- **비용**: 월 $10,000 (글로벌 인프라) - -### 4.4 비용 효율성 분석 - -| Phase | 인프라 비용 | 예상 절감 | ROI | -|-------|------------|----------|-----| -| MVP | $500/월 | - | - | -| Phase 2 | $2,000/월 | Cache-Aside: RAG 비용 60% 절감
Async: AI API 30% 절감
Auto-scaling: 유휴 자원 80% 절감 | **총 50% 절감** | -| Phase 3 | $10,000/월 | 글로벌 확장으로 사용자 10배 증가
비용은 5배만 증가 (효율성 2배) | **비용 대비 2배 효율** | - ---- - -## 5. 구현 시 고려사항 - -### 5.1 API Gateway 구현 - -**기술 스택**: Spring Cloud Gateway - -**설정**: +**Spring Cloud Gateway 설정 (application.yml)** ```yaml spring: cloud: gateway: routes: - id: user-service - uri: lb://user-service + uri: lb://USER-SERVICE predicates: - Path=/api/users/** filters: - name: RequestRateLimiter args: - redis-rate-limiter.replenishRate: 100 - redis-rate-limiter.burstCapacity: 200 + redis-rate-limiter.replenishRate: 10 + redis-rate-limiter.burstCapacity: 20 + - id: meeting-service - uri: lb://meeting-service + uri: lb://MEETING-SERVICE predicates: - Path=/api/meetings/** + filters: + - name: RequestRateLimiter + args: + redis-rate-limiter.replenishRate: 20 + redis-rate-limiter.burstCapacity: 40 ``` -**Gateway Offloading**: -- LDAP 인증: Spring Security LDAP -- 로깅: Spring Cloud Sleuth + Zipkin -- SSL 종료: Let's Encrypt 인증서 +#### 3.1.4 주의사항 +- API Gateway가 SPOF(Single Point of Failure)가 되지 않도록 HA 구성 필요 +- Gateway 자체의 성능이 병목이 되지 않도록 수평 확장 가능한 구조 유지 +- 과도한 비즈니스 로직 포함 금지 (단순 라우팅 및 공통 기능만 처리) -**Rate Limiting**: -- 사용자당: 100 req/min -- 익명 사용자: 10 req/min -- VIP: 500 req/min +--- -### 5.2 Queue 설정 +### 3.2 Queue-Based Load Leveling -**기술 스택**: RabbitMQ (MVP), Kafka (Phase 2) +#### 3.2.1 패턴 개요 +**문제**: 트래픽이 급증할 때 서비스가 과부하 상태가 되어 응답 지연 또는 실패 발생 -**Queue 구성**: +**해결**: 메시지 큐를 사용하여 요청을 버퍼링하고, 서비스가 처리 가능한 속도로 소비 + +#### 3.2.2 적용 방안 + +**구현 기술** +- RabbitMQ 또는 Apache Kafka +- Spring AMQP / Spring Kafka + +**적용 대상 서비스** ```yaml -queues: - stt: - name: stt-conversion-queue - type: priority # Priority Queue (Phase 3) - durable: true - ttl: 1800000 # 30분 - dead-letter-exchange: stt-dlx +적용 서비스: + Meeting Service: + - 대량 회의 생성 요청 + - 회의 종료 후 후처리 작업 - ai: - name: ai-generation-queue - type: priority - durable: true - ttl: 1800000 - dead-letter-exchange: ai-dlx + Transcript Service: + - 회의록 생성 요청 (STT 처리) + - 회의록 섹션별 검증 요청 - notification: - name: notification-queue - durable: true - ttl: 300000 # 5분 + AI Service: + - AI 일정 생성 요청 + - AI 요약 생성 요청 + - 다량의 AI 처리 요청 + + Notification Service: + - 알림 발송 요청 (이메일, SMS) + - 대량 알림 발송 ``` -**Dead Letter Queue**: -- 실패한 메시지 3회 재시도 후 DLQ로 이동 -- DLQ 메시지 수동 확인 및 재처리 +**적용 시나리오** +``` +회의록 생성 플로우: +1. Frontend → Meeting Service: 회의 종료 요청 +2. Meeting Service → Queue: 회의록 생성 메시지 발행 +3. Transcript Service: Queue에서 메시지 소비 (속도 제어) +4. Transcript Service: STT 처리 및 회의록 생성 +5. Transcript Service → Meeting Service: 완료 알림 -**메시지 우선순위** (Phase 3): -- VIP 회의: Priority 9 -- 일반 회의: Priority 5 -- 배치 작업: Priority 1 - -### 5.3 Cache 전략 - -**기술 스택**: Redis Cluster (고가용성) - -**TTL 설정**: -```yaml -cache: - rag-search: - ttl: 3600 # 1시간 - max-size: 10000 - - user-info: - ttl: 1800 # 30분 - max-size: 5000 - - meeting-list: - ttl: 300 # 5분 - max-size: 1000 - - todo-list: - ttl: 300 # 5분 - max-size: 2000 +AI 일정 생성 플로우: +1. Frontend → Meeting Service: AI 일정 생성 요청 +2. Meeting Service → Queue: AI 처리 메시지 발행 +3. AI Service: Queue에서 메시지 소비 (처리 속도 제어) +4. AI Service: AI 분석 및 일정 생성 +5. AI Service → Notification Service: 완료 알림 발송 ``` -**캐시 무효화**: -- 쓰기 작업 시 즉시 무효화 -- 회의록 수정 → 해당 회의 캐시 삭제 -- Todo 상태 변경 → Todo 목록 캐시 삭제 +#### 3.2.3 구현 예시 -**캐시 워밍**: -- 애플리케이션 시작 시 인기 회의록 사전 로드 -- 스케줄러: 매일 오전 6시 캐시 갱신 +**RabbitMQ 큐 정의** +```java +@Configuration +public class QueueConfig { -### 5.4 WebSocket 최적화 + // 회의록 생성 큐 + @Bean + public Queue transcriptQueue() { + return QueueBuilder.durable("transcript.creation.queue") + .withArgument("x-max-length", 1000) // 최대 메시지 수 + .withArgument("x-message-ttl", 3600000) // 1시간 TTL + .build(); + } -**기술 스택**: SockJS + STOMP + // AI 처리 큐 + @Bean + public Queue aiProcessingQueue() { + return QueueBuilder.durable("ai.processing.queue") + .withArgument("x-max-length", 500) + .withArgument("x-message-ttl", 7200000) // 2시간 TTL + .build(); + } -**설정**: -```yaml -websocket: - heartbeat: - client: 30000 # 30초 - server: 30000 - - message-size-limit: 1048576 # 1MB - - connection: - max-per-user: 5 - timeout: 300000 # 5분 + // 알림 발송 큐 + @Bean + public Queue notificationQueue() { + return QueueBuilder.durable("notification.queue") + .withArgument("x-max-length", 2000) + .withArgument("x-message-ttl", 1800000) // 30분 TTL + .build(); + } +} ``` -**재연결 정책**: -- Exponential Backoff: 1초, 2초, 4초, 8초, 16초 -- 최대 재시도: 5회 - -**메시지 전송 최적화**: -- Delta 전송: 전체 내용이 아닌 변경 부분만 -- 압축: gzip 압축 (1KB 이상 메시지) -- 배치: 100ms 내 변경사항 묶어서 전송 - -### 5.5 AI/LLM 최적화 - -**기술 스택**: Azure OpenAI Service - -**배치 처리**: -```python -# 여러 요청 묶어서 처리 -batch_requests = [] -for i in range(batch_size): - request = queue.pop() - batch_requests.append(request) - -# 한 번에 처리 -responses = openai.ChatCompletion.create_batch(batch_requests) -``` - -**스트리밍 응답**: -- 긴 회의록 생성 시 스트리밍 -- 클라이언트에 실시간 전달 (WebSocket) - -**프롬프트 캐싱**: -- 동일 템플릿 재사용 -- Redis에 프롬프트 템플릿 저장 - -**비용 최적화**: -- 토큰 제한: 요약 시 최대 500 토큰 -- 모델 선택: GPT-3.5 (일반), GPT-4 (중요 회의) - -### 5.6 Circuit Breaker 설정 - -**기술 스택**: Resilience4j - -**설정**: -```yaml -resilience4j: - circuitbreaker: - instances: - azure-speech: - failure-rate-threshold: 50 # 50% 실패 시 OPEN - wait-duration-in-open-state: 30s - sliding-window-size: 10 - minimum-number-of-calls: 5 - - openai: - failure-rate-threshold: 50 - wait-duration-in-open-state: 60s # 1분 - sliding-window-size: 20 - - retry: - instances: - azure-speech: - max-attempts: 3 - wait-duration: 1s - retry-exceptions: - - java.net.SocketTimeoutException - - org.springframework.web.client.HttpServerErrorException -``` - -**Fallback 전략**: -- STT 실패 → 수동 입력 안내 -- AI 실패 → 기본 템플릿 제공 -- RAG 실패 → 일반 용어 설명 - -### 5.7 CQRS 구현 (Phase 2) - -**Read Model** (조회): +**Producer 예시 (Meeting Service)** ```java @Service -public class MeetingQueryService { - @Cacheable("meeting-list") - public List getMeetingList(Long userId) { - // Materialized View 조회 - return materializedViewRepository.findByUserId(userId); +@RequiredArgsConstructor +public class TranscriptQueueProducer { + + private final RabbitTemplate rabbitTemplate; + + public void sendTranscriptCreationRequest(TranscriptCreationMessage message) { + rabbitTemplate.convertAndSend( + "transcript.creation.queue", + message, + msg -> { + msg.getMessageProperties().setPriority(message.getPriority()); + msg.getMessageProperties().setExpiration("3600000"); // 1시간 + return msg; + } + ); + log.info("Transcript creation request sent: meetingId={}", message.getMeetingId()); } } ``` -**Write Model** (쓰기): +**Consumer 예시 (Transcript Service)** ```java @Service -public class MeetingCommandService { - @CacheEvict(value = "meeting-list", key = "#userId") - public void updateMeeting(Long meetingId, MeetingUpdateRequest request) { - // DB 갱신 - meetingRepository.update(meetingId, request); +@RequiredArgsConstructor +public class TranscriptQueueConsumer { - // 이벤트 발행 - eventBus.publish(new MeetingUpdatedEvent(meetingId)); + private final TranscriptService transcriptService; + + @RabbitListener( + queues = "transcript.creation.queue", + concurrency = "3-10" // 동시 처리 워커 수 (최소 3, 최대 10) + ) + public void handleTranscriptCreation(TranscriptCreationMessage message) { + try { + log.info("Processing transcript creation: meetingId={}", message.getMeetingId()); + transcriptService.createTranscript(message); + log.info("Transcript creation completed: meetingId={}", message.getMeetingId()); + } catch (Exception e) { + log.error("Transcript creation failed: meetingId={}", message.getMeetingId(), e); + throw new AmqpRejectAndDontRequeueException("Failed to process transcript", e); + } } } ``` -**Materialized View 갱신**: -- 이벤트 기반 비동기 갱신 -- 배치 작업: 매 10분마다 동기화 +#### 3.2.4 주의사항 +- 메시지 큐가 가득 찰 경우의 처리 전략 정의 필요 (거부, 대기, 우선순위 기반 처리) +- Dead Letter Queue 구성으로 실패 메시지 별도 처리 +- Consumer의 동시 처리 수(concurrency) 적절히 설정하여 리소스 효율 극대화 -### 5.8 Event Sourcing 구현 (Phase 3) +--- -**Event Store**: -```java -@Entity -public class MeetingEvent { - @Id - private UUID eventId; - private Long aggregateId; // Meeting ID - private String eventType; // CREATED, UPDATED, SHARED - private String eventData; // JSON - private LocalDateTime timestamp; - private Long userId; -} +### 3.3 Cache-Aside + +#### 3.3.1 패턴 개요 +**문제**: 데이터베이스 반복 조회로 인한 성능 저하 및 DB 부하 증가 + +**해결**: 애플리케이션이 캐시를 먼저 확인하고, 캐시 미스 시 DB에서 조회 후 캐시에 저장 + +#### 3.3.2 적용 방안 + +**구현 기술** +- Redis (분산 캐시) +- Spring Cache Abstraction +- Caffeine (로컬 캐시 - 옵션) + +**적용 대상 데이터** +```yaml +캐싱 대상: + User Service: + - 사용자 프로필 정보 (TTL: 30분) + - 사용자 권한 정보 (TTL: 1시간) + + Meeting Service: + - 회의 기본 정보 (TTL: 10분) + - 회의 참여자 목록 (TTL: 10분) + - 회의 템플릿 목록 (TTL: 1시간) + + Transcript Service: + - 회의록 조회 (TTL: 30분) + - 회의록 섹션 정보 (TTL: 30분) + + Todo Service: + - 사용자별 Todo 목록 (TTL: 5분) + - Todo 진행 상태 통계 (TTL: 5분) ``` -**이벤트 재생**: +**캐시 전략** +```yaml +캐시 정책: + 읽기 집중 데이터: + - 패턴: Cache-Aside + - 전략: Lazy Loading + - 예: 사용자 프로필, 회의 정보 조회 + + 쓰기 빈도 높은 데이터: + - 패턴: Write-Through + Cache-Aside + - 전략: 업데이트 시 캐시 무효화 + - 예: Todo 상태 변경, 회의 정보 수정 + + 캐시 무효화: + - 데이터 변경 시 즉시 무효화 + - TTL 기반 자동 만료 + - 명시적 삭제 API 제공 +``` + +**적용 시나리오** +``` +사용자 프로필 조회: +1. User Service: Redis에서 사용자 프로필 조회 +2. Cache Hit → 캐시 데이터 반환 +3. Cache Miss → DB 조회 → Redis 저장 (TTL: 30분) → 데이터 반환 + +회의 정보 수정: +1. Meeting Service: 회의 정보 업데이트 (DB) +2. Meeting Service: Redis에서 해당 회의 캐시 삭제 +3. 다음 조회 시 새로운 데이터로 캐시 재생성 +``` + +#### 3.3.3 구현 예시 + +**Redis 설정 (application.yml)** +```yaml +spring: + data: + redis: + host: localhost + port: 6379 + password: ${REDIS_PASSWORD} + lettuce: + pool: + max-active: 10 + max-idle: 5 + min-idle: 2 + + cache: + type: redis + redis: + time-to-live: 1800000 # 기본 TTL: 30분 + cache-null-values: false + use-key-prefix: true +``` + +**Cache 설정** ```java -public Meeting rebuildFromEvents(Long meetingId) { - List events = eventStore.findByAggregateId(meetingId); - Meeting meeting = new Meeting(); - for (MeetingEvent event : events) { - meeting.apply(event); +@Configuration +@EnableCaching +public class CacheConfig { + + @Bean + public RedisCacheConfiguration cacheConfiguration() { + return RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(30)) + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new StringRedisSerializer() + ) + ) + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new GenericJackson2JsonRedisSerializer() + ) + ); + } + + @Bean + public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { + Map cacheConfigurations = new HashMap<>(); + + // 사용자 프로필 캐시 (TTL: 30분) + cacheConfigurations.put("userProfile", + RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(30))); + + // 회의 정보 캐시 (TTL: 10분) + cacheConfigurations.put("meetingInfo", + RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(10))); + + // Todo 목록 캐시 (TTL: 5분) + cacheConfigurations.put("todoList", + RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(5))); + + return RedisCacheManager.builder(connectionFactory) + .cacheDefaults(cacheConfiguration()) + .withInitialCacheConfigurations(cacheConfigurations) + .build(); } - return meeting; } ``` -**스냅샷** (성능 최적화): -- 100개 이벤트마다 스냅샷 생성 -- 재생 시 최신 스냅샷부터 시작 +**서비스 레이어 적용** +```java +@Service +@RequiredArgsConstructor +public class UserService { -### 5.9 모니터링 및 운영 + private final UserRepository userRepository; -**Health Endpoint**: + // Cache-Aside 패턴: 조회 + @Cacheable(value = "userProfile", key = "#userId") + public UserProfileDto getUserProfile(Long userId) { + log.info("Cache miss - Loading user profile from DB: userId={}", userId); + User user = userRepository.findById(userId) + .orElseThrow(() -> new UserNotFoundException(userId)); + return UserProfileDto.from(user); + } + + // 캐시 무효화: 업데이트 + @CacheEvict(value = "userProfile", key = "#userId") + public UserProfileDto updateUserProfile(Long userId, UpdateUserProfileRequest request) { + log.info("Updating user profile and evicting cache: userId={}", userId); + User user = userRepository.findById(userId) + .orElseThrow(() -> new UserNotFoundException(userId)); + user.updateProfile(request); + userRepository.save(user); + return UserProfileDto.from(user); + } + + // 캐시 무효화: 삭제 + @CacheEvict(value = "userProfile", key = "#userId") + public void deleteUser(Long userId) { + log.info("Deleting user and evicting cache: userId={}", userId); + userRepository.deleteById(userId); + } +} +``` + +**회의 정보 캐싱** +```java +@Service +@RequiredArgsConstructor +public class MeetingService { + + private final MeetingRepository meetingRepository; + + @Cacheable(value = "meetingInfo", key = "#meetingId") + public MeetingDto getMeeting(Long meetingId) { + log.info("Cache miss - Loading meeting from DB: meetingId={}", meetingId); + Meeting meeting = meetingRepository.findById(meetingId) + .orElseThrow(() -> new MeetingNotFoundException(meetingId)); + return MeetingDto.from(meeting); + } + + @CacheEvict(value = "meetingInfo", key = "#meetingId") + public MeetingDto updateMeeting(Long meetingId, UpdateMeetingRequest request) { + log.info("Updating meeting and evicting cache: meetingId={}", meetingId); + Meeting meeting = meetingRepository.findById(meetingId) + .orElseThrow(() -> new MeetingNotFoundException(meetingId)); + meeting.update(request); + meetingRepository.save(meeting); + return MeetingDto.from(meeting); + } +} +``` + +#### 3.3.4 주의사항 +- 캐시와 DB 간 데이터 정합성 유지 전략 필요 +- 캐시 크기 제한 및 메모리 관리 (Eviction Policy 설정) +- Hot Key 문제 대응 (특정 키에 대한 과도한 접근) +- Cache Stampede 방지 (동시 다발적 Cache Miss 시 DB 부하) + +--- + +### 3.4 Publisher-Subscriber (Pub-Sub) + +#### 3.4.1 패턴 개요 +**문제**: 서비스 간 강한 결합으로 인한 확장성 저하 및 이벤트 기반 통신의 어려움 + +**해결**: 메시지 브로커를 통해 이벤트를 발행하고, 관심 있는 서비스가 구독하여 처리 + +#### 3.4.2 적용 방안 + +**구현 기술** +- RabbitMQ (Exchange/Topic 기반) 또는 Apache Kafka (Topic 기반) +- Spring Cloud Stream + +**적용 이벤트** +```yaml +도메인 이벤트: + User Events: + - UserCreated: 사용자 생성 이벤트 + - UserUpdated: 사용자 정보 변경 이벤트 + - UserDeleted: 사용자 삭제 이벤트 + + Meeting Events: + - MeetingCreated: 회의 생성 이벤트 + - MeetingStarted: 회의 시작 이벤트 + - MeetingEnded: 회의 종료 이벤트 + - MeetingCancelled: 회의 취소 이벤트 + + Transcript Events: + - TranscriptCreated: 회의록 생성 완료 이벤트 + - TranscriptVerified: 회의록 검증 완료 이벤트 + - TranscriptShared: 회의록 공유 이벤트 + + Todo Events: + - TodoCreated: Todo 생성 이벤트 + - TodoStatusChanged: Todo 상태 변경 이벤트 + - TodoCompleted: Todo 완료 이벤트 +``` + +**구독자 매트릭스** +```yaml +이벤트 구독: + MeetingCreated: + - Notification Service: 참여자에게 알림 발송 + - AI Service: 회의 일정 분석 준비 + + MeetingEnded: + - Transcript Service: 회의록 생성 시작 + - Notification Service: 회의 종료 알림 + - Todo Service: 회의 기반 Todo 추출 + + TranscriptCreated: + - Notification Service: 회의록 생성 완료 알림 + - Meeting Service: 회의 상태 업데이트 + - AI Service: AI 분석 시작 + + TodoCreated: + - Notification Service: 담당자에게 알림 + + TodoCompleted: + - Notification Service: 완료 알림 + - Meeting Service: 회의 진행률 업데이트 +``` + +**적용 시나리오** +``` +회의 종료 후 플로우: +1. Meeting Service: MeetingEnded 이벤트 발행 +2. Transcript Service: 이벤트 구독 → 회의록 생성 시작 +3. Notification Service: 이벤트 구독 → 참여자에게 종료 알림 +4. Todo Service: 이벤트 구독 → 회의 내용에서 Todo 추출 +5. AI Service: 이벤트 구독 → AI 분석 준비 + +회의록 생성 완료 플로우: +1. Transcript Service: TranscriptCreated 이벤트 발행 +2. Notification Service: 이벤트 구독 → 회의록 생성 완료 알림 +3. Meeting Service: 이벤트 구독 → 회의 상태 '회의록 생성 완료'로 업데이트 +4. AI Service: 이벤트 구독 → 회의록 AI 분석 시작 +``` + +#### 3.4.3 구현 예시 + +**RabbitMQ Exchange/Queue 설정** +```java +@Configuration +public class RabbitMQConfig { + + // Topic Exchange 정의 + @Bean + public TopicExchange meetingExchange() { + return new TopicExchange("meeting.events"); + } + + @Bean + public TopicExchange transcriptExchange() { + return new TopicExchange("transcript.events"); + } + + @Bean + public TopicExchange todoExchange() { + return new TopicExchange("todo.events"); + } + + // Notification Service용 Queue + @Bean + public Queue notificationMeetingQueue() { + return new Queue("notification.meeting.queue", true); + } + + @Bean + public Queue notificationTranscriptQueue() { + return new Queue("notification.transcript.queue", true); + } + + // Transcript Service용 Queue + @Bean + public Queue transcriptMeetingQueue() { + return new Queue("transcript.meeting.queue", true); + } + + // Todo Service용 Queue + @Bean + public Queue todoMeetingQueue() { + return new Queue("todo.meeting.queue", true); + } + + // AI Service용 Queue + @Bean + public Queue aiTranscriptQueue() { + return new Queue("ai.transcript.queue", true); + } + + // Binding 설정 + @Bean + public Binding bindingNotificationMeeting() { + return BindingBuilder + .bind(notificationMeetingQueue()) + .to(meetingExchange()) + .with("meeting.*"); // meeting.created, meeting.ended 등 모든 이벤트 + } + + @Bean + public Binding bindingTranscriptMeeting() { + return BindingBuilder + .bind(transcriptMeetingQueue()) + .to(meetingExchange()) + .with("meeting.ended"); // 회의 종료 이벤트만 구독 + } + + @Bean + public Binding bindingTodoMeeting() { + return BindingBuilder + .bind(todoMeetingQueue()) + .to(meetingExchange()) + .with("meeting.ended"); // 회의 종료 이벤트만 구독 + } + + @Bean + public Binding bindingAiTranscript() { + return BindingBuilder + .bind(aiTranscriptQueue()) + .to(transcriptExchange()) + .with("transcript.created"); // 회의록 생성 이벤트만 구독 + } +} +``` + +**Publisher 예시 (Meeting Service)** +```java +@Service +@RequiredArgsConstructor +public class MeetingEventPublisher { + + private final RabbitTemplate rabbitTemplate; + + public void publishMeetingEnded(MeetingEndedEvent event) { + rabbitTemplate.convertAndSend( + "meeting.events", + "meeting.ended", + event, + message -> { + message.getMessageProperties().setContentType("application/json"); + message.getMessageProperties().setTimestamp(new Date()); + return message; + } + ); + log.info("Published MeetingEnded event: meetingId={}", event.getMeetingId()); + } + + public void publishMeetingCreated(MeetingCreatedEvent event) { + rabbitTemplate.convertAndSend( + "meeting.events", + "meeting.created", + event + ); + log.info("Published MeetingCreated event: meetingId={}", event.getMeetingId()); + } +} + +// 이벤트 모델 +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class MeetingEndedEvent { + private Long meetingId; + private String meetingTitle; + private LocalDateTime startTime; + private LocalDateTime endTime; + private List participantIds; + private String audioFileUrl; + private LocalDateTime occurredAt; +} +``` + +**Subscriber 예시 (Transcript Service)** +```java +@Service +@RequiredArgsConstructor +public class TranscriptEventSubscriber { + + private final TranscriptService transcriptService; + + @RabbitListener(queues = "transcript.meeting.queue") + public void handleMeetingEnded(MeetingEndedEvent event) { + try { + log.info("Received MeetingEnded event: meetingId={}", event.getMeetingId()); + + // 회의록 생성 시작 + transcriptService.createTranscript( + event.getMeetingId(), + event.getAudioFileUrl(), + event.getParticipantIds() + ); + + log.info("Transcript creation started: meetingId={}", event.getMeetingId()); + } catch (Exception e) { + log.error("Failed to handle MeetingEnded event: meetingId={}", + event.getMeetingId(), e); + throw new AmqpRejectAndDontRequeueException("Failed to process event", e); + } + } +} +``` + +**Subscriber 예시 (Notification Service)** +```java +@Service +@RequiredArgsConstructor +public class NotificationEventSubscriber { + + private final NotificationService notificationService; + + @RabbitListener(queues = "notification.meeting.queue") + public void handleMeetingEvents(Message message) { + String routingKey = message.getMessageProperties().getReceivedRoutingKey(); + + switch (routingKey) { + case "meeting.created": + MeetingCreatedEvent createdEvent = + (MeetingCreatedEvent) message.getBody(); + handleMeetingCreated(createdEvent); + break; + + case "meeting.ended": + MeetingEndedEvent endedEvent = + (MeetingEndedEvent) message.getBody(); + handleMeetingEnded(endedEvent); + break; + + default: + log.warn("Unknown routing key: {}", routingKey); + } + } + + private void handleMeetingCreated(MeetingCreatedEvent event) { + log.info("Sending meeting creation notification: meetingId={}", + event.getMeetingId()); + notificationService.notifyMeetingCreated( + event.getParticipantIds(), + event.getMeetingTitle(), + event.getScheduledTime() + ); + } + + private void handleMeetingEnded(MeetingEndedEvent event) { + log.info("Sending meeting end notification: meetingId={}", + event.getMeetingId()); + notificationService.notifyMeetingEnded( + event.getParticipantIds(), + event.getMeetingTitle() + ); + } +} +``` + +#### 3.4.4 주의사항 +- 이벤트 순서 보장이 필요한 경우 Kafka Partition Key 활용 +- Idempotent Consumer 패턴으로 중복 처리 방지 +- 이벤트 스키마 버전 관리 및 하위 호환성 유지 +- 장애 시 이벤트 유실 방지를 위한 Persistent 설정 + +--- + +### 3.5 Asynchronous Request-Reply + +#### 3.5.1 패턴 개요 +**문제**: 장시간 실행되는 작업을 동기 방식으로 처리하면 클라이언트 대기 시간이 길어지고 연결이 끊길 수 있음 + +**해결**: 요청을 비동기로 처리하고, 클라이언트가 상태를 폴링하거나 콜백으로 결과를 받음 + +#### 3.5.2 적용 방안 + +**구현 기술** +- RabbitMQ (Reply-To Queue) +- Redis (상태 저장) +- WebSocket (실시간 알림 - 옵션) + +**적용 대상 작업** +```yaml +장시간 작업: + AI Service: + - AI 일정 생성 (예상 시간: 30초 ~ 2분) + - AI 회의록 요약 생성 (예상 시간: 1분 ~ 5분) + - 대량 회의 분석 (예상 시간: 5분 ~ 30분) + + Transcript Service: + - 음성 파일 STT 변환 (예상 시간: 1분 ~ 10분) + - 대용량 회의록 검증 (예상 시간: 30초 ~ 3분) +``` + +**처리 플로우** +```yaml +비동기 요청-응답 플로우: + 1. 요청 단계: + - 클라이언트 → API Gateway → AI Service: AI 일정 생성 요청 + - AI Service: 작업 ID 생성 및 Redis에 상태 저장 (status: PENDING) + - AI Service → 클라이언트: 작업 ID 즉시 반환 (202 Accepted) + + 2. 처리 단계: + - AI Service: Queue에 작업 메시지 발행 + - AI Worker: Queue에서 메시지 소비 및 처리 시작 + - AI Worker: Redis 상태 업데이트 (status: PROCESSING) + - AI Worker: AI 처리 완료 + - AI Worker: Redis 상태 업데이트 (status: COMPLETED, result: {...}) + + 3. 결과 조회: + 방법 1 - 폴링: + - 클라이언트 → API Gateway → AI Service: GET /api/ai/tasks/{taskId} + - AI Service: Redis에서 상태 조회 및 반환 + + 방법 2 - WebSocket (옵션): + - AI Worker: 완료 시 WebSocket으로 클라이언트에게 Push +``` + +**적용 시나리오** +``` +AI 일정 생성 요청: +1. Frontend → AI Service: POST /api/ai/schedules + Body: { meetingId: 123, transcriptId: 456 } + +2. AI Service: + - taskId 생성: "ai-schedule-uuid-12345" + - Redis 저장: { taskId, status: "PENDING", createdAt: "2025-01-20T10:00:00Z" } + - Queue 발행: { taskId, meetingId, transcriptId } + +3. AI Service → Frontend: + Response: 202 Accepted + Body: { + taskId: "ai-schedule-uuid-12345", + status: "PENDING", + statusUrl: "/api/ai/tasks/ai-schedule-uuid-12345" + } + +4. AI Worker: + - Queue에서 메시지 수신 + - Redis 업데이트: { taskId, status: "PROCESSING", startedAt: "2025-01-20T10:00:05Z" } + - AI 일정 생성 처리 (1분 소요) + - Redis 업데이트: { + taskId, + status: "COMPLETED", + completedAt: "2025-01-20T10:01:05Z", + result: { scheduleId: 789, schedules: [...] } + } + +5. Frontend (폴링): + - 5초마다 GET /api/ai/tasks/ai-schedule-uuid-12345 호출 + - status가 "COMPLETED"일 때 result 획득 +``` + +#### 3.5.3 구현 예시 + +**작업 상태 모델** +```java +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class AsyncTaskStatus { + private String taskId; + private TaskStatus status; // PENDING, PROCESSING, COMPLETED, FAILED + private LocalDateTime createdAt; + private LocalDateTime startedAt; + private LocalDateTime completedAt; + private Object result; // 완료 시 결과 + private String errorMessage; // 실패 시 오류 메시지 + + public enum TaskStatus { + PENDING, + PROCESSING, + COMPLETED, + FAILED + } +} +``` + +**AI Service - 요청 접수 및 작업 ID 반환** +```java +@RestController +@RequestMapping("/api/ai") +@RequiredArgsConstructor +public class AiScheduleController { + + private final AiScheduleService aiScheduleService; + + @PostMapping("/schedules") + public ResponseEntity createSchedule( + @RequestBody @Valid CreateScheduleRequest request) { + + String taskId = aiScheduleService.requestScheduleCreation(request); + + AsyncTaskResponse response = AsyncTaskResponse.builder() + .taskId(taskId) + .status(TaskStatus.PENDING) + .statusUrl("/api/ai/tasks/" + taskId) + .build(); + + return ResponseEntity.accepted() + .location(URI.create("/api/ai/tasks/" + taskId)) + .body(response); + } + + @GetMapping("/tasks/{taskId}") + public ResponseEntity getTaskStatus(@PathVariable String taskId) { + AsyncTaskStatus status = aiScheduleService.getTaskStatus(taskId); + + if (status == null) { + return ResponseEntity.notFound().build(); + } + + if (status.getStatus() == TaskStatus.COMPLETED) { + return ResponseEntity.ok(status); + } else if (status.getStatus() == TaskStatus.FAILED) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(status); + } else { + // PENDING or PROCESSING + return ResponseEntity.status(HttpStatus.ACCEPTED).body(status); + } + } +} +``` + +**AI Service - 작업 요청 처리** +```java +@Service +@RequiredArgsConstructor +public class AiScheduleService { + + private final RedisTemplate redisTemplate; + private final RabbitTemplate rabbitTemplate; + + public String requestScheduleCreation(CreateScheduleRequest request) { + // 작업 ID 생성 + String taskId = "ai-schedule-" + UUID.randomUUID().toString(); + + // Redis에 초기 상태 저장 + AsyncTaskStatus taskStatus = new AsyncTaskStatus( + taskId, + TaskStatus.PENDING, + LocalDateTime.now(), + null, + null, + null, + null + ); + + redisTemplate.opsForValue().set( + "task:" + taskId, + taskStatus, + Duration.ofHours(24) // TTL: 24시간 + ); + + // Queue에 작업 메시지 발행 + AiScheduleMessage message = new AiScheduleMessage( + taskId, + request.getMeetingId(), + request.getTranscriptId() + ); + + rabbitTemplate.convertAndSend("ai.processing.queue", message); + + log.info("AI schedule creation requested: taskId={}, meetingId={}", + taskId, request.getMeetingId()); + + return taskId; + } + + public AsyncTaskStatus getTaskStatus(String taskId) { + return redisTemplate.opsForValue().get("task:" + taskId); + } +} +``` + +**AI Worker - 비동기 작업 처리** +```java +@Service +@RequiredArgsConstructor +public class AiScheduleWorker { + + private final RedisTemplate redisTemplate; + private final AiScheduleGenerator aiScheduleGenerator; + + @RabbitListener(queues = "ai.processing.queue", concurrency = "2-5") + public void processScheduleCreation(AiScheduleMessage message) { + String taskId = message.getTaskId(); + + try { + // 상태 업데이트: PROCESSING + updateTaskStatus(taskId, TaskStatus.PROCESSING, null, null); + + log.info("AI schedule processing started: taskId={}, meetingId={}", + taskId, message.getMeetingId()); + + // AI 일정 생성 처리 (장시간 소요) + AiScheduleResult result = aiScheduleGenerator.generateSchedule( + message.getMeetingId(), + message.getTranscriptId() + ); + + log.info("AI schedule processing completed: taskId={}", taskId); + + // 상태 업데이트: COMPLETED + updateTaskStatus(taskId, TaskStatus.COMPLETED, result, null); + + } catch (Exception e) { + log.error("AI schedule processing failed: taskId={}", taskId, e); + + // 상태 업데이트: FAILED + updateTaskStatus(taskId, TaskStatus.FAILED, null, e.getMessage()); + + throw new AmqpRejectAndDontRequeueException("Failed to process AI schedule", e); + } + } + + private void updateTaskStatus(String taskId, TaskStatus status, + Object result, String errorMessage) { + AsyncTaskStatus currentStatus = redisTemplate.opsForValue().get("task:" + taskId); + + if (currentStatus != null) { + AsyncTaskStatus updatedStatus = new AsyncTaskStatus( + taskId, + status, + currentStatus.getCreatedAt(), + status == TaskStatus.PROCESSING ? LocalDateTime.now() : currentStatus.getStartedAt(), + status == TaskStatus.COMPLETED || status == TaskStatus.FAILED ? + LocalDateTime.now() : null, + result, + errorMessage + ); + + redisTemplate.opsForValue().set( + "task:" + taskId, + updatedStatus, + Duration.ofHours(24) + ); + } + } +} +``` + +**프론트엔드 - 폴링 방식 구현** +```javascript +// AI 일정 생성 요청 +async function requestAiSchedule(meetingId, transcriptId) { + const response = await fetch('/api/ai/schedules', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ meetingId, transcriptId }) + }); + + if (response.status === 202) { + const { taskId, statusUrl } = await response.json(); + + // 폴링 시작 + pollTaskStatus(statusUrl); + } +} + +// 작업 상태 폴링 +async function pollTaskStatus(statusUrl) { + const maxAttempts = 60; // 최대 5분 (5초 * 60) + let attempts = 0; + + const intervalId = setInterval(async () => { + attempts++; + + try { + const response = await fetch(statusUrl); + const taskStatus = await response.json(); + + if (taskStatus.status === 'COMPLETED') { + clearInterval(intervalId); + console.log('AI schedule completed:', taskStatus.result); + displayScheduleResult(taskStatus.result); + } else if (taskStatus.status === 'FAILED') { + clearInterval(intervalId); + console.error('AI schedule failed:', taskStatus.errorMessage); + displayError(taskStatus.errorMessage); + } else if (attempts >= maxAttempts) { + clearInterval(intervalId); + console.warn('Polling timeout'); + displayTimeout(); + } else { + console.log('AI schedule processing... status:', taskStatus.status); + updateProgress(taskStatus.status); + } + } catch (error) { + console.error('Polling error:', error); + } + }, 5000); // 5초마다 폴링 +} +``` + +#### 3.5.4 주의사항 +- 작업 ID는 충돌하지 않도록 UUID 사용 +- Redis TTL 설정으로 완료된 작업 상태 자동 삭제 +- 폴링 간격과 최대 시도 횟수 적절히 설정 (네트워크 부하 고려) +- 장기 실행 작업의 경우 WebSocket 또는 Server-Sent Events (SSE) 고려 + +--- + +### 3.6 Health Endpoint Monitoring + +#### 3.6.1 패턴 개요 +**문제**: 서비스의 상태를 외부에서 확인할 방법이 없어 장애 발생 시 빠른 대응이 어려움 + +**해결**: 각 서비스가 Health Check 엔드포인트를 제공하여 상태를 모니터링하고, 장애 시 자동 복구 + +#### 3.6.2 적용 방안 + +**구현 기술** +- Spring Boot Actuator +- Kubernetes Liveness/Readiness Probes +- Prometheus + Grafana (메트릭 수집 및 대시보드) + +**Health Check 레벨** +```yaml +Health Check 계층: + 1. Liveness Probe (생존 확인): + - 목적: 서비스가 살아있는지 확인 + - 실패 시: 컨테이너 재시작 + - 엔드포인트: /actuator/health/liveness + - 체크 항목: 애플리케이션 기본 동작 여부 + + 2. Readiness Probe (준비 상태 확인): + - 목적: 서비스가 트래픽을 받을 준비가 되었는지 확인 + - 실패 시: 트래픽 라우팅 중단 (재시작 X) + - 엔드포인트: /actuator/health/readiness + - 체크 항목: + - 데이터베이스 연결 + - Redis 연결 + - RabbitMQ 연결 + - 외부 API 연결 + + 3. Custom Health Indicator: + - 서비스별 비즈니스 로직 상태 확인 + - 예: AI Service → AI 모델 로딩 상태 + - 예: Transcript Service → STT 엔진 연결 상태 +``` + +**서비스별 Health Check 구성** +```yaml +모든 서비스 공통: + Liveness: + - /actuator/health/liveness + - initialDelaySeconds: 30 + - periodSeconds: 10 + - timeoutSeconds: 5 + - failureThreshold: 3 + + Readiness: + - /actuator/health/readiness + - initialDelaySeconds: 10 + - periodSeconds: 5 + - timeoutSeconds: 3 + - failureThreshold: 3 + +User Service: + Dependencies: + - PostgreSQL (user_db) + - Redis (cache) + +Meeting Service: + Dependencies: + - PostgreSQL (meeting_db) + - Redis (cache) + - RabbitMQ + +Transcript Service: + Dependencies: + - PostgreSQL (transcript_db) + - Redis (cache) + - RabbitMQ + - STT Engine (외부 API) + +AI Service: + Dependencies: + - PostgreSQL (ai_db) + - Redis (cache) + - RabbitMQ + - AI Model Server (외부 API) + +Notification Service: + Dependencies: + - PostgreSQL (notification_db) + - RabbitMQ + - Email Service (SMTP) + - SMS Service (외부 API) + +Todo Service: + Dependencies: + - PostgreSQL (todo_db) + - Redis (cache) + - RabbitMQ +``` + +#### 3.6.3 구현 예시 + +**Spring Boot Actuator 설정 (application.yml)** ```yaml management: endpoints: web: exposure: - include: health, metrics, prometheus - health: - circuitbreakers: + include: health,info,metrics,prometheus + base-path: /actuator + + endpoint: + health: enabled: true - redis: + show-details: always # 개발: always, 운영: when-authorized + probes: + enabled: true # Liveness/Readiness 활성화 + + health: + livenessState: + enabled: true + readinessState: enabled: true db: enabled: true + redis: + enabled: true + rabbit: + enabled: true + + metrics: + export: + prometheus: + enabled: true ``` -**Metrics** (Prometheus + Grafana): -- 서비스 응답시간 (p50, p95, p99) -- 요청 처리량 (req/s) -- 에러율 (%) -- Circuit Breaker 상태 -- 캐시 적중률 +**Custom Health Indicator (Meeting Service)** +```java +@Component +public class MeetingServiceHealthIndicator implements HealthIndicator { -**분산 추적** (Zipkin): -- 요청 전체 흐름 추적 -- 병목 구간 식별 + private final MeetingRepository meetingRepository; + private final RedisTemplate redisTemplate; + private final RabbitTemplate rabbitTemplate; -**로그 중앙화** (ELK Stack): -- Elasticsearch: 로그 저장 -- Logstash: 로그 수집 및 파싱 -- Kibana: 로그 검색 및 시각화 + @Override + public Health health() { + try { + // 데이터베이스 연결 확인 + meetingRepository.count(); -**알림** (Phase 2): -- 에러율 5% 초과 → Slack 알림 -- Circuit Breaker OPEN → 즉시 알림 -- 디스크 사용량 80% → 경고 + // Redis 연결 확인 + redisTemplate.opsForValue().get("health-check"); -### 5.10 보안 + // RabbitMQ 연결 확인 + rabbitTemplate.getConnectionFactory().createConnection().isOpen(); -**HTTPS 강제**: -- Let's Encrypt 무료 인증서 -- HTTP → HTTPS 자동 리다이렉트 + return Health.up() + .withDetail("database", "Connected") + .withDetail("redis", "Connected") + .withDetail("rabbitmq", "Connected") + .build(); -**CORS 설정**: + } catch (Exception e) { + return Health.down() + .withDetail("error", e.getMessage()) + .build(); + } + } +} +``` + +**Custom Health Indicator (AI Service)** +```java +@Component +public class AiServiceHealthIndicator implements HealthIndicator { + + private final AiModelClient aiModelClient; + private final AiRepository aiRepository; + private final RedisTemplate redisTemplate; + + @Override + public Health health() { + Health.Builder builder = new Health.Builder(); + + try { + // 데이터베이스 연결 확인 + aiRepository.count(); + builder.withDetail("database", "Connected"); + + // Redis 연결 확인 + redisTemplate.opsForValue().get("health-check"); + builder.withDetail("redis", "Connected"); + + // AI 모델 서버 연결 확인 + boolean aiModelReady = aiModelClient.checkHealth(); + if (aiModelReady) { + builder.withDetail("ai-model", "Ready"); + } else { + builder.withDetail("ai-model", "Not Ready"); + return builder.down().build(); + } + + return builder.up().build(); + + } catch (Exception e) { + return builder.down() + .withDetail("error", e.getMessage()) + .build(); + } + } +} +``` + +**Kubernetes Deployment with Probes** ```yaml -spring: - web: - cors: - allowed-origins: - - https://meeting.example.com - allowed-methods: GET, POST, PUT, DELETE - allowed-headers: "*" - allow-credentials: true +apiVersion: apps/v1 +kind: Deployment +metadata: + name: meeting-service +spec: + replicas: 3 + selector: + matchLabels: + app: meeting-service + template: + metadata: + labels: + app: meeting-service + spec: + containers: + - name: meeting-service + image: meeting-service:1.0.0 + ports: + - containerPort: 8080 + + # Liveness Probe: 컨테이너가 살아있는지 확인 + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: 8080 + initialDelaySeconds: 30 # 초기 대기 시간 + periodSeconds: 10 # 체크 간격 + timeoutSeconds: 5 # 응답 대기 시간 + failureThreshold: 3 # 실패 허용 횟수 + + # Readiness Probe: 트래픽을 받을 준비가 되었는지 확인 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + + env: + - name: SPRING_PROFILES_ACTIVE + value: "prod" + - name: SPRING_DATASOURCE_URL + valueFrom: + secretKeyRef: + name: meeting-db-secret + key: url ``` -**SQL Injection 방지**: -- JPA/Hibernate PreparedStatement 사용 -- 동적 쿼리 금지 +**Health Check 응답 예시** +```json +// /actuator/health +{ + "status": "UP", + "components": { + "db": { + "status": "UP", + "details": { + "database": "PostgreSQL", + "validationQuery": "isValid()" + } + }, + "redis": { + "status": "UP", + "details": { + "version": "7.0.5" + } + }, + "rabbit": { + "status": "UP", + "details": { + "version": "3.11.0" + } + }, + "meetingService": { + "status": "UP", + "details": { + "database": "Connected", + "redis": "Connected", + "rabbitmq": "Connected" + } + }, + "diskSpace": { + "status": "UP", + "details": { + "total": 107374182400, + "free": 53687091200, + "threshold": 10485760, + "exists": true + } + } + } +} -**XSS 방지**: -- Input Sanitization (OWASP Java Encoder) -- Output Encoding +// /actuator/health/liveness +{ + "status": "UP" +} -**민감 정보 암호화**: -- DB 암호화: AES-256 -- 전송 암호화: TLS 1.3 +// /actuator/health/readiness +{ + "status": "UP", + "components": { + "db": { "status": "UP" }, + "redis": { "status": "UP" }, + "rabbit": { "status": "UP" } + } +} +``` + +**Prometheus 메트릭 수집** +```yaml +# prometheus.yml +scrape_configs: + - job_name: 'meeting-service' + metrics_path: '/actuator/prometheus' + static_configs: + - targets: ['meeting-service:8080'] + + - job_name: 'transcript-service' + metrics_path: '/actuator/prometheus' + static_configs: + - targets: ['transcript-service:8080'] + + - job_name: 'ai-service' + metrics_path: '/actuator/prometheus' + static_configs: + - targets: ['ai-service:8080'] +``` + +#### 3.6.4 주의사항 +- Health Check 엔드포인트는 가벼워야 함 (복잡한 비즈니스 로직 포함 금지) +- Liveness와 Readiness를 명확히 구분 (잘못 설정 시 무한 재시작 발생 가능) +- initialDelaySeconds는 애플리케이션 시작 시간보다 길게 설정 +- failureThreshold를 너무 낮게 설정하면 일시적 네트워크 장애에도 재시작 발생 --- -## 6. 예상 성과 지표 +## 4. 패턴 간 통합 시나리오 -### 6.1 Phase별 성과 비교 +### 4.1 회의 종료 후 전체 플로우 -| 지표 | MVP (Phase 1) | Phase 2 (확장) | Phase 3 (고도화) | -|------|---------------|----------------|------------------| -| **성능** | | | | -| STT 변환 | 1초 이내 | 0.8초 | 0.5초 | -| AI 회의록 생성 | 10초 | 7초 (배치 처리) | 5초 (스트리밍) | -| RAG 검색 (캐시 미스) | 2초 | 1.5초 | 1초 | -| RAG 검색 (캐시 적중) | 0.1초 | 0.1초 | 0.1초 | -| 실시간 동기화 | 3-5초 | 2-3초 | 1-2초 | -| 회의록 목록 조회 | 500ms | 100ms (Materialized View) | 50ms | -| **가용성** | | | | -| Uptime | 99% | 99.9% | 99.99% | -| MTTR (평균 복구 시간) | 30분 | 3분 (Circuit Breaker) | 1분 | -| **확장성** | | | | -| 동시 사용자 | 100명 | 1,000명 | 10,000명 | -| 회의당 최대 참석자 | 10명 | 50명 | 100명 | -| 일일 회의 처리 | 500건 | 5,000건 | 50,000건 | -| **비용** | | | | -| 인프라 비용 | $500/월 | $2,000/월 | $10,000/월 | -| 사용자당 비용 | $5/월 | $2/월 (2.5배 절감) | $1/월 (5배 절감) | +``` +사용자 → Frontend → API Gateway → Meeting Service + ↓ + 1. 회의 종료 처리 + 2. MeetingEnded 이벤트 발행 (Pub-Sub) + 3. Redis 캐시 무효화 (Cache-Aside) + ↓ + ┌───────────────┼───────────────┐ + ↓ ↓ ↓ + Transcript Service Notification Todo Service + ↓ Service ↓ + Queue 발행 알림 발송 Queue 발행 + (Queue-Based) (Queue-Based) + ↓ ↓ + STT Worker Todo Worker + 비동기 처리 Todo 추출 + (Async Request-Reply) + ↓ + TranscriptCreated 이벤트 + (Pub-Sub) + ↓ + ┌────────┼────────┐ + ↓ ↓ ↓ + Meeting Notification AI Service + Service Service ↓ + ↓ ↓ Queue 발행 + 캐시 완료 알림 (Queue-Based) + 무효화 ↓ + AI Worker + (Async Request-Reply) +``` -### 6.2 패턴별 기대 효과 +**단계별 패턴 적용** -#### Cache-Aside -- **RAG 검색 비용**: 60% 절감 - - 캐시 미스: 2초 → 캐시 적중: 0.1초 - - 캐시 적중률: 80% (Phase 2) -- **API 호출 감소**: 외부 API 호출 80% 감소 -- **비용 절감**: 월 $500 → $200 (RAG API 비용) +1. **API Gateway**: 사용자 요청 라우팅 및 인증 +2. **Cache-Aside**: Meeting Service에서 회의 정보 캐시 무효화 +3. **Pub-Sub**: MeetingEnded 이벤트 발행 +4. **Queue-Based Load Leveling**: Transcript Service, Todo Service가 Queue를 통해 작업 수신 +5. **Asynchronous Request-Reply**: STT 처리 및 AI 분석은 비동기로 처리, 상태 폴링 +6. **Health Endpoint Monitoring**: 모든 서비스 상태 모니터링 -#### Queue-Based Load Leveling -- **처리량 향상**: 동시 요청 100 → 1,000 (10배) -- **응답 안정성**: 피크 시간대 응답시간 편차 50% 감소 -- **자원 효율**: CPU 사용률 평준화 (80% → 50%) +### 4.2 회의록 조회 플로우 -#### Asynchronous Request-Reply -- **사용자 대기 시간**: 10초 → 즉시 응답 (202 Accepted) -- **사용자 경험**: 응답 대기 중 다른 작업 가능 -- **AI API 최적화**: 배치 처리로 30% 비용 절감 +``` +사용자 → Frontend → API Gateway → Transcript Service + ↓ + 1. Redis 캐시 확인 (Cache-Aside) + 2. Cache Hit → 캐시 반환 + 3. Cache Miss → DB 조회 → 캐시 저장 + ↓ + 응답 반환 +``` -#### CQRS (Phase 2) -- **읽기 성능**: 응답시간 70% 단축 (500ms → 150ms) -- **쓰기 성능**: 독립적 최적화 가능 -- **확장성**: 읽기/쓰기 DB 독립 스케일링 - -#### Circuit Breaker (Phase 2) -- **장애 복구**: MTTR 30분 → 3분 (90% 단축) -- **장애 격리**: 한 서비스 장애가 전체 영향 없음 -- **가용성**: 99% → 99.9% uptime - -#### Event Sourcing (Phase 3) -- **감사 추적**: 100% 변경 이력 보존 -- **데이터 복원**: 임의 시점 상태 복원 가능 -- **규제 준수**: 완벽한 감사 로그 - -#### Priority Queue (Phase 3) -- **VIP 회의**: 응답시간 50% 단축 (10초 → 5초) -- **비즈니스 가치**: 중요 회의 우선 처리 -- **자원 활용**: 일반 회의는 유휴 시간 활용 - -### 6.3 비용 효율성 분석 - -#### Phase 1 (MVP) -- **인프라**: $500/월 - - API Gateway: $100 - - Redis: $50 - - RabbitMQ: $50 - - DB (PostgreSQL): $100 - - 컴퓨팅 (VM): $200 -- **외부 API**: $300/월 - - Azure Speech: $100 - - OpenAI: $200 -- **총 비용**: $800/월 -- **사용자**: 100명 -- **사용자당 비용**: $8/월 - -#### Phase 2 (확장) -- **인프라**: $2,000/월 (4배 증가) -- **외부 API**: $600/월 (2배 증가) - - Cache-Aside로 60% 절감 → $240 절감 - - Async 배치 처리로 30% 절감 → $60 절감 - - 실제 비용: $600 - $300 = $300 -- **총 비용**: $2,300/월 -- **사용자**: 1,000명 (10배 증가) -- **사용자당 비용**: $2.3/월 (71% 절감) - -#### Phase 3 (고도화) -- **인프라**: $10,000/월 (글로벌 배포) -- **외부 API**: $1,000/월 -- **총 비용**: $11,000/월 -- **사용자**: 10,000명 -- **사용자당 비용**: $1.1/월 (86% 절감 대비 MVP) - -**ROI 분석**: -- Phase 2: 투자 대비 2.5배 효율 (사용자 10배, 비용 4배) -- Phase 3: 투자 대비 7배 효율 (사용자 100배, 비용 14배) +**패턴 적용** +- **API Gateway**: 요청 라우팅 및 인증 +- **Cache-Aside**: 회의록 데이터 캐싱으로 성능 최적화 --- -## 7. 결론 +## 5. 패턴 적용 로드맵 -### 7.1 핵심 성과 +### 5.1 1단계: 기본 인프라 (Week 1-2) -본 아키텍처 패턴 적용 방안은 회의록 작성 및 공유 개선 서비스의 기술적 도전과제를 체계적으로 해결합니다: +**목표**: 핵심 인프라 구축 및 기본 패턴 적용 -1. **실시간 협업 처리**: Publisher-Subscriber + WebSocket으로 3-5초 실시간 동기화 달성 -2. **대용량 데이터 처리**: Queue-Based Load Leveling으로 안정적 비동기 처리 -3. **AI/LLM 통합**: Asynchronous Request-Reply + Circuit Breaker로 안정성과 사용자 경험 보장 -4. **RAG 성능**: Cache-Aside로 80% 캐시 적중률, 60% 비용 절감 -5. **서비스 격리**: Circuit Breaker + Bulkhead로 99.9% 가용성 달성 -6. **데이터 일관성**: Saga + Event Sourcing으로 완벽한 일관성 보장 +**작업 항목** +1. API Gateway 설정 + - Kong 또는 Spring Cloud Gateway 선택 및 설치 + - 라우팅 규칙 정의 + - JWT 인증 설정 -### 7.2 차별화 포인트 실현 +2. Health Endpoint Monitoring 구현 + - Spring Boot Actuator 활성화 + - Liveness/Readiness Probes 설정 + - Kubernetes Deployment 설정 -아키텍처 패턴 적용으로 서비스 차별화 포인트를 강화합니다: +3. 메시지 브로커 설치 + - RabbitMQ 설치 및 설정 + - Exchange/Queue 정의 -- **맥락 기반 용어 설명**: RAG + Cache-Aside로 2초 이내 실용적 정보 제공 -- **강화된 Todo 연결**: Saga 패턴으로 Todo ↔ 회의록 실시간 양방향 연동 -- **프롬프팅 기반 회의록 개선**: Async Request-Reply로 즉시 응답, 백그라운드 생성 +4. Redis 설치 + - Redis 클러스터 구성 + - 캐시 설정 -### 7.3 단계별 추진 전략 +**완료 기준** +- 모든 서비스가 API Gateway를 통해 접근 가능 +- Health Check 엔드포인트가 정상 동작 +- RabbitMQ와 Redis 연결 확인 -**Phase 1 (MVP)**: 핵심 패턴 8개로 안정적 출시 -- API Gateway, Queue, Cache, Pub-Sub 중심 -- 100명 사용자, 99% 가용성 목표 +### 5.2 2단계: 성능 최적화 (Week 3-4) -**Phase 2 (확장)**: 성능 최적화 및 안정성 강화 -- CQRS, Circuit Breaker, Materialized View 추가 -- 1,000명 사용자, 99.9% 가용성 목표 -- 70% 성능 향상, 90% 장애 복구 시간 단축 +**목표**: 캐싱 및 부하 분산 패턴 적용 -**Phase 3 (고도화)**: 대규모 확장 및 글로벌 배포 -- Event Sourcing, Saga, Priority Queue 적용 -- 10,000명 사용자, 99.99% 가용성 목표 -- 완벽한 감사 추적, 글로벌 3개 리전 +**작업 항목** +1. Cache-Aside 패턴 구현 + - User Service: 사용자 프로필 캐싱 + - Meeting Service: 회의 정보 캐싱 + - Todo Service: Todo 목록 캐싱 -### 7.4 비용 효율성 +2. Queue-Based Load Leveling 구현 + - Meeting Service → Transcript Service (회의록 생성) + - Meeting Service → AI Service (AI 처리) + - Notification Service (알림 발송) -- **Phase 1**: 사용자당 $8/월 -- **Phase 2**: 사용자당 $2.3/월 (71% 절감) -- **Phase 3**: 사용자당 $1.1/월 (86% 절감) +**완료 기준** +- 캐시 Hit Rate 70% 이상 +- Queue를 통한 비동기 처리 정상 동작 +- 부하 테스트 결과 성능 개선 확인 -**투자 대비 수익**: -- Phase 2: 2.5배 효율 (사용자 10배 증가, 비용 4배 증가) -- Phase 3: 7배 효율 (사용자 100배 증가, 비용 14배 증가) +### 5.3 3단계: 이벤트 기반 아키텍처 (Week 5-6) -### 7.5 리스크 및 대응 +**목표**: 서비스 간 느슨한 결합 및 확장성 향상 -| 리스크 | 영향 | 대응 패턴 | -|--------|------|-----------| -| AI/STT API 장애 | 서비스 중단 | Circuit Breaker + Retry | -| 동시 수정 충돌 | 데이터 손실 | Last Write Wins + Event Sourcing | -| 대용량 트래픽 | 응답 지연 | Queue-Based Load Leveling + Auto-scaling | -| 캐시 불일치 | 데이터 정합성 | Write-through + TTL 관리 | -| 분산 트랜잭션 실패 | 데이터 불일치 | Saga + 보상 트랜잭션 | +**작업 항목** +1. Pub-Sub 패턴 구현 + - MeetingEnded, TranscriptCreated, TodoCreated 이벤트 + - 이벤트 구독자 구현 (Notification, AI, Todo Service) -### 7.6 다음 단계 +2. Asynchronous Request-Reply 패턴 구현 + - AI Service: AI 일정 생성 비동기 처리 + - Transcript Service: STT 처리 비동기 처리 + - Redis 기반 상태 관리 -1. **MVP 개발 착수** (즉시) - - API Gateway, Queue, Cache 인프라 구축 - - User/Meeting 서비스 개발 시작 +**완료 기준** +- 이벤트 발행/구독 정상 동작 +- 비동기 작업 상태 조회 가능 +- 서비스 간 직접 의존성 제거 확인 -2. **프로토타입 검증** (1개월 내) - - 핵심 패턴 PoC 구현 - - 성능 및 안정성 테스트 +### 5.4 4단계: 모니터링 및 안정화 (Week 7-8) -3. **단계별 출시** (3-12개월) - - Phase 1: 3개월 MVP 출시 - - Phase 2: 6개월 성능 최적화 - - Phase 3: 12개월 글로벌 확장 +**목표**: 운영 안정성 확보 + +**작업 항목** +1. Prometheus + Grafana 구축 + - 메트릭 수집 설정 + - 대시보드 구성 + +2. 알림 설정 + - Health Check 실패 알림 + - Queue 적체 알림 + - 성능 저하 알림 + +3. 부하 테스트 및 튜닝 + - Queue Consumer Concurrency 조정 + - 캐시 TTL 최적화 + - API Gateway Rate Limiting 조정 + +**완료 기준** +- Grafana 대시보드에서 모든 서비스 상태 확인 가능 +- 알림 시스템 정상 동작 +- 목표 성능 달성 (응답 시간, 처리량) --- -## 부록 +## 6. 모니터링 및 운영 -### A. 참고 문서 -- [유저스토리](../userstory.md) -- [클라우드 디자인 패턴 개요](../../claude/cloud-design-patterns.md) -- [아키텍처 패턴 선정 가이드](../../claude/architecture-patterns.md) +### 6.1 주요 모니터링 지표 -### B. 기술 스택 -- **API Gateway**: Spring Cloud Gateway -- **Message Queue**: RabbitMQ (MVP), Kafka (Phase 2) -- **Cache**: Redis Cluster -- **Database**: PostgreSQL (주 DB), MongoDB (Event Store) -- **WebSocket**: SockJS + STOMP -- **Circuit Breaker**: Resilience4j -- **모니터링**: Prometheus + Grafana, Zipkin, ELK Stack -- **외부 API**: Azure Speech, Azure OpenAI, Azure Blob Storage +**API Gateway** +- 요청 수 (RPS) +- 응답 시간 (P50, P95, P99) +- 에러율 (4xx, 5xx) +- Rate Limit 초과 횟수 -### C. 용어 정의 -- **STT**: Speech-to-Text (음성-텍스트 변환) -- **LLM**: Large Language Model (대규모 언어 모델) -- **RAG**: Retrieval-Augmented Generation (검색 증강 생성) -- **CQRS**: Command Query Responsibility Segregation (명령 쿼리 책임 분리) -- **Saga**: 분산 트랜잭션 관리 패턴 -- **Event Sourcing**: 이벤트 기반 데이터 저장 패턴 -- **Circuit Breaker**: 장애 격리 패턴 -- **Pub-Sub**: Publisher-Subscriber (발행-구독) 패턴 +**Queue-Based Load Leveling** +- Queue 길이 +- 메시지 처리 속도 +- Dead Letter Queue 메시지 수 +- Consumer Lag -### D. Mermaid 다이어그램 검증 -모든 Mermaid 다이어그램은 https://mermaid.live/edit 에서 검증 완료되었습니다. +**Cache-Aside** +- Cache Hit Rate +- Cache Miss Rate +- 캐시 메모리 사용량 +- 캐시 Eviction 수 + +**Pub-Sub** +- 이벤트 발행 수 +- 이벤트 구독 지연 시간 +- 이벤트 처리 실패 수 + +**Asynchronous Request-Reply** +- 비동기 작업 대기 시간 +- 작업 완료율 +- 작업 실패율 + +**Health Endpoint Monitoring** +- 서비스 가용성 (Uptime) +- Health Check 응답 시간 +- Liveness/Readiness 실패 횟수 + +### 6.2 알림 규칙 + +| 지표 | 임계값 | 조치 | +|------|--------|------| +| API Gateway 에러율 | > 5% | 즉시 알림, 로그 분석 | +| Queue 길이 | > 1000 | Consumer 증설 검토 | +| Cache Hit Rate | < 50% | 캐시 전략 재검토 | +| Health Check 실패 | 3회 연속 | 서비스 재시작, 근본 원인 분석 | +| 비동기 작업 실패율 | > 10% | Worker 상태 점검, Queue 확인 | --- -**문서 버전**: 1.0 -**최종 수정**: 2025-10-21 -**작성자**: 아키텍트 팀 (길동) +## 7. 장애 대응 + +### 7.1 API Gateway 장애 + +**증상**: 모든 서비스 접근 불가 + +**대응** +1. API Gateway Pod 재시작 +2. HA 구성 확인 (최소 2개 이상 인스턴스) +3. 부하 분산 설정 점검 + +### 7.2 Queue 적체 + +**증상**: 메시지 처리 지연, Queue 길이 증가 + +**대응** +1. Consumer 수 증가 (Concurrency 조정) +2. Worker Pod 수평 확장 +3. 메시지 우선순위 재조정 + +### 7.3 Cache 장애 + +**증상**: 응답 시간 증가, DB 부하 증가 + +**대응** +1. Redis 연결 확인 +2. Cache Fallback 동작 확인 (DB 직접 조회) +3. Redis 재시작 또는 Failover + +### 7.4 이벤트 유실 + +**증상**: 특정 이벤트가 구독자에게 전달되지 않음 + +**대응** +1. RabbitMQ 연결 상태 확인 +2. Queue Binding 설정 점검 +3. Dead Letter Queue 확인 +4. 필요 시 수동 재발행 + +--- + +## 8. 성공 지표 + +### 8.1 성능 목표 + +| 지표 | 목표 | +|------|------| +| API 응답 시간 (P95) | < 500ms | +| 회의록 생성 시간 | < 2분 | +| AI 일정 생성 시간 | < 1분 | +| Cache Hit Rate | > 70% | +| 시스템 가용성 | > 99.5% | + +### 8.2 확장성 목표 + +| 항목 | 목표 | +|------|------| +| 동시 사용자 수 | 10,000명 | +| 동시 진행 회의 수 | 1,000개 | +| 일일 처리 회의 수 | 100,000개 | + +--- + +## 9. 참고 자료 + +- [Microsoft Azure Cloud Design Patterns](https://learn.microsoft.com/en-us/azure/architecture/patterns/) +- [Kong Gateway Documentation](https://docs.konghq.com/) +- [Spring Cloud Gateway Reference](https://spring.io/projects/spring-cloud-gateway) +- [RabbitMQ Patterns](https://www.rabbitmq.com/getstarted.html) +- [Redis Best Practices](https://redis.io/docs/management/optimization/) +- [Kubernetes Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) + +--- + +## 10. 문서 이력 + +| 버전 | 작성일 | 작성자 | 변경 내용 | +|------|--------|--------|----------| +| 1.0 | 2025-01-20 | 길동 | 초안 작성 (6개 핵심 패턴 적용 방안) | diff --git a/design/pattern/architecture-pattern_bk.md b/design/pattern/architecture-pattern_bk.md new file mode 100644 index 0000000..c34c2ca --- /dev/null +++ b/design/pattern/architecture-pattern_bk.md @@ -0,0 +1,1274 @@ +# 클라우드 아키텍처 패턴 적용 방안 + +## 문서 정보 +- **작성일**: 2025-10-21 +- **작성자**: 아키텍트 팀 +- **버전**: 1.0 +- **프로젝트**: 회의록 작성 및 공유 개선 서비스 + +--- + +## 1. 요구사항 분석 결과 + +### 1.1 기능적 요구사항 + +#### 핵심 기능 +1. **실시간 회의록 작성 및 협업** + - 여러 참석자의 동시 수정 및 실시간 동기화 + - 충돌 감지 및 해결 (Last Write Wins) + - 버전 관리 및 변경 이력 추적 + - WebSocket 기반 3-5초 간격 업데이트 + +2. **AI 기반 회의록 자동 생성** + - STT 음성-텍스트 변환 (1초 이내 지연) + - LLM 기반 구조화된 회의록 작성 + - Todo 자동 추출 및 담당자 식별 + - 프롬프팅 기반 다양한 형식 변환 + +3. **맥락 기반 용어 설명 (RAG)** + - 전문용어 자동 감지 및 하이라이트 + - 과거 회의록/사내 문서 기반 실용적 설명 + - 벡터 유사도 검색 및 관련 문서 연결 + +4. **Todo 실시간 연동** + - AI 추출 Todo 자동 할당 + - 회의록과 양방향 실시간 연결 + - Todo 완료 시 회의록 자동 반영 + - 캘린더 자동 등록 및 리마인더 + +#### 서비스 간 연계 +- **Meeting ↔ STT**: 음성 녹음 → 텍스트 변환 +- **STT ↔ AI**: 텍스트 → 회의록 자동 생성 +- **AI ↔ RAG**: 용어 감지 → 맥락 기반 설명 +- **Meeting ↔ Todo**: 회의록 확정 → Todo 추출/할당 +- **Collaboration**: 모든 변경사항 실시간 동기화 + +### 1.2 비기능적 요구사항 + +#### 성능 요구사항 +| 지표 | 목표 | 근거 | +|------|------|------| +| STT 발언 인식 지연 | 1초 이내 | 유저스토리 UFR-STT-010 | +| STT 변환 정확도 | 60% 이상 | 유저스토리 UFR-STT-020 | +| 화자 식별 정확도 | 90% 이상 | 유저스토리 UFR-STT-010 | +| 실시간 동기화 간격 | 3-5초 | 유저스토리 UFR-AI-010 | +| AI 회의록 생성 | 실시간 업데이트 | 유저스토리 UFR-AI-010 | +| RAG 검색 응답시간 | 2초 이내 | 사용자 경험 | +| Todo 할당 알림 | 즉시 발송 | 유저스토리 UFR-TODO-010 | + +#### 가용성 및 신뢰성 +- **회의 중 서비스 중단 불가**: 99.9% uptime 목표 +- **장애 격리**: 한 서비스 장애가 전체 시스템에 영향 최소화 +- **데이터 손실 방지**: 모든 회의록 변경사항 영구 저장 + +#### 확장성 +- **동시 회의 처리**: MVP 100명 → Phase 2: 1,000명 → Phase 3: 10,000명 +- **대용량 데이터**: 장시간 회의 (3시간+) 안정적 처리 +- **글로벌 확장**: 다중 리전 배포 준비 (Phase 3) + +#### 보안 +- **사용자 인증**: LDAP 연동 SSO +- **권한 관리**: 회의록 접근 권한 제어 +- **데이터 보호**: 민감한 회의 내용 암호화 저장 + +#### 데이터 일관성 +- **Todo ↔ 회의록 동기화**: 실시간 양방향 연결 +- **버전 관리**: 모든 수정사항 이력 추적 +- **충돌 해결**: 동시 수정 시 일관성 보장 + +### 1.3 기술적 도전과제 + +#### 1. 실시간 협업 처리 +**문제**: +- 여러 참석자의 동시 수정으로 인한 충돌 +- WebSocket 연결 관리 및 메시지 전송 부하 +- 3-5초 간격 실시간 업데이트 요구 + +**영향**: +- 사용자 경험 저하 (수정 내용 손실, 충돌) +- 서버 부하 증가 (다수 WebSocket 연결) + +#### 2. 대용량 데이터 처리 +**문제**: +- 음성 녹음 파일 (장시간 회의 시 GB 단위) +- STT 변환 처리 시간 증가 +- 장시간 회의록 (수만 자 텍스트) + +**영향**: +- 응답 시간 지연 +- 저장소 비용 증가 + +#### 3. AI/LLM 통합 복잡성 +**문제**: +- LLM API 호출 시간 (수 초 ~ 수십 초) +- API 비용 (토큰 기반 과금) +- API 장애 시 서비스 중단 + +**영향**: +- 사용자 대기 시간 증가 +- 운영 비용 상승 +- 가용성 저하 + +#### 4. RAG 시스템 성능 +**문제**: +- 벡터 DB 검색 성능 (대량 회의록 축적 시) +- 관련 문서 추출 정확도 +- 맥락 기반 설명 생성 시간 + +**영향**: +- 용어 설명 응답 지연 +- 검색 정확도 저하 + +#### 5. 서비스 간 의존성 관리 +**문제**: +- Meeting → STT → AI 순차 의존성 +- Todo ↔ Meeting 양방향 의존성 +- 한 서비스 장애 시 전체 기능 마비 + +**영향**: +- 장애 전파 +- 가용성 저하 + +#### 6. 데이터 일관성 보장 +**문제**: +- Todo 완료 → 회의록 반영 시 동기화 +- 실시간 수정 충돌 해결 +- 분산 트랜잭션 관리 + +**영향**: +- 데이터 불일치 +- 사용자 혼란 + +--- + +## 2. 패턴 선정 및 평가 + +### 2.1 평가 기준 + +| 기준 | 가중치 | 평가 내용 | +|------|--------|-----------| +| **기능 적합성** | 35% | 요구사항을 직접 해결하는 능력 | +| **성능 효과** | 25% | 응답시간 및 처리량 개선 효과 | +| **운영 복잡도** | 20% | 구현 및 운영의 용이성 (낮을수록 좋음) | +| **확장성** | 15% | 미래 요구사항에 대한 대응력 | +| **비용 효율성** | 5% | 개발/운영 비용 대비 효과 (ROI) | + +**평가 척도**: 1-10점 (10점이 가장 우수) + +### 2.2 패턴 평가 매트릭스 + +| 패턴 | 기능 적합성
(35%) | 성능 효과
(25%) | 운영 복잡도
(20%) | 확장성
(15%) | 비용 효율성
(5%) | **총점** | Phase | +|------|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| **API Gateway** | 9 × 0.35 = 3.15 | 8 × 0.25 = 2.0 | 8 × 0.20 = 1.6 | 9 × 0.15 = 1.35 | 8 × 0.05 = 0.4 | **8.50** | MVP | +| **Queue-Based Load Leveling** | 9 × 0.35 = 3.15 | 9 × 0.25 = 2.25 | 7 × 0.20 = 1.4 | 8 × 0.15 = 1.2 | 7 × 0.05 = 0.35 | **8.35** | MVP | +| **Cache-Aside** | 8 × 0.35 = 2.8 | 9 × 0.25 = 2.25 | 9 × 0.20 = 1.8 | 7 × 0.15 = 1.05 | 9 × 0.05 = 0.45 | **8.35** | MVP | +| **Publisher-Subscriber** | 9 × 0.35 = 3.15 | 7 × 0.25 = 1.75 | 8 × 0.20 = 1.6 | 9 × 0.15 = 1.35 | 8 × 0.05 = 0.4 | **8.25** | MVP | +| **Asynchronous Request-Reply** | 9 × 0.35 = 3.15 | 8 × 0.25 = 2.0 | 7 × 0.20 = 1.4 | 8 × 0.15 = 1.2 | 7 × 0.05 = 0.35 | **8.10** | MVP | +| **Gateway Offloading** | 8 × 0.35 = 2.8 | 6 × 0.25 = 1.5 | 9 × 0.20 = 1.8 | 7 × 0.15 = 1.05 | 8 × 0.05 = 0.4 | **7.55** | MVP | +| **Federated Identity** | 8 × 0.35 = 2.8 | 5 × 0.25 = 1.25 | 9 × 0.20 = 1.8 | 6 × 0.15 = 0.9 | 8 × 0.05 = 0.4 | **7.15** | MVP | +| **Circuit Breaker** | 8 × 0.35 = 2.8 | 6 × 0.25 = 1.5 | 8 × 0.20 = 1.6 | 7 × 0.15 = 1.05 | 8 × 0.05 = 0.4 | **7.35** | Phase 2 | +| **CQRS** | 7 × 0.35 = 2.45 | 8 × 0.25 = 2.0 | 5 × 0.20 = 1.0 | 8 × 0.15 = 1.2 | 6 × 0.05 = 0.3 | **6.95** | Phase 2 | +| **Retry** | 7 × 0.35 = 2.45 | 6 × 0.25 = 1.5 | 8 × 0.20 = 1.6 | 6 × 0.15 = 0.9 | 8 × 0.05 = 0.4 | **6.85** | Phase 2 | +| **Materialized View** | 6 × 0.35 = 2.1 | 8 × 0.25 = 2.0 | 6 × 0.20 = 1.2 | 7 × 0.15 = 1.05 | 7 × 0.05 = 0.35 | **6.70** | Phase 2 | +| **Event Sourcing** | 7 × 0.35 = 2.45 | 7 × 0.25 = 1.75 | 4 × 0.20 = 0.8 | 9 × 0.15 = 1.35 | 5 × 0.05 = 0.25 | **6.60** | Phase 3 | +| **Saga** | 7 × 0.35 = 2.45 | 6 × 0.25 = 1.5 | 4 × 0.20 = 0.8 | 8 × 0.15 = 1.2 | 5 × 0.05 = 0.25 | **6.20** | Phase 3 | +| **Bulkhead** | 6 × 0.35 = 2.1 | 6 × 0.25 = 1.5 | 7 × 0.20 = 1.4 | 6 × 0.15 = 0.9 | 7 × 0.05 = 0.35 | **6.25** | Phase 2 | +| **Valet Key** | 5 × 0.35 = 1.75 | 7 × 0.25 = 1.75 | 8 × 0.20 = 1.6 | 5 × 0.15 = 0.75 | 8 × 0.05 = 0.4 | **6.25** | Phase 2 | +| **Priority Queue** | 5 × 0.35 = 1.75 | 6 × 0.25 = 1.5 | 7 × 0.20 = 1.4 | 6 × 0.15 = 0.9 | 6 × 0.05 = 0.3 | **5.85** | Phase 3 | +| **Health Endpoint Monitoring** | 7 × 0.35 = 2.45 | 4 × 0.25 = 1.0 | 9 × 0.20 = 1.8 | 5 × 0.15 = 0.75 | 8 × 0.05 = 0.4 | **6.40** | MVP | +| **External Configuration Store** | 6 × 0.35 = 2.1 | 4 × 0.25 = 1.0 | 8 × 0.20 = 1.6 | 6 × 0.15 = 0.9 | 8 × 0.05 = 0.4 | **6.00** | Phase 3 | + +### 2.3 선정 패턴 및 근거 + +#### MVP Phase (총 8개 패턴) +1. **API Gateway (8.50점)** ⭐ 최고점 + - 단일 진입점으로 모든 클라이언트 요청 처리 + - 서비스 라우팅 중앙화 + - 높은 기능 적합성과 확장성 + +2. **Queue-Based Load Leveling (8.35점)** + - STT/AI 비동기 처리 필수 + - 부하 분산으로 성능 안정화 + - AI API 호출 최적화 + +3. **Cache-Aside (8.35점)** + - RAG 검색 결과 캐싱으로 성능 향상 + - 구현 용이, 높은 비용 효율성 + - 60% 검색 비용 절감 예상 + +4. **Publisher-Subscriber (8.25점)** + - Todo 상태 변경 이벤트 + - 회의록 공유 알림 + - 실시간 알림 발송 + +5. **Asynchronous Request-Reply (8.10점)** + - AI 회의록 생성 비동기 처리 + - 사용자 대기 시간 단축 + - 장시간 작업 효율적 처리 + +6. **Gateway Offloading (7.55점)** + - 인증(LDAP), 로깅, SSL 처리 + - 서비스 복잡도 감소 + - 핵심 비즈니스 로직에 집중 + +7. **Federated Identity (7.15점)** + - LDAP SSO 연동 + - 사용자 관리 효율화 + - 보안 강화 + +8. **Health Endpoint Monitoring (6.40점)** + - 서비스 상태 점검 + - 장애 조기 발견 + - 운영 안정성 + +#### Phase 2 - 확장 (총 6개 패턴 추가) +9. **Circuit Breaker (7.35점)** + - AI/STT API 장애 격리 + - 장애 복구 시간 90% 단축 + - 가용성 99.9% 달성 + +10. **CQRS (6.95점)** + - 회의록 읽기/쓰기 분리 + - 읽기 성능 70% 향상 + - 확장성 개선 + +11. **Retry (6.85점)** + - 일시적 오류 자동 복구 + - Circuit Breaker와 통합 + - 안정성 향상 + +12. **Materialized View (6.70점)** + - 회의록 목록, 요약 뷰 + - 조회 성능 80% 향상 + - 사용자 경험 개선 + +13. **Bulkhead (6.25점)** + - 서비스 격리로 장애 전파 방지 + - 리소스 풀 분리 + - 안정성 강화 + +14. **Valet Key (6.25점)** + - 음성 파일 직접 다운로드 + - 네트워크 대역폭 절감 + - Azure Storage SAS 토큰 활용 + +#### Phase 3 - 고도화 (총 4개 패턴 추가) +15. **Event Sourcing (6.60점)** + - 완벽한 변경 이력 추적 + - 감사 추적 (Audit Trail) + - 데이터 복원 가능 + +16. **Saga (6.20점)** + - Todo 할당 분산 트랜잭션 + - 데이터 일관성 보장 + - 보상 트랜잭션 + +17. **Priority Queue (5.85점)** + - VIP 회의 우선 처리 + - 중요 회의 응답시간 50% 단축 + - 비즈니스 가치 향상 + +18. **External Configuration Store (6.00점)** + - 동적 설정 관리 + - 재배포 없이 설정 변경 + - 운영 효율성 + +--- + +## 3. 서비스별 패턴 적용 설계 + +### 3.1 서비스별 패턴 매핑 + +#### API Gateway Layer +- **API Gateway**: 모든 요청 진입점, 라우팅 +- **Gateway Offloading**: LDAP 인증, 로깅, SSL 종료 +- **Rate Limiting**: 사용자당 100 req/min + +#### User Service +- **Federated Identity**: LDAP SSO 연동 +- **Cache-Aside**: 사용자 정보 캐싱 (TTL 30분) +- **Health Endpoint**: /actuator/health + +#### Meeting Service +- **CQRS**: 읽기(조회)/쓰기(작성) 모델 분리 +- **Materialized View**: 회의록 목록, 대시보드 요약 +- **Event Sourcing**: 회의록 변경 이력 (Phase 3) +- **Publisher-Subscriber**: 회의록 공유 이벤트 +- **Cache-Aside**: 회의록 목록 (TTL 5분) + +#### STT Service +- **Queue-Based Load Leveling**: 음성 변환 큐 처리 +- **Asynchronous Request-Reply**: 변환 결과 비동기 반환 +- **Valet Key**: Azure Blob Storage 음성 파일 직접 업로드 +- **Circuit Breaker**: Azure Speech API 장애 격리 +- **Retry**: STT API 일시적 오류 (최대 3회) +- **Priority Queue**: 중요 회의 우선 처리 (Phase 3) + +#### AI Service +- **Queue-Based Load Leveling**: LLM 요청 큐 (배치 처리) +- **Asynchronous Request-Reply**: AI 생성 비동기 응답 +- **Circuit Breaker**: OpenAI/Azure OpenAI API 장애 격리 +- **Retry**: LLM API 오류 재시도 (Exponential Backoff) +- **Priority Queue**: VIP 회의 우선 처리 (Phase 3) +- **Cache-Aside**: 프롬프트 템플릿 캐싱 + +#### RAG Service +- **Cache-Aside**: 벡터 검색 결과 캐싱 (TTL 1시간) +- **Materialized View**: 용어 사전 인덱스 +- **Circuit Breaker**: 벡터 DB 장애 격리 +- **Retry**: 검색 실패 재시도 + +#### Collaboration Service +- **Publisher-Subscriber**: 실시간 동기화 이벤트 (WebSocket) +- **Event Sourcing**: 수정 이력 관리 (Phase 3) +- **Bulkhead**: WebSocket 연결 풀 격리 + +#### Todo Service +- **Saga**: Todo 할당 → 알림 → 캘린더 등록 (Phase 3) +- **Publisher-Subscriber**: Todo 상태 변경 이벤트 +- **Cache-Aside**: Todo 목록 캐싱 (TTL 5분) +- **Asynchronous Request-Reply**: 캘린더 등록 비동기 + +#### Notification Service +- **Queue-Based Load Leveling**: 알림 발송 큐 +- **Competing Consumers**: 병렬 알림 처리 (이메일/푸시) +- **Retry**: 알림 발송 실패 재시도 + +#### 공통 적용 +- **Health Endpoint Monitoring**: 모든 서비스 +- **External Configuration Store**: 환경 설정 중앙 관리 (Phase 3) +- **Bulkhead**: 서비스별 리소스 풀 격리 + +### 3.2 전체 시스템 아키텍처 + +```mermaid +graph TB + subgraph "Client Layer" + Web[Web Client] + Mobile[Mobile App] + end + + subgraph "API Gateway Layer" + Gateway[API Gateway
+ Gateway Offloading
+ Rate Limiting] + end + + subgraph "Authentication" + LDAP[LDAP Server
Federated Identity] + end + + subgraph "Microservices" + User[User Service
Cache-Aside] + Meeting[Meeting Service
CQRS + Event Sourcing] + STT[STT Service
Queue + Async] + AI[AI Service
Queue + Circuit Breaker] + RAG[RAG Service
Cache-Aside] + Collab[Collaboration Service
Pub-Sub + WebSocket] + Todo[Todo Service
Saga + Pub-Sub] + Notif[Notification Service
Queue + Competing Consumers] + end + + subgraph "Data Layer" + Cache[(Redis Cache
Cache-Aside)] + UserDB[(User DB)] + MeetingDB[(Meeting DB)] + STTDB[(STT DB)] + AIDB[(AI DB)] + RAGDB[(Vector DB)] + TodoDB[(Todo DB)] + end + + subgraph "Messaging Layer" + Queue[Message Queue
RabbitMQ/Kafka
Queue-Based Load Leveling] + EventBus[Event Bus
Publisher-Subscriber] + end + + subgraph "External Services" + AzureSpeech[Azure Speech API
Circuit Breaker] + OpenAI[OpenAI API
Circuit Breaker] + AzureBlob[Azure Blob Storage
Valet Key] + Email[Email Service] + end + + Web --> Gateway + Mobile --> Gateway + + Gateway --> LDAP + Gateway --> User + Gateway --> Meeting + Gateway --> STT + Gateway --> AI + Gateway --> RAG + Gateway --> Collab + Gateway --> Todo + + User --> Cache + User --> UserDB + + Meeting --> Cache + Meeting --> MeetingDB + Meeting --> EventBus + + STT --> Queue + STT --> AzureSpeech + STT --> AzureBlob + STT --> STTDB + + AI --> Queue + AI --> OpenAI + AI --> Cache + AI --> AIDB + + RAG --> Cache + RAG --> RAGDB + + Collab --> EventBus + + Todo --> EventBus + Todo --> TodoDB + + Notif --> Queue + Notif --> Email + + Queue --> STT + Queue --> AI + Queue --> Notif + + EventBus --> Meeting + EventBus --> Collab + EventBus --> Todo + EventBus --> Notif +``` + +### 3.3 실시간 협업 플로우 + +```mermaid +sequenceDiagram + participant Client1 as 참석자1 + participant Client2 as 참석자2 + participant Gateway as API Gateway + participant Collab as Collaboration Service + participant EventBus as Event Bus
(Pub-Sub) + participant Meeting as Meeting Service + participant Cache as Redis Cache + + Note over Client1,Client2: WebSocket 연결 + Client1->>Gateway: WebSocket Connect + Gateway->>Collab: Establish Connection + Client2->>Gateway: WebSocket Connect + Gateway->>Collab: Establish Connection + + Note over Client1,EventBus: 회의록 수정 + Client1->>Collab: 회의록 수정 (Delta) + Collab->>Meeting: 수정 내용 저장 + Meeting->>Cache: 캐시 무효화 + Meeting->>EventBus: 수정 이벤트 발행
(Publisher) + + Note over EventBus,Client2: 실시간 동기화 + EventBus->>Collab: 수정 이벤트 전달
(Subscriber) + Collab->>Client2: WebSocket Push (Delta) + Client2->>Client2: 화면 실시간 업데이트 + + Note over Client1,Meeting: 충돌 처리 (Last Write Wins) + Client1->>Collab: 동시 수정 시도 + Client2->>Collab: 동시 수정 시도 + Collab->>Meeting: 충돌 감지 + Meeting->>Meeting: Last Write Wins 적용 + Meeting->>EventBus: 최종 수정 이벤트 + EventBus->>Collab: 동기화 + Collab->>Client1: 최종 버전 전송 + Collab->>Client2: 최종 버전 전송 +``` + +### 3.4 AI/STT 비동기 처리 플로우 + +```mermaid +sequenceDiagram + participant Client as Web Client + participant Gateway as API Gateway + participant Meeting as Meeting Service + participant STTQueue as STT Queue
(Queue-Based) + participant STT as STT Service
(Circuit Breaker) + participant AzureSpeech as Azure Speech API + participant AIQueue as AI Queue
(Queue-Based) + participant AI as AI Service
(Circuit Breaker) + participant OpenAI as OpenAI API + participant RAG as RAG Service
(Cache-Aside) + participant Cache as Redis Cache + + Note over Client,Meeting: 음성 녹음 시작 + Client->>Gateway: 음성 녹음 업로드 + Gateway->>Meeting: 녹음 데이터 전달 + Meeting->>STTQueue: 변환 요청 적재
(Async Request-Reply) + Meeting-->>Client: 202 Accepted (즉시 응답) + + Note over STTQueue,AzureSpeech: STT 비동기 처리 + STTQueue->>STT: 큐에서 메시지 가져오기 + STT->>AzureSpeech: 음성-텍스트 변환 요청 + + alt API 정상 + AzureSpeech-->>STT: 변환 결과 + STT->>AIQueue: AI 회의록 작성 요청 + else API 장애 + AzureSpeech--xSTT: 오류 + STT->>STT: Circuit Breaker OPEN + STT->>STT: Retry (최대 3회) + alt Retry 성공 + STT->>AIQueue: AI 요청 + else Retry 실패 + STT->>Meeting: 실패 알림 + Meeting->>Client: WebSocket Push (오류) + end + end + + Note over AIQueue,OpenAI: AI 비동기 처리 + AIQueue->>AI: 큐에서 메시지 가져오기 + AI->>Cache: 프롬프트 템플릿 조회 + Cache-->>AI: 캐시된 템플릿 + AI->>OpenAI: 회의록 생성 요청 + + alt API 정상 + OpenAI-->>AI: 회의록 초안 + AI->>RAG: 용어 설명 요청 + RAG->>Cache: 검색 결과 조회 + + alt 캐시 적중 + Cache-->>RAG: 캐시된 설명 + else 캐시 미스 + RAG->>RAG: 벡터 검색 수행 + RAG->>Cache: 결과 캐싱 (TTL 1시간) + end + + RAG-->>AI: 용어 설명 + AI->>Meeting: 최종 회의록 전달 + Meeting->>Client: WebSocket Push (결과) + else API 장애 + OpenAI--xAI: 오류 + AI->>AI: Circuit Breaker OPEN + AI->>AI: Retry (Exponential Backoff) + end +``` + +### 3.5 Todo 실시간 연동 플로우 (Saga 패턴) + +```mermaid +sequenceDiagram + participant Client as Web Client + participant Gateway as API Gateway + participant Meeting as Meeting Service + participant AI as AI Service + participant Todo as Todo Service
(Saga Orchestrator) + participant EventBus as Event Bus
(Pub-Sub) + participant Notif as Notification Service + participant Calendar as Calendar API + + Note over Client,AI: 회의록 확정 및 Todo 추출 + Client->>Gateway: 회의록 최종 확정 + Gateway->>Meeting: 확정 요청 + Meeting->>AI: Todo 자동 추출 요청 + AI->>AI: LLM 분석
(액션 아이템 추출) + AI-->>Meeting: 추출된 Todo 목록 + + Note over Meeting,Todo: Saga 시작 (Phase 3) + Meeting->>Todo: Todo 할당 요청 (Saga Start) + + Note over Todo,Calendar: Saga Step 1: Todo 등록 + Todo->>Todo: Todo 데이터 저장 + Todo->>EventBus: TodoCreated 이벤트
(Publisher) + + Note over Todo,Calendar: Saga Step 2: 알림 발송 + EventBus->>Notif: TodoCreated 수신
(Subscriber) + Notif->>Notif: 이메일 발송 + + alt 알림 성공 + Notif->>EventBus: NotificationSent 이벤트 + else 알림 실패 + Notif->>EventBus: NotificationFailed 이벤트 + EventBus->>Todo: 보상 트랜잭션
(Todo 상태 변경: 알림 실패) + end + + Note over Todo,Calendar: Saga Step 3: 캘린더 등록 + EventBus->>Todo: NotificationSent 수신 + Todo->>Calendar: 일정 등록 요청 (비동기) + + alt 캘린더 등록 성공 + Calendar-->>Todo: 성공 + Todo->>EventBus: TodoCompleted 이벤트
(Saga 완료) + else 캘린더 등록 실패 + Calendar--xTodo: 실패 + Todo->>Todo: 보상 트랜잭션
(Todo 상태: 캘린더 미등록) + Todo->>EventBus: SagaCompensated 이벤트 + end + + Note over EventBus,Meeting: 회의록 실시간 반영 + EventBus->>Meeting: Todo 이벤트 수신 + Meeting->>Meeting: 회의록에 Todo 링크 추가 + Meeting->>EventBus: MeetingUpdated 이벤트 + EventBus->>Client: WebSocket Push (Todo 상태) + Client->>Client: 화면 업데이트 + + Note over Client,Meeting: Todo 완료 처리 + Client->>Todo: Todo 완료 요청 + Todo->>Todo: 완료 시간 기록 + Todo->>EventBus: TodoCompleted 이벤트 + EventBus->>Meeting: Todo 완료 수신 + Meeting->>Meeting: 회의록에 완료 표시 + Meeting->>EventBus: MeetingUpdated 이벤트 + EventBus->>Client: WebSocket Push (완료 상태) +``` + +--- + +## 4. Phase별 구현 로드맵 + +### 4.1 Phase 1: MVP (3개월) + +#### 목표 +- 핵심 기능 구현 (회의록 작성, STT, AI, Todo) +- 안정적 서비스 출시 +- 100명 동시 사용자 지원 + +#### 적용 패턴 (8개) +1. API Gateway +2. Gateway Offloading +3. Queue-Based Load Leveling +4. Cache-Aside +5. Publisher-Subscriber +6. Asynchronous Request-Reply +7. Federated Identity +8. Health Endpoint Monitoring + +#### 마일스톤 +| 주차 | 작업 내용 | 산출물 | +|------|-----------|--------| +| 1-2주 | 인프라 구축 | API Gateway, Redis, RabbitMQ 설정 | +| 3-4주 | User/Meeting 서비스 개발 | 인증, 회의 관리 API | +| 5-6주 | STT 서비스 개발 | 음성 녹음, 변환 API | +| 7-8주 | AI 서비스 개발 | 회의록 자동 생성, Todo 추출 | +| 9-10주 | RAG/Collaboration 서비스 | 용어 설명, 실시간 동기화 | +| 11-12주 | 통합 테스트 및 배포 | 프로덕션 배포, 모니터링 | + +#### 예상 성과 +- **성능**: + - STT 변환: 1초 이내 ✓ + - AI 회의록 생성: 10초 이내 + - RAG 검색: 2초 (캐시 적중 0.1초) + - 실시간 동기화: 3-5초 간격 +- **가용성**: 99% uptime +- **동시 사용자**: 100명 +- **비용**: 월 $500 (Azure 인프라) + +### 4.2 Phase 2: 확장 (6개월, MVP 이후) + +#### 목표 +- 성능 최적화 (CQRS, Materialized View) +- 안정성 강화 (Circuit Breaker, Retry, Bulkhead) +- 1,000명 동시 사용자 지원 + +#### 추가 패턴 (6개) +9. CQRS +10. Circuit Breaker +11. Retry +12. Materialized View +13. Bulkhead +14. Valet Key + +#### 마일스톤 +| 월 | 작업 내용 | 성과 지표 | +|----|-----------|-----------| +| 1-2개월 | CQRS 적용 (Meeting 서비스) | 읽기 성능 70% 향상 | +| 3-4개월 | Circuit Breaker/Retry 적용 | 장애 복구 시간 90% 단축 | +| 5개월 | Materialized View 구현 | 목록 조회 80% 빠름 | +| 6개월 | 부하 테스트 및 최적화 | 1,000명 동시 사용자 | + +#### 예상 성과 +- **성능 개선**: + - 읽기 응답시간: 70% 단축 (CQRS) + - 목록 조회: 80% 빠름 (Materialized View) + - RAG 캐시 적중률: 80% +- **가용성**: 99.9% uptime (Circuit Breaker) +- **동시 사용자**: 1,000명 (10배 증가) +- **비용**: 월 $2,000 (Auto-scaling) + +### 4.3 Phase 3: 고도화 (12개월, Phase 2 이후) + +#### 목표 +- 대규모 확장 (10,000명 동시 사용자) +- 완벽한 감사 추적 (Event Sourcing) +- 글로벌 배포 (다중 리전) + +#### 추가 패턴 (4개) +15. Event Sourcing +16. Saga +17. Priority Queue +18. External Configuration Store + +#### 마일스톤 +| 분기 | 작업 내용 | 성과 지표 | +|------|-----------|-----------| +| Q1 | Event Sourcing 구현 | 완벽한 변경 이력 | +| Q2 | Saga 패턴 적용 (Todo) | 분산 트랜잭션 일관성 | +| Q3 | Priority Queue 도입 | VIP 회의 50% 빠름 | +| Q4 | 다중 리전 배포 | 글로벌 확장 | + +#### 예상 성과 +- **성능**: + - Priority Queue: VIP 회의 응답시간 50% 단축 + - Event Sourcing: 완벽한 감사 추적 +- **가용성**: 99.99% uptime (다중 리전) +- **동시 사용자**: 10,000명 +- **글로벌**: 3개 리전 (한국, 미국, 유럽) +- **비용**: 월 $10,000 (글로벌 인프라) + +### 4.4 비용 효율성 분석 + +| Phase | 인프라 비용 | 예상 절감 | ROI | +|-------|------------|----------|-----| +| MVP | $500/월 | - | - | +| Phase 2 | $2,000/월 | Cache-Aside: RAG 비용 60% 절감
Async: AI API 30% 절감
Auto-scaling: 유휴 자원 80% 절감 | **총 50% 절감** | +| Phase 3 | $10,000/월 | 글로벌 확장으로 사용자 10배 증가
비용은 5배만 증가 (효율성 2배) | **비용 대비 2배 효율** | + +--- + +## 5. 구현 시 고려사항 + +### 5.1 API Gateway 구현 + +**기술 스택**: Spring Cloud Gateway + +**설정**: +```yaml +spring: + cloud: + gateway: + routes: + - id: user-service + uri: lb://user-service + predicates: + - Path=/api/users/** + filters: + - name: RequestRateLimiter + args: + redis-rate-limiter.replenishRate: 100 + redis-rate-limiter.burstCapacity: 200 + - id: meeting-service + uri: lb://meeting-service + predicates: + - Path=/api/meetings/** +``` + +**Gateway Offloading**: +- LDAP 인증: Spring Security LDAP +- 로깅: Spring Cloud Sleuth + Zipkin +- SSL 종료: Let's Encrypt 인증서 + +**Rate Limiting**: +- 사용자당: 100 req/min +- 익명 사용자: 10 req/min +- VIP: 500 req/min + +### 5.2 Queue 설정 + +**기술 스택**: RabbitMQ (MVP), Kafka (Phase 2) + +**Queue 구성**: +```yaml +queues: + stt: + name: stt-conversion-queue + type: priority # Priority Queue (Phase 3) + durable: true + ttl: 1800000 # 30분 + dead-letter-exchange: stt-dlx + + ai: + name: ai-generation-queue + type: priority + durable: true + ttl: 1800000 + dead-letter-exchange: ai-dlx + + notification: + name: notification-queue + durable: true + ttl: 300000 # 5분 +``` + +**Dead Letter Queue**: +- 실패한 메시지 3회 재시도 후 DLQ로 이동 +- DLQ 메시지 수동 확인 및 재처리 + +**메시지 우선순위** (Phase 3): +- VIP 회의: Priority 9 +- 일반 회의: Priority 5 +- 배치 작업: Priority 1 + +### 5.3 Cache 전략 + +**기술 스택**: Redis Cluster (고가용성) + +**TTL 설정**: +```yaml +cache: + rag-search: + ttl: 3600 # 1시간 + max-size: 10000 + + user-info: + ttl: 1800 # 30분 + max-size: 5000 + + meeting-list: + ttl: 300 # 5분 + max-size: 1000 + + todo-list: + ttl: 300 # 5분 + max-size: 2000 +``` + +**캐시 무효화**: +- 쓰기 작업 시 즉시 무효화 +- 회의록 수정 → 해당 회의 캐시 삭제 +- Todo 상태 변경 → Todo 목록 캐시 삭제 + +**캐시 워밍**: +- 애플리케이션 시작 시 인기 회의록 사전 로드 +- 스케줄러: 매일 오전 6시 캐시 갱신 + +### 5.4 WebSocket 최적화 + +**기술 스택**: SockJS + STOMP + +**설정**: +```yaml +websocket: + heartbeat: + client: 30000 # 30초 + server: 30000 + + message-size-limit: 1048576 # 1MB + + connection: + max-per-user: 5 + timeout: 300000 # 5분 +``` + +**재연결 정책**: +- Exponential Backoff: 1초, 2초, 4초, 8초, 16초 +- 최대 재시도: 5회 + +**메시지 전송 최적화**: +- Delta 전송: 전체 내용이 아닌 변경 부분만 +- 압축: gzip 압축 (1KB 이상 메시지) +- 배치: 100ms 내 변경사항 묶어서 전송 + +### 5.5 AI/LLM 최적화 + +**기술 스택**: Azure OpenAI Service + +**배치 처리**: +```python +# 여러 요청 묶어서 처리 +batch_requests = [] +for i in range(batch_size): + request = queue.pop() + batch_requests.append(request) + +# 한 번에 처리 +responses = openai.ChatCompletion.create_batch(batch_requests) +``` + +**스트리밍 응답**: +- 긴 회의록 생성 시 스트리밍 +- 클라이언트에 실시간 전달 (WebSocket) + +**프롬프트 캐싱**: +- 동일 템플릿 재사용 +- Redis에 프롬프트 템플릿 저장 + +**비용 최적화**: +- 토큰 제한: 요약 시 최대 500 토큰 +- 모델 선택: GPT-3.5 (일반), GPT-4 (중요 회의) + +### 5.6 Circuit Breaker 설정 + +**기술 스택**: Resilience4j + +**설정**: +```yaml +resilience4j: + circuitbreaker: + instances: + azure-speech: + failure-rate-threshold: 50 # 50% 실패 시 OPEN + wait-duration-in-open-state: 30s + sliding-window-size: 10 + minimum-number-of-calls: 5 + + openai: + failure-rate-threshold: 50 + wait-duration-in-open-state: 60s # 1분 + sliding-window-size: 20 + + retry: + instances: + azure-speech: + max-attempts: 3 + wait-duration: 1s + retry-exceptions: + - java.net.SocketTimeoutException + - org.springframework.web.client.HttpServerErrorException +``` + +**Fallback 전략**: +- STT 실패 → 수동 입력 안내 +- AI 실패 → 기본 템플릿 제공 +- RAG 실패 → 일반 용어 설명 + +### 5.7 CQRS 구현 (Phase 2) + +**Read Model** (조회): +```java +@Service +public class MeetingQueryService { + @Cacheable("meeting-list") + public List getMeetingList(Long userId) { + // Materialized View 조회 + return materializedViewRepository.findByUserId(userId); + } +} +``` + +**Write Model** (쓰기): +```java +@Service +public class MeetingCommandService { + @CacheEvict(value = "meeting-list", key = "#userId") + public void updateMeeting(Long meetingId, MeetingUpdateRequest request) { + // DB 갱신 + meetingRepository.update(meetingId, request); + + // 이벤트 발행 + eventBus.publish(new MeetingUpdatedEvent(meetingId)); + } +} +``` + +**Materialized View 갱신**: +- 이벤트 기반 비동기 갱신 +- 배치 작업: 매 10분마다 동기화 + +### 5.8 Event Sourcing 구현 (Phase 3) + +**Event Store**: +```java +@Entity +public class MeetingEvent { + @Id + private UUID eventId; + private Long aggregateId; // Meeting ID + private String eventType; // CREATED, UPDATED, SHARED + private String eventData; // JSON + private LocalDateTime timestamp; + private Long userId; +} +``` + +**이벤트 재생**: +```java +public Meeting rebuildFromEvents(Long meetingId) { + List events = eventStore.findByAggregateId(meetingId); + Meeting meeting = new Meeting(); + for (MeetingEvent event : events) { + meeting.apply(event); + } + return meeting; +} +``` + +**스냅샷** (성능 최적화): +- 100개 이벤트마다 스냅샷 생성 +- 재생 시 최신 스냅샷부터 시작 + +### 5.9 모니터링 및 운영 + +**Health Endpoint**: +```yaml +management: + endpoints: + web: + exposure: + include: health, metrics, prometheus + health: + circuitbreakers: + enabled: true + redis: + enabled: true + db: + enabled: true +``` + +**Metrics** (Prometheus + Grafana): +- 서비스 응답시간 (p50, p95, p99) +- 요청 처리량 (req/s) +- 에러율 (%) +- Circuit Breaker 상태 +- 캐시 적중률 + +**분산 추적** (Zipkin): +- 요청 전체 흐름 추적 +- 병목 구간 식별 + +**로그 중앙화** (ELK Stack): +- Elasticsearch: 로그 저장 +- Logstash: 로그 수집 및 파싱 +- Kibana: 로그 검색 및 시각화 + +**알림** (Phase 2): +- 에러율 5% 초과 → Slack 알림 +- Circuit Breaker OPEN → 즉시 알림 +- 디스크 사용량 80% → 경고 + +### 5.10 보안 + +**HTTPS 강제**: +- Let's Encrypt 무료 인증서 +- HTTP → HTTPS 자동 리다이렉트 + +**CORS 설정**: +```yaml +spring: + web: + cors: + allowed-origins: + - https://meeting.example.com + allowed-methods: GET, POST, PUT, DELETE + allowed-headers: "*" + allow-credentials: true +``` + +**SQL Injection 방지**: +- JPA/Hibernate PreparedStatement 사용 +- 동적 쿼리 금지 + +**XSS 방지**: +- Input Sanitization (OWASP Java Encoder) +- Output Encoding + +**민감 정보 암호화**: +- DB 암호화: AES-256 +- 전송 암호화: TLS 1.3 + +--- + +## 6. 예상 성과 지표 + +### 6.1 Phase별 성과 비교 + +| 지표 | MVP (Phase 1) | Phase 2 (확장) | Phase 3 (고도화) | +|------|---------------|----------------|------------------| +| **성능** | | | | +| STT 변환 | 1초 이내 | 0.8초 | 0.5초 | +| AI 회의록 생성 | 10초 | 7초 (배치 처리) | 5초 (스트리밍) | +| RAG 검색 (캐시 미스) | 2초 | 1.5초 | 1초 | +| RAG 검색 (캐시 적중) | 0.1초 | 0.1초 | 0.1초 | +| 실시간 동기화 | 3-5초 | 2-3초 | 1-2초 | +| 회의록 목록 조회 | 500ms | 100ms (Materialized View) | 50ms | +| **가용성** | | | | +| Uptime | 99% | 99.9% | 99.99% | +| MTTR (평균 복구 시간) | 30분 | 3분 (Circuit Breaker) | 1분 | +| **확장성** | | | | +| 동시 사용자 | 100명 | 1,000명 | 10,000명 | +| 회의당 최대 참석자 | 10명 | 50명 | 100명 | +| 일일 회의 처리 | 500건 | 5,000건 | 50,000건 | +| **비용** | | | | +| 인프라 비용 | $500/월 | $2,000/월 | $10,000/월 | +| 사용자당 비용 | $5/월 | $2/월 (2.5배 절감) | $1/월 (5배 절감) | + +### 6.2 패턴별 기대 효과 + +#### Cache-Aside +- **RAG 검색 비용**: 60% 절감 + - 캐시 미스: 2초 → 캐시 적중: 0.1초 + - 캐시 적중률: 80% (Phase 2) +- **API 호출 감소**: 외부 API 호출 80% 감소 +- **비용 절감**: 월 $500 → $200 (RAG API 비용) + +#### Queue-Based Load Leveling +- **처리량 향상**: 동시 요청 100 → 1,000 (10배) +- **응답 안정성**: 피크 시간대 응답시간 편차 50% 감소 +- **자원 효율**: CPU 사용률 평준화 (80% → 50%) + +#### Asynchronous Request-Reply +- **사용자 대기 시간**: 10초 → 즉시 응답 (202 Accepted) +- **사용자 경험**: 응답 대기 중 다른 작업 가능 +- **AI API 최적화**: 배치 처리로 30% 비용 절감 + +#### CQRS (Phase 2) +- **읽기 성능**: 응답시간 70% 단축 (500ms → 150ms) +- **쓰기 성능**: 독립적 최적화 가능 +- **확장성**: 읽기/쓰기 DB 독립 스케일링 + +#### Circuit Breaker (Phase 2) +- **장애 복구**: MTTR 30분 → 3분 (90% 단축) +- **장애 격리**: 한 서비스 장애가 전체 영향 없음 +- **가용성**: 99% → 99.9% uptime + +#### Event Sourcing (Phase 3) +- **감사 추적**: 100% 변경 이력 보존 +- **데이터 복원**: 임의 시점 상태 복원 가능 +- **규제 준수**: 완벽한 감사 로그 + +#### Priority Queue (Phase 3) +- **VIP 회의**: 응답시간 50% 단축 (10초 → 5초) +- **비즈니스 가치**: 중요 회의 우선 처리 +- **자원 활용**: 일반 회의는 유휴 시간 활용 + +### 6.3 비용 효율성 분석 + +#### Phase 1 (MVP) +- **인프라**: $500/월 + - API Gateway: $100 + - Redis: $50 + - RabbitMQ: $50 + - DB (PostgreSQL): $100 + - 컴퓨팅 (VM): $200 +- **외부 API**: $300/월 + - Azure Speech: $100 + - OpenAI: $200 +- **총 비용**: $800/월 +- **사용자**: 100명 +- **사용자당 비용**: $8/월 + +#### Phase 2 (확장) +- **인프라**: $2,000/월 (4배 증가) +- **외부 API**: $600/월 (2배 증가) + - Cache-Aside로 60% 절감 → $240 절감 + - Async 배치 처리로 30% 절감 → $60 절감 + - 실제 비용: $600 - $300 = $300 +- **총 비용**: $2,300/월 +- **사용자**: 1,000명 (10배 증가) +- **사용자당 비용**: $2.3/월 (71% 절감) + +#### Phase 3 (고도화) +- **인프라**: $10,000/월 (글로벌 배포) +- **외부 API**: $1,000/월 +- **총 비용**: $11,000/월 +- **사용자**: 10,000명 +- **사용자당 비용**: $1.1/월 (86% 절감 대비 MVP) + +**ROI 분석**: +- Phase 2: 투자 대비 2.5배 효율 (사용자 10배, 비용 4배) +- Phase 3: 투자 대비 7배 효율 (사용자 100배, 비용 14배) + +--- + +## 7. 결론 + +### 7.1 핵심 성과 + +본 아키텍처 패턴 적용 방안은 회의록 작성 및 공유 개선 서비스의 기술적 도전과제를 체계적으로 해결합니다: + +1. **실시간 협업 처리**: Publisher-Subscriber + WebSocket으로 3-5초 실시간 동기화 달성 +2. **대용량 데이터 처리**: Queue-Based Load Leveling으로 안정적 비동기 처리 +3. **AI/LLM 통합**: Asynchronous Request-Reply + Circuit Breaker로 안정성과 사용자 경험 보장 +4. **RAG 성능**: Cache-Aside로 80% 캐시 적중률, 60% 비용 절감 +5. **서비스 격리**: Circuit Breaker + Bulkhead로 99.9% 가용성 달성 +6. **데이터 일관성**: Saga + Event Sourcing으로 완벽한 일관성 보장 + +### 7.2 차별화 포인트 실현 + +아키텍처 패턴 적용으로 서비스 차별화 포인트를 강화합니다: + +- **맥락 기반 용어 설명**: RAG + Cache-Aside로 2초 이내 실용적 정보 제공 +- **강화된 Todo 연결**: Saga 패턴으로 Todo ↔ 회의록 실시간 양방향 연동 +- **프롬프팅 기반 회의록 개선**: Async Request-Reply로 즉시 응답, 백그라운드 생성 + +### 7.3 단계별 추진 전략 + +**Phase 1 (MVP)**: 핵심 패턴 8개로 안정적 출시 +- API Gateway, Queue, Cache, Pub-Sub 중심 +- 100명 사용자, 99% 가용성 목표 + +**Phase 2 (확장)**: 성능 최적화 및 안정성 강화 +- CQRS, Circuit Breaker, Materialized View 추가 +- 1,000명 사용자, 99.9% 가용성 목표 +- 70% 성능 향상, 90% 장애 복구 시간 단축 + +**Phase 3 (고도화)**: 대규모 확장 및 글로벌 배포 +- Event Sourcing, Saga, Priority Queue 적용 +- 10,000명 사용자, 99.99% 가용성 목표 +- 완벽한 감사 추적, 글로벌 3개 리전 + +### 7.4 비용 효율성 + +- **Phase 1**: 사용자당 $8/월 +- **Phase 2**: 사용자당 $2.3/월 (71% 절감) +- **Phase 3**: 사용자당 $1.1/월 (86% 절감) + +**투자 대비 수익**: +- Phase 2: 2.5배 효율 (사용자 10배 증가, 비용 4배 증가) +- Phase 3: 7배 효율 (사용자 100배 증가, 비용 14배 증가) + +### 7.5 리스크 및 대응 + +| 리스크 | 영향 | 대응 패턴 | +|--------|------|-----------| +| AI/STT API 장애 | 서비스 중단 | Circuit Breaker + Retry | +| 동시 수정 충돌 | 데이터 손실 | Last Write Wins + Event Sourcing | +| 대용량 트래픽 | 응답 지연 | Queue-Based Load Leveling + Auto-scaling | +| 캐시 불일치 | 데이터 정합성 | Write-through + TTL 관리 | +| 분산 트랜잭션 실패 | 데이터 불일치 | Saga + 보상 트랜잭션 | + +### 7.6 다음 단계 + +1. **MVP 개발 착수** (즉시) + - API Gateway, Queue, Cache 인프라 구축 + - User/Meeting 서비스 개발 시작 + +2. **프로토타입 검증** (1개월 내) + - 핵심 패턴 PoC 구현 + - 성능 및 안정성 테스트 + +3. **단계별 출시** (3-12개월) + - Phase 1: 3개월 MVP 출시 + - Phase 2: 6개월 성능 최적화 + - Phase 3: 12개월 글로벌 확장 + +--- + +## 부록 + +### A. 참고 문서 +- [유저스토리](../userstory.md) +- [클라우드 디자인 패턴 개요](../../claude/cloud-design-patterns.md) +- [아키텍처 패턴 선정 가이드](../../claude/architecture-patterns.md) + +### B. 기술 스택 +- **API Gateway**: Spring Cloud Gateway +- **Message Queue**: RabbitMQ (MVP), Kafka (Phase 2) +- **Cache**: Redis Cluster +- **Database**: PostgreSQL (주 DB), MongoDB (Event Store) +- **WebSocket**: SockJS + STOMP +- **Circuit Breaker**: Resilience4j +- **모니터링**: Prometheus + Grafana, Zipkin, ELK Stack +- **외부 API**: Azure Speech, Azure OpenAI, Azure Blob Storage + +### C. 용어 정의 +- **STT**: Speech-to-Text (음성-텍스트 변환) +- **LLM**: Large Language Model (대규모 언어 모델) +- **RAG**: Retrieval-Augmented Generation (검색 증강 생성) +- **CQRS**: Command Query Responsibility Segregation (명령 쿼리 책임 분리) +- **Saga**: 분산 트랜잭션 관리 패턴 +- **Event Sourcing**: 이벤트 기반 데이터 저장 패턴 +- **Circuit Breaker**: 장애 격리 패턴 +- **Pub-Sub**: Publisher-Subscriber (발행-구독) 패턴 + +### D. Mermaid 다이어그램 검증 +모든 Mermaid 다이어그램은 https://mermaid.live/edit 에서 검증 완료되었습니다. + +--- + +**문서 버전**: 1.0 +**최종 수정**: 2025-10-21 +**작성자**: 아키텍트 팀 (길동)