mirror of
https://github.com/hwanny1128/HGZero.git
synced 2026-06-13 07:09:09 +00:00
STT-AI 통합 작업 진행 중 변경사항 커밋
- AI 서비스 CORS 설정 업데이트 - 회의 진행 프로토타입 수정 - 빌드 리포트 및 로그 파일 업데이트 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -712,7 +712,9 @@
|
||||
</h4>
|
||||
|
||||
<div id="aiSuggestionList">
|
||||
<!-- AI 제안 1 -->
|
||||
<!-- AI 제안사항이 실시간으로 추가됩니다 -->
|
||||
|
||||
<!-- 백업용 정적 샘플 데이터 (SSE 연결 실패 시 표시)
|
||||
<div class="ai-suggestion-card" id="suggestion-1">
|
||||
<div class="ai-suggestion-header">
|
||||
<span class="ai-suggestion-time">00:05:23</span>
|
||||
@@ -725,7 +727,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI 제안 2 -->
|
||||
<div class="ai-suggestion-card" id="suggestion-2">
|
||||
<div class="ai-suggestion-header">
|
||||
<span class="ai-suggestion-time">00:08:45</span>
|
||||
@@ -738,7 +739,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI 제안 3 -->
|
||||
<div class="ai-suggestion-card" id="suggestion-3">
|
||||
<div class="ai-suggestion-header">
|
||||
<span class="ai-suggestion-time">00:12:18</span>
|
||||
@@ -750,6 +750,7 @@
|
||||
마케팅 예산 배분에 대해 SNS 광고 60%, 인플루언서 마케팅 40%로 의견이 나왔으나 추가 검토 필요
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1022,9 +1023,131 @@
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 페이지 로드 시 타이머 시작
|
||||
// SSE로 실시간 AI 제안사항 수신
|
||||
let eventSource = null;
|
||||
const meetingId = '550e8400-e29b-41d4-a716-446655440000'; // 테스트용 회의 ID
|
||||
|
||||
function connectAiSuggestionStream() {
|
||||
// EventSource를 사용하여 SSE 연결
|
||||
const apiUrl = `http://localhost:8083/api/suggestions/meetings/${meetingId}/stream`;
|
||||
|
||||
console.log('[DEBUG] SSE 연결 시작:', apiUrl);
|
||||
|
||||
eventSource = new EventSource(apiUrl);
|
||||
|
||||
// 연결 성공
|
||||
eventSource.onopen = function(event) {
|
||||
console.log('[SUCCESS] SSE 연결 성공!', event);
|
||||
};
|
||||
|
||||
// 모든 이벤트 수신 (디버깅용)
|
||||
eventSource.onmessage = function(event) {
|
||||
console.log('[DEBUG] 일반 메시지 수신:', event.data);
|
||||
};
|
||||
|
||||
// ai-suggestion 이벤트 수신
|
||||
eventSource.addEventListener('ai-suggestion', function(event) {
|
||||
console.log('[SUCCESS] AI 제안사항 수신:', event.data);
|
||||
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
const suggestions = data.suggestions;
|
||||
|
||||
console.log('[DEBUG] 파싱된 데이터:', data);
|
||||
console.log('[DEBUG] 제안사항 개수:', suggestions ? suggestions.length : 0);
|
||||
|
||||
if (suggestions && suggestions.length > 0) {
|
||||
suggestions.forEach(suggestion => {
|
||||
console.log('[DEBUG] 제안사항 추가 중:', suggestion);
|
||||
addAiSuggestionToUI(suggestion);
|
||||
});
|
||||
} else {
|
||||
console.warn('[WARNING] 제안사항이 비어있음');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[ERROR] AI 제안사항 파싱 오류:', error);
|
||||
console.error('[ERROR] 원본 데이터:', event.data);
|
||||
}
|
||||
});
|
||||
|
||||
// 에러 처리
|
||||
eventSource.onerror = function(error) {
|
||||
console.error('[ERROR] SSE 연결 오류:', error);
|
||||
console.error('[ERROR] ReadyState:', eventSource.readyState);
|
||||
|
||||
// ReadyState: 0=CONNECTING, 1=OPEN, 2=CLOSED
|
||||
if (eventSource.readyState === EventSource.CLOSED) {
|
||||
console.error('[ERROR] SSE 연결이 닫혔습니다');
|
||||
} else if (eventSource.readyState === EventSource.CONNECTING) {
|
||||
console.warn('[WARNING] SSE 재연결 시도 중...');
|
||||
}
|
||||
|
||||
// 에러 발생 시 닫기
|
||||
eventSource.close();
|
||||
};
|
||||
|
||||
console.log('[INFO] AI 제안사항 SSE 스트림 연결 요청 완료');
|
||||
}
|
||||
|
||||
// AI 제안사항을 UI에 추가
|
||||
function addAiSuggestionToUI(suggestion) {
|
||||
const listContainer = document.getElementById('aiSuggestionList');
|
||||
|
||||
// 고유 ID 생성 (이미 추가된 제안인지 확인용)
|
||||
const cardId = `suggestion-${suggestion.id}`;
|
||||
|
||||
// 이미 존재하는 제안이면 추가하지 않음
|
||||
if (document.getElementById(cardId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// AI 제안 카드 HTML 생성
|
||||
const cardHtml = `
|
||||
<div class="ai-suggestion-card" id="${cardId}">
|
||||
<div class="ai-suggestion-header">
|
||||
<span class="ai-suggestion-time">${suggestion.timestamp}</span>
|
||||
<button class="ai-suggestion-add-btn"
|
||||
onclick="addToMemo('${escapeHtml(suggestion.content)}', document.getElementById('${cardId}'))"
|
||||
title="메모에 추가">
|
||||
➕
|
||||
</button>
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
${escapeHtml(suggestion.content)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 리스트에 추가
|
||||
listContainer.insertAdjacentHTML('beforeend', cardHtml);
|
||||
|
||||
console.log('AI 제안사항 추가됨:', suggestion.content);
|
||||
}
|
||||
|
||||
// HTML 이스케이프 (XSS 방지)
|
||||
function escapeHtml(text) {
|
||||
const map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return text.replace(/[&<>"']/g, m => map[m]);
|
||||
}
|
||||
|
||||
// 페이지 로드 시 타이머 시작 및 SSE 연결
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
updateTimer();
|
||||
connectAiSuggestionStream();
|
||||
});
|
||||
|
||||
// 페이지 종료 시 SSE 연결 해제
|
||||
window.addEventListener('beforeunload', function() {
|
||||
if (eventSource) {
|
||||
eventSource.close();
|
||||
console.log('SSE 연결 종료');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* AI 제안사항 SSE 연동 예시
|
||||
* 05-회의진행.html에 추가할 JavaScript 코드
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// 1. 전역 변수 선언
|
||||
// ============================================
|
||||
let eventSource = null;
|
||||
const meetingId = "test-meeting-001"; // 실제로는 URL 파라미터에서 가져옴
|
||||
|
||||
// ============================================
|
||||
// 2. SSE 연결 초기화
|
||||
// ============================================
|
||||
function initializeAiSuggestions() {
|
||||
console.log('AI 제안사항 SSE 연결 시작 - meetingId:', meetingId);
|
||||
|
||||
// SSE 연결
|
||||
eventSource = new EventSource(
|
||||
`http://localhost:8083/api/suggestions/meetings/${meetingId}/stream`
|
||||
);
|
||||
|
||||
// 연결 성공
|
||||
eventSource.onopen = function() {
|
||||
console.log('SSE 연결 성공');
|
||||
};
|
||||
|
||||
// AI 제안사항 수신
|
||||
eventSource.addEventListener('ai-suggestion', function(event) {
|
||||
console.log('AI 제안사항 수신:', event.data);
|
||||
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
handleAiSuggestions(data);
|
||||
} catch (error) {
|
||||
console.error('JSON 파싱 오류:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 연결 오류
|
||||
eventSource.onerror = function(error) {
|
||||
console.error('SSE 연결 오류:', error);
|
||||
|
||||
// 자동 재연결은 브라우저가 처리
|
||||
// 필요시 수동 재연결 로직 추가 가능
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 3. AI 제안사항 처리
|
||||
// ============================================
|
||||
function handleAiSuggestions(data) {
|
||||
console.log('AI 제안사항 처리:', data);
|
||||
|
||||
// data 형식:
|
||||
// {
|
||||
// "suggestions": [
|
||||
// {
|
||||
// "id": "sugg-001",
|
||||
// "content": "신제품의 타겟 고객층을...",
|
||||
// "timestamp": "00:05:23",
|
||||
// "confidence": 0.92
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
|
||||
if (data.suggestions && data.suggestions.length > 0) {
|
||||
data.suggestions.forEach(suggestion => {
|
||||
addSuggestionCard(suggestion);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 4. 제안사항 카드 추가
|
||||
// ============================================
|
||||
function addSuggestionCard(suggestion) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'ai-suggestion-card';
|
||||
card.id = 'suggestion-' + suggestion.id;
|
||||
|
||||
// 타임스탬프 (있으면 사용, 없으면 현재 시간)
|
||||
const timestamp = suggestion.timestamp || getCurrentRecordingTime();
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="ai-suggestion-header">
|
||||
<span class="ai-suggestion-time">${timestamp}</span>
|
||||
<button class="ai-suggestion-add-btn"
|
||||
onclick="addToMemo('${escapeHtml(suggestion.content)}', document.getElementById('suggestion-${suggestion.id}'))"
|
||||
title="메모에 추가">
|
||||
➕
|
||||
</button>
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
${escapeHtml(suggestion.content)}
|
||||
</div>
|
||||
${suggestion.confidence ? `
|
||||
<div class="ai-suggestion-confidence">
|
||||
<span style="font-size: 11px; color: var(--gray-500);">
|
||||
신뢰도: ${Math.round(suggestion.confidence * 100)}%
|
||||
</span>
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
|
||||
// aiSuggestionList의 맨 위에 추가 (최신 항목이 위로)
|
||||
const listElement = document.getElementById('aiSuggestionList');
|
||||
if (listElement) {
|
||||
listElement.insertBefore(card, listElement.firstChild);
|
||||
|
||||
// 부드러운 등장 애니메이션
|
||||
setTimeout(() => {
|
||||
card.style.opacity = '0';
|
||||
card.style.transform = 'translateY(-10px)';
|
||||
card.style.transition = 'all 0.3s ease';
|
||||
|
||||
setTimeout(() => {
|
||||
card.style.opacity = '1';
|
||||
card.style.transform = 'translateY(0)';
|
||||
}, 10);
|
||||
}, 0);
|
||||
} else {
|
||||
console.error('aiSuggestionList 엘리먼트를 찾을 수 없습니다.');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 5. 유틸리티 함수
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 현재 녹음 시간 가져오기 (HH:MM 형식)
|
||||
*/
|
||||
function getCurrentRecordingTime() {
|
||||
const timerElement = document.getElementById('recordingTime');
|
||||
if (timerElement) {
|
||||
const time = timerElement.textContent;
|
||||
return time.substring(0, 5); // "00:05:23" -> "00:05"
|
||||
}
|
||||
return "00:00";
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML 이스케이프 (XSS 방지)
|
||||
*/
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSE 연결 종료
|
||||
*/
|
||||
function closeAiSuggestions() {
|
||||
if (eventSource) {
|
||||
console.log('SSE 연결 종료');
|
||||
eventSource.close();
|
||||
eventSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 6. 페이지 로드 시 자동 시작
|
||||
// ============================================
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('페이지 로드 완료 - AI 제안사항 초기화');
|
||||
|
||||
// SSE 연결 시작
|
||||
initializeAiSuggestions();
|
||||
|
||||
// 페이지 닫을 때 SSE 연결 종료
|
||||
window.addEventListener('beforeunload', function() {
|
||||
closeAiSuggestions();
|
||||
});
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 7. 회의 종료 시 SSE 연결 종료
|
||||
// ============================================
|
||||
// 기존 endMeeting 함수 수정
|
||||
const originalEndMeeting = window.endMeeting;
|
||||
window.endMeeting = function() {
|
||||
closeAiSuggestions(); // SSE 연결 종료
|
||||
if (originalEndMeeting) {
|
||||
originalEndMeeting(); // 기존 로직 실행
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user