mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 16:06:23 +00:00
[포트 통일] - ai-python 서비스 포트를 8087로 완전 통일 - 모든 문서에서 8086 참조 제거 - README.md, 개발 가이드 문서 전부 8087로 업데이트 변경 파일: - ai-python/README.md - develop/dev/ai-frontend-integration-guide.md - develop/dev/dev-*.md (5개 파일) [meeting-ai 테스트] 테스트 완료 항목: ✓ 회의록 통합 및 취합 ✓ AI 한줄 요약/상세 요약 생성 ✓ 회의 전체 결정사항 추출 ✓ TODO 자동 추출 (9개) ✓ 통계 정보 생성 ✓ 주요 키워드 추출 (10개) 테스트 파일: - develop/test/meeting-ai-test-data.json (테스트 데이터) - develop/test/consolidate-response.json (API 응답) - develop/test/meeting-ai-test-result.md (상세 결과서)
385 lines
9.3 KiB
Markdown
385 lines
9.3 KiB
Markdown
# 프론트엔드 Mock 데이터 개발 가이드
|
|
|
|
**작성일**: 2025-10-27
|
|
**대상**: 프론트엔드 개발자 (유진)
|
|
**작성자**: AI팀 (서연), 백엔드팀 (준호)
|
|
|
|
---
|
|
|
|
## 📋 개요
|
|
|
|
**현재 상황**: STT 서비스 개발 완료 전까지는 **실제 AI 제안사항이 생성되지 않습니다.**
|
|
|
|
**해결 방안**: Mock 데이터를 사용하여 프론트엔드 UI를 독립적으로 개발할 수 있습니다.
|
|
|
|
---
|
|
|
|
## 🎯 왜 Mock 데이터가 필요한가?
|
|
|
|
### 실제 데이터 생성 흐름
|
|
|
|
```
|
|
회의 (음성)
|
|
↓
|
|
STT 서비스 (음성 → 텍스트) ← 아직 개발 중
|
|
↓
|
|
Redis (텍스트 축적)
|
|
↓
|
|
AI 서비스 (Claude API 분석)
|
|
↓
|
|
SSE 스트리밍
|
|
↓
|
|
프론트엔드
|
|
```
|
|
|
|
**문제점**: STT가 없으면 텍스트가 생성되지 않아 → Redis가 비어있음 → AI 분석이 실행되지 않음
|
|
|
|
**해결**: Mock 데이터로 **STT 없이도** UI 개발 가능
|
|
|
|
---
|
|
|
|
## 💻 Mock 데이터 구현 방법
|
|
|
|
### 방법 1: 로컬 Mock 함수 (권장)
|
|
|
|
**장점**: 백엔드 없이 완전 독립 개발 가능
|
|
|
|
```javascript
|
|
/**
|
|
* Mock AI 제안사항 생성기
|
|
* 실제 AI처럼 5초마다 하나씩 제안사항 발행
|
|
*/
|
|
function connectMockAISuggestions(meetingId) {
|
|
const mockSuggestions = [
|
|
{
|
|
id: crypto.randomUUID(),
|
|
content: "신제품의 타겟 고객층을 20-30대로 설정하고, 모바일 우선 전략을 취하기로 논의 중입니다.",
|
|
timestamp: "00:05:23",
|
|
confidence: 0.92
|
|
},
|
|
{
|
|
id: crypto.randomUUID(),
|
|
content: "개발 일정: 1차 프로토타입은 11월 15일까지 완성, 2차 베타는 12월 1일 론칭",
|
|
timestamp: "00:08:45",
|
|
confidence: 0.88
|
|
},
|
|
{
|
|
id: crypto.randomUUID(),
|
|
content: "마케팅 예산 배분에 대해 SNS 광고 60%, 인플루언서 마케팅 40%로 의견이 나왔으나 추가 검토 필요",
|
|
timestamp: "00:12:18",
|
|
confidence: 0.85
|
|
},
|
|
{
|
|
id: crypto.randomUUID(),
|
|
content: "보안 요구사항 검토가 필요하며, 데이터 암호화 방식에 대한 논의가 진행 중입니다.",
|
|
timestamp: "00:15:30",
|
|
confidence: 0.90
|
|
},
|
|
{
|
|
id: crypto.randomUUID(),
|
|
content: "React로 프론트엔드 개발하기로 결정되었으며, TypeScript 사용을 권장합니다.",
|
|
timestamp: "00:18:42",
|
|
confidence: 0.93
|
|
}
|
|
];
|
|
|
|
let index = 0;
|
|
const interval = setInterval(() => {
|
|
if (index < mockSuggestions.length) {
|
|
// EventSource의 addEventListener('ai-suggestion', ...) 핸들러를 모방
|
|
const event = {
|
|
data: JSON.stringify({
|
|
suggestions: [mockSuggestions[index]]
|
|
})
|
|
};
|
|
|
|
// 실제 핸들러 호출
|
|
handleAISuggestion(event);
|
|
index++;
|
|
} else {
|
|
clearInterval(interval);
|
|
console.log('[MOCK] 모든 Mock 제안사항 발행 완료');
|
|
}
|
|
}, 5000); // 5초마다 하나씩
|
|
|
|
console.log('[MOCK] Mock AI 제안사항 연결 시작');
|
|
|
|
// 정리 함수 반환
|
|
return {
|
|
close: () => {
|
|
clearInterval(interval);
|
|
console.log('[MOCK] Mock 연결 종료');
|
|
}
|
|
};
|
|
}
|
|
```
|
|
|
|
### 방법 2: 환경 변수로 전환
|
|
|
|
```javascript
|
|
// 환경 변수로 Mock/Real 모드 전환
|
|
const USE_MOCK_AI = process.env.REACT_APP_USE_MOCK_AI === 'true';
|
|
|
|
function connectAISuggestions(meetingId) {
|
|
if (USE_MOCK_AI) {
|
|
console.log('[MOCK] Mock 모드로 실행');
|
|
return connectMockAISuggestions(meetingId);
|
|
} else {
|
|
console.log('[REAL] 실제 AI 서비스 연결');
|
|
return connectRealAISuggestions(meetingId);
|
|
}
|
|
}
|
|
|
|
function connectRealAISuggestions(meetingId) {
|
|
const url = `http://localhost:8087/api/v1/ai/suggestions/meetings/${meetingId}/stream`;
|
|
const eventSource = new EventSource(url);
|
|
|
|
eventSource.addEventListener('ai-suggestion', handleAISuggestion);
|
|
|
|
eventSource.onerror = (error) => {
|
|
console.error('[REAL] SSE 연결 오류:', error);
|
|
eventSource.close();
|
|
};
|
|
|
|
return eventSource;
|
|
}
|
|
|
|
// 공통 핸들러
|
|
function handleAISuggestion(event) {
|
|
const data = JSON.parse(event.data);
|
|
|
|
data.suggestions.forEach(suggestion => {
|
|
addSuggestionToUI(suggestion);
|
|
});
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 개발 환경 설정
|
|
|
|
### `.env.local` 파일
|
|
|
|
```env
|
|
# Mock 모드 사용 (개발 중)
|
|
REACT_APP_USE_MOCK_AI=true
|
|
|
|
# 실제 AI 서비스 URL (STT 완료 후)
|
|
REACT_APP_AI_SERVICE_URL=http://localhost:8087
|
|
```
|
|
|
|
### `package.json` 스크립트
|
|
|
|
```json
|
|
{
|
|
"scripts": {
|
|
"start": "REACT_APP_USE_MOCK_AI=true react-scripts start",
|
|
"start:real": "REACT_APP_USE_MOCK_AI=false react-scripts start",
|
|
"build": "REACT_APP_USE_MOCK_AI=false react-scripts build"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🎨 React 전체 예시
|
|
|
|
```typescript
|
|
import { useEffect, useState, useRef } from 'react';
|
|
|
|
interface Suggestion {
|
|
id: string;
|
|
content: string;
|
|
timestamp: string;
|
|
confidence: number;
|
|
}
|
|
|
|
interface MockConnection {
|
|
close: () => void;
|
|
}
|
|
|
|
function useMockAISuggestions(meetingId: string) {
|
|
const [suggestions, setSuggestions] = useState<Suggestion[]>([]);
|
|
const [connected, setConnected] = useState(false);
|
|
const connectionRef = useRef<MockConnection | null>(null);
|
|
|
|
useEffect(() => {
|
|
const mockSuggestions: Suggestion[] = [
|
|
{
|
|
id: crypto.randomUUID(),
|
|
content: "신제품의 타겟 고객층을 20-30대로 설정하고...",
|
|
timestamp: "00:05:23",
|
|
confidence: 0.92
|
|
},
|
|
// ... 더 많은 Mock 데이터
|
|
];
|
|
|
|
let index = 0;
|
|
setConnected(true);
|
|
|
|
const interval = setInterval(() => {
|
|
if (index < mockSuggestions.length) {
|
|
setSuggestions(prev => [mockSuggestions[index], ...prev]);
|
|
index++;
|
|
} else {
|
|
clearInterval(interval);
|
|
}
|
|
}, 5000);
|
|
|
|
connectionRef.current = {
|
|
close: () => {
|
|
clearInterval(interval);
|
|
setConnected(false);
|
|
}
|
|
};
|
|
|
|
return () => {
|
|
connectionRef.current?.close();
|
|
};
|
|
}, [meetingId]);
|
|
|
|
return { suggestions, connected };
|
|
}
|
|
|
|
function AISuggestionsPanel({ meetingId }: { meetingId: string }) {
|
|
const USE_MOCK = process.env.REACT_APP_USE_MOCK_AI === 'true';
|
|
|
|
const mockData = useMockAISuggestions(meetingId);
|
|
const realData = useRealAISuggestions(meetingId); // 실제 SSE 연결
|
|
|
|
const { suggestions, connected } = USE_MOCK ? mockData : realData;
|
|
|
|
return (
|
|
<div className="ai-panel">
|
|
<div className="header">
|
|
<h3>AI 제안사항</h3>
|
|
<span className={`badge ${connected ? 'connected' : 'disconnected'}`}>
|
|
{connected ? (USE_MOCK ? 'Mock 모드' : '연결됨') : '연결 끊김'}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="suggestions">
|
|
{suggestions.map(s => (
|
|
<SuggestionCard key={s.id} suggestion={s} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 테스트 시나리오
|
|
|
|
### 1. Mock 모드 테스트
|
|
|
|
```bash
|
|
# Mock 모드로 실행
|
|
REACT_APP_USE_MOCK_AI=true npm start
|
|
```
|
|
|
|
**확인 사항**:
|
|
- [ ] 5초마다 제안사항이 추가됨
|
|
- [ ] 총 5개의 제안사항이 표시됨
|
|
- [ ] 타임스탬프, 신뢰도가 정상 표시됨
|
|
- [ ] "추가" 버튼 클릭 시 회의록에 추가됨
|
|
- [ ] "무시" 버튼 클릭 시 제안사항이 제거됨
|
|
|
|
### 2. 실제 모드 테스트 (STT 완료 후)
|
|
|
|
```bash
|
|
# AI 서비스 시작
|
|
cd ai-python && ./start.sh
|
|
|
|
# 실제 모드로 실행
|
|
REACT_APP_USE_MOCK_AI=false npm start
|
|
```
|
|
|
|
**확인 사항**:
|
|
- [ ] SSE 연결이 정상적으로 됨
|
|
- [ ] 실제 AI 제안사항이 수신됨
|
|
- [ ] 회의 진행에 따라 동적으로 제안사항 생성됨
|
|
|
|
---
|
|
|
|
## 📊 Mock vs Real 비교
|
|
|
|
| 항목 | Mock 모드 | Real 모드 |
|
|
|------|----------|----------|
|
|
| **백엔드 필요** | 불필요 | 필요 (AI 서비스) |
|
|
| **제안 타이밍** | 5초 고정 간격 | 회의 진행에 따라 동적 |
|
|
| **제안 개수** | 5개 고정 | 무제한 (회의 종료까지) |
|
|
| **데이터 품질** | 하드코딩 샘플 | Claude AI 실제 분석 |
|
|
| **네트워크 필요** | 불필요 | 필요 |
|
|
| **개발 속도** | 빠름 | 느림 (백엔드 의존) |
|
|
|
|
---
|
|
|
|
## ⚠️ 주의사항
|
|
|
|
### 1. Mock 데이터 관리
|
|
|
|
```javascript
|
|
// ❌ 나쁜 예: 컴포넌트 내부에 하드코딩
|
|
function Component() {
|
|
const mockData = [/* ... */]; // 재사용 불가
|
|
}
|
|
|
|
// ✅ 좋은 예: 별도 파일로 분리
|
|
// src/mocks/aiSuggestions.ts
|
|
export const MOCK_AI_SUGGESTIONS = [/* ... */];
|
|
```
|
|
|
|
### 2. 환경 변수 누락 방지
|
|
|
|
```javascript
|
|
// ❌ 나쁜 예: 하드코딩
|
|
const USE_MOCK = true;
|
|
|
|
// ✅ 좋은 예: 환경 변수 + 기본값
|
|
const USE_MOCK = process.env.REACT_APP_USE_MOCK_AI !== 'false';
|
|
```
|
|
|
|
### 3. 프로덕션 빌드 시 Mock 제거
|
|
|
|
```javascript
|
|
// ❌ 나쁜 예: 프로덕션에도 Mock 코드 포함
|
|
if (USE_MOCK) { /* mock logic */ }
|
|
|
|
// ✅ 좋은 예: Tree-shaking 가능하도록 작성
|
|
if (process.env.NODE_ENV !== 'production' && USE_MOCK) {
|
|
/* mock logic */
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 다음 단계
|
|
|
|
### Phase 1: Mock으로 UI 개발 (현재)
|
|
- ✅ Mock 데이터 함수 구현
|
|
- ✅ UI 컴포넌트 개발
|
|
- ✅ 사용자 인터랙션 구현
|
|
|
|
### Phase 2: STT 연동 대기 (진행 중)
|
|
- 🔄 Backend에서 STT 개발 중
|
|
- 🔄 Event Hub 연동 개발 중
|
|
|
|
### Phase 3: 실제 연동 (STT 완료 후)
|
|
- [ ] Mock → Real 모드 전환
|
|
- [ ] 통합 테스트
|
|
- [ ] 성능 최적화
|
|
|
|
---
|
|
|
|
## 📞 문의
|
|
|
|
**Mock 데이터 관련**: 프론트엔드팀 (유진)
|
|
**STT 개발 현황**: 백엔드팀 (준호)
|
|
**AI 서비스**: AI팀 (서연)
|
|
|
|
---
|
|
|
|
**최종 업데이트**: 2025-10-27
|