# AI 샘플 데이터 통합 가이드 ## 개요 AI 서비스 개발이 완료되지 않은 상황에서 프론트엔드 개발을 병행하기 위해 **샘플 데이터 자동 발행 기능**을 구현했습니다. ### 목적 - 프론트엔드 개발자가 AI 기능 완성을 기다리지 않고 화면 개발 가능 - 실시간 SSE(Server-Sent Events) 스트리밍 동작 테스트 - 회의 진행 중 AI 제안사항 표시 기능 검증 ### 주요 기능 - **백엔드**: AI Service에서 5초마다 샘플 제안사항 3개 자동 발행 - **프론트엔드**: EventSource API를 통한 실시간 데이터 수신 및 화면 표시 --- ## 1. 백엔드 구현 ### 1.1 수정 파일 - **파일**: `ai/src/main/java/com/unicorn/hgzero/ai/biz/service/SuggestionService.java` - **수정 내용**: `startMockDataEmission()` 메서드 추가 ### 1.2 구현 내용 #### Mock 데이터 자동 발행 메서드 ```java /** * TODO: AI 개발 완료 후 제거 * Mock 데이터 자동 발행 (프론트엔드 개발용) * 5초마다 샘플 제안사항을 발행합니다. */ private void startMockDataEmission(String meetingId, Sinks.Many sink) { // 프론트엔드 HTML에 맞춘 샘플 데이터 (3개) List mockSuggestions = List.of( SimpleSuggestionDto.builder() .id("suggestion-1") .content("신제품의 타겟 고객층을 20-30대로 설정하고, 모바일 우선 전략을 취하기로 논의 중입니다.") .timestamp("00:05:23") .confidence(0.92) .build(), // ... 3개의 샘플 데이터 ); // 5초마다 하나씩 발행 (총 3개) Flux.interval(Duration.ofSeconds(5)) .take(3) .map(index -> { SimpleSuggestionDto suggestion = mockSuggestions.get(index.intValue()); return RealtimeSuggestionsDto.builder() .suggestions(List.of(suggestion)) .build(); }) .subscribe( suggestions -> { sink.tryEmitNext(suggestions); log.info("Mock 제안사항 발행 완료"); } ); } ``` #### SSE 스트리밍 메서드 수정 ```java @Override public Flux streamRealtimeSuggestions(String meetingId) { // Sink 생성 Sinks.Many sink = Sinks.many() .multicast() .onBackpressureBuffer(); meetingSinks.put(meetingId, sink); // TODO: AI 개발 완료 후 제거 - Mock 데이터 자동 발행 startMockDataEmission(meetingId, sink); return sink.asFlux() .doOnCancel(() -> { meetingSinks.remove(meetingId); cleanupMeetingData(meetingId); }); } ``` ### 1.3 샘플 데이터 구조 ```json { "suggestions": [ { "id": "suggestion-1", "content": "신제품의 타겟 고객층을 20-30대로 설정하고, 모바일 우선 전략을 취하기로 논의 중입니다.", "timestamp": "00:05:23", "confidence": 0.92 } ] } ``` --- ## 2. 프론트엔드 구현 ### 2.1 수정 파일 - **파일**: `design/uiux/prototype/05-회의진행.html` - **수정 내용**: SSE 연결 및 실시간 데이터 수신 코드 추가 ### 2.2 구현 내용 #### SSE 연결 함수 ```javascript function connectAiSuggestionStream() { const apiUrl = `http://localhost:8082/api/suggestions/meetings/${meetingId}/stream`; eventSource = new EventSource(apiUrl); eventSource.addEventListener('ai-suggestion', function(event) { const data = JSON.parse(event.data); const suggestions = data.suggestions; if (suggestions && suggestions.length > 0) { suggestions.forEach(suggestion => { addAiSuggestionToUI(suggestion); }); } }); eventSource.onerror = function(error) { console.error('SSE 연결 오류:', error); eventSource.close(); }; } ``` #### UI 추가 함수 ```javascript function addAiSuggestionToUI(suggestion) { const listContainer = document.getElementById('aiSuggestionList'); const cardId = `suggestion-${suggestion.id}`; // 중복 방지 if (document.getElementById(cardId)) { return; } // AI 제안 카드 HTML 생성 const cardHtml = `
${suggestion.timestamp}
${escapeHtml(suggestion.content)}
`; listContainer.insertAdjacentHTML('beforeend', cardHtml); } ``` #### XSS 방지 ```javascript function escapeHtml(text) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text.replace(/[&<>"']/g, m => map[m]); } ``` --- ## 3. 테스트 방법 ### 3.1 서비스 실행 #### 1단계: AI 서비스 실행 ```bash # IntelliJ 실행 프로파일 사용 python3 tools/run-intellij-service-profile.py ai # 또는 직접 실행 cd ai ./gradlew bootRun ``` **실행 확인**: - 포트: `8082` - 로그 확인: `ai/logs/ai-service.log` #### 2단계: 프론트엔드 HTML 열기 ```bash # 브라우저에서 직접 열기 open design/uiux/prototype/05-회의진행.html # 또는 HTTP 서버 실행 cd design/uiux/prototype python3 -m http.server 8000 # 브라우저: http://localhost:8000/05-회의진행.html ``` ### 3.2 동작 확인 #### 브라우저 콘솔 확인 1. 개발자 도구 열기 (F12) 2. Console 탭 확인 **예상 로그**: ``` AI 제안사항 SSE 스트림 연결됨: http://localhost:8082/api/suggestions/meetings/550e8400-e29b-41d4-a716-446655440000/stream AI 제안사항 수신: {"suggestions":[{"id":"suggestion-1", ...}]} AI 제안사항 추가됨: 신제품의 타겟 고객층을 20-30대로 설정하고... ``` #### 화면 동작 확인 1. **페이지 로드**: 회의진행.html 열기 2. **AI 제안 탭 클릭**: "AI 제안" 탭으로 이동 3. **5초 대기**: 첫 번째 제안사항 표시 4. **10초 대기**: 두 번째 제안사항 표시 5. **15초 대기**: 세 번째 제안사항 표시 #### 백엔드 로그 확인 ```bash tail -f ai/logs/ai-service.log ``` **예상 로그**: ``` 실시간 AI 제안사항 스트리밍 시작 - meetingId: 550e8400-e29b-41d4-a716-446655440000 Mock 데이터 자동 발행 시작 - meetingId: 550e8400-e29b-41d4-a716-446655440000 Mock 제안사항 발행 - meetingId: 550e8400-e29b-41d4-a716-446655440000, 제안: 신제품의 타겟 고객층... ``` ### 3.3 API 직접 테스트 (curl) ```bash # SSE 스트림 연결 curl -N http://localhost:8082/api/suggestions/meetings/550e8400-e29b-41d4-a716-446655440000/stream ``` **예상 응답**: ``` event: ai-suggestion id: 123456789 data: {"suggestions":[{"id":"suggestion-1","content":"신제품의 타겟 고객층...","timestamp":"00:05:23","confidence":0.92}]} event: ai-suggestion id: 987654321 data: {"suggestions":[{"id":"suggestion-2","content":"개발 일정...","timestamp":"00:08:45","confidence":0.88}]} ``` --- ## 4. CORS 설정 (필요 시) 프론트엔드를 다른 포트에서 실행할 경우 CORS 설정이 필요합니다. ### 4.1 application.yml 확인 ```yaml # ai/src/main/resources/application.yml spring: web: cors: allowed-origins: - http://localhost:8000 - http://localhost:3000 allowed-methods: - GET - POST - PUT - DELETE allowed-headers: - "*" allow-credentials: true ``` --- ## 5. 주의사항 ### 5.1 Mock 데이터 제거 시점 ⚠️ **AI 개발 완료 후 반드시 제거해야 할 코드**: #### 백엔드 (SuggestionService.java) ```java // TODO: AI 개발 완료 후 제거 - 이 줄 삭제 startMockDataEmission(meetingId, sink); // TODO: AI 개발 완료 후 제거 - 이 메서드 전체 삭제 private void startMockDataEmission(...) { ... } ``` #### 프론트엔드 (회의진행.html) - SSE 연결 코드는 **그대로 유지** - API URL만 실제 환경에 맞게 수정: ```javascript // 개발 환경 const apiUrl = `http://localhost:8082/api/suggestions/meetings/${meetingId}/stream`; // 운영 환경 (예시) const apiUrl = `/api/suggestions/meetings/${meetingId}/stream`; ``` ### 5.2 제한사항 1. **회의 ID 고정** - 현재 테스트용 회의 ID가 하드코딩됨 - 실제 환경에서는 회의 생성 API 응답에서 받아야 함 2. **샘플 데이터 개수** - 현재 3개로 제한 - 실제 AI는 회의 진행에 따라 동적으로 생성 3. **재연결 처리 없음** - SSE 연결이 끊어지면 재연결하지 않음 - 실제 환경에서는 재연결 로직 필요 4. **인증/인가 없음** - 현재 JWT 토큰 검증 없이 테스트 - 실제 환경에서는 인증 헤더 추가 필요 --- ## 6. 트러블슈팅 ### 문제 1: SSE 연결 안 됨 **증상**: 브라우저 콘솔에 "SSE 연결 오류" 표시 **해결 방법**: 1. AI 서비스가 실행 중인지 확인 ```bash curl http://localhost:8082/actuator/health ``` 2. CORS 설정 확인 3. 방화벽/포트 확인 ### 문제 2: 제안사항이 표시되지 않음 **증상**: SSE는 연결되지만 화면에 아무것도 표시되지 않음 **해결 방법**: 1. 브라우저 콘솔에서 에러 확인 2. Network 탭에서 SSE 이벤트 확인 3. 백엔드 로그 확인 ### 문제 3: 중복 제안사항 표시 **증상**: 같은 제안이 여러 번 표시됨 **해결 방법**: - 페이지 새로고침 (SSE 연결 재시작) - 브라우저 캐시 삭제 --- ## 7. 다음 단계 ### AI 개발 완료 후 작업 1. **Mock 코드 제거** - `startMockDataEmission()` 메서드 삭제 - 관련 TODO 주석 제거 2. **실제 AI 로직 연결** - Claude API 연동 - Event Hub 메시지 수신 - Redis 텍스트 축적 및 분석 3. **프론트엔드 개선** - 재연결 로직 추가 - 에러 핸들링 강화 - 로딩 상태 표시 4. **성능 최적화** - SSE 연결 풀 관리 - 메모리 누수 방지 - 네트워크 재시도 전략 --- ## 8. 관련 문서 - [AI Service API 설계서](../../design/backend/api/spec/ai-service-api-spec.md) - [SSE 스트리밍 가이드](dev-ai-realtime-streaming.md) - [백엔드 개발 가이드](dev-backend.md) --- ## 문서 이력 | 버전 | 작성일 | 작성자 | 변경 내용 | |------|--------|--------|----------| | 1.0 | 2025-10-27 | 준호 (Backend Developer) | 초안 작성 |