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:
Minseo-Jo
2025-10-27 13:17:47 +09:00
parent 9bf3597cec
commit 14d03dcacf
31 changed files with 9531 additions and 1036 deletions
+127 -4
View File
@@ -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 = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
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
View File
@@ -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(); // 기존 로직 실행
}
};