mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 11:26:25 +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 (상세 결과서)
12 KiB
12 KiB
AI 서비스 프론트엔드 통합 가이드
개요
AI 서비스의 실시간 제안사항 API를 프론트엔드에서 사용하기 위한 통합 가이드입니다.
⚠️ 중요: AI 서비스가 **Python (FastAPI)**로 마이그레이션 되었습니다.
- 기존 포트: 8083 (Java Spring Boot) → 새 포트: 8086 (Python FastAPI)
- 엔드포인트 경로:
/api/suggestions/...→/api/v1/ai/suggestions/...
1. API 정보
엔드포인트
GET /api/v1/ai/suggestions/meetings/{meetingId}/stream
변경 사항:
- ✅ 새 경로 (Python):
/api/v1/ai/suggestions/meetings/{meetingId}/stream - ❌ 구 경로 (Java):
/api/suggestions/meetings/{meetingId}/stream
메서드
- HTTP Method: GET
- Content-Type: text/event-stream (SSE)
- 인증: 개발 환경에서는 불필요 (운영 환경에서는 JWT 필요)
파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| meetingId | string (UUID) | 필수 | 회의 고유 ID |
예시
# Python (새 버전)
http://localhost:8087/api/v1/ai/suggestions/meetings/550e8400-e29b-41d4-a716-446655440000/stream
# Java (구 버전 - 사용 중단 예정)
http://localhost:8083/api/suggestions/meetings/550e8400-e29b-41d4-a716-446655440000/stream
2. 응답 데이터 구조
SSE 이벤트 형식
event: ai-suggestion
id: 123456789
data: {"suggestions":[...]}
데이터 스키마 (JSON)
interface RealtimeSuggestionsDto {
suggestions: SimpleSuggestionDto[];
}
interface SimpleSuggestionDto {
id: string; // 제안 고유 ID (예: "suggestion-1")
content: string; // 제안 내용 (예: "신제품의 타겟 고객층...")
timestamp: string; // 시간 정보 (HH:MM:SS 형식, 예: "00:05:23")
confidence: number; // 신뢰도 점수 (0.0 ~ 1.0)
}
샘플 응답
{
"suggestions": [
{
"id": "suggestion-1",
"content": "신제품의 타겟 고객층을 20-30대로 설정하고, 모바일 우선 전략을 취하기로 논의 중입니다.",
"timestamp": "00:05:23",
"confidence": 0.92
}
]
}
3. 프론트엔드 구현 방법
3.1 EventSource로 연결
// 회의 ID (실제로는 회의 생성 API에서 받아야 함)
const meetingId = '550e8400-e29b-41d4-a716-446655440000';
// SSE 연결 (Python 버전)
const apiUrl = `http://localhost:8087/api/v1/ai/suggestions/meetings/${meetingId}/stream`;
const eventSource = new EventSource(apiUrl);
// 연결 성공
eventSource.onopen = function(event) {
console.log('SSE 연결 성공');
};
// ai-suggestion 이벤트 수신
eventSource.addEventListener('ai-suggestion', function(event) {
const data = JSON.parse(event.data);
const suggestions = data.suggestions;
suggestions.forEach(suggestion => {
console.log('제안:', suggestion.content);
addSuggestionToUI(suggestion);
});
});
// 에러 처리
eventSource.onerror = function(error) {
console.error('SSE 연결 오류:', error);
eventSource.close();
};
3.2 UI에 제안사항 추가
function addSuggestionToUI(suggestion) {
const container = document.getElementById('aiSuggestionList');
// 중복 방지
if (document.getElementById(`suggestion-${suggestion.id}`)) {
return;
}
// HTML 생성
const html = `
<div class="ai-suggestion-card" id="suggestion-${suggestion.id}">
<div class="ai-suggestion-header">
<span class="ai-suggestion-time">${escapeHtml(suggestion.timestamp)}</span>
<button onclick="handleAddToMemo('${escapeHtml(suggestion.content)}')">
➕
</button>
</div>
<div class="ai-suggestion-text">
${escapeHtml(suggestion.content)}
</div>
</div>
`;
container.insertAdjacentHTML('beforeend', html);
}
3.3 XSS 방지
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
}
3.4 연결 종료
// 페이지 종료 시 또는 회의 종료 시
window.addEventListener('beforeunload', function() {
if (eventSource) {
eventSource.close();
}
});
4. React 통합 예시
4.1 Custom Hook
import { useEffect, useState } from 'react';
interface Suggestion {
id: string;
content: string;
timestamp: string;
confidence: number;
}
export function useAiSuggestions(meetingId: string) {
const [suggestions, setSuggestions] = useState<Suggestion[]>([]);
const [isConnected, setIsConnected] = useState(false);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const apiUrl = `http://localhost:8087/api/v1/ai/suggestions/meetings/${meetingId}/stream`;
const eventSource = new EventSource(apiUrl);
eventSource.onopen = () => {
setIsConnected(true);
setError(null);
};
eventSource.addEventListener('ai-suggestion', (event) => {
try {
const data = JSON.parse(event.data);
setSuggestions(prev => [...prev, ...data.suggestions]);
} catch (err) {
setError(err as Error);
}
});
eventSource.onerror = (err) => {
setError(new Error('SSE connection failed'));
setIsConnected(false);
eventSource.close();
};
return () => {
eventSource.close();
setIsConnected(false);
};
}, [meetingId]);
return { suggestions, isConnected, error };
}
4.2 Component 사용
function MeetingPage({ meetingId }: { meetingId: string }) {
const { suggestions, isConnected, error } = useAiSuggestions(meetingId);
if (error) {
return <div>Error: {error.message}</div>;
}
return (
<div>
<div>연결 상태: {isConnected ? '연결됨' : '연결 안 됨'}</div>
<div className="suggestions-list">
{suggestions.map(suggestion => (
<div key={suggestion.id} className="suggestion-card">
<span className="timestamp">{suggestion.timestamp}</span>
<p>{suggestion.content}</p>
<button onClick={() => addToMemo(suggestion.content)}>
메모에 추가
</button>
</div>
))}
</div>
</div>
);
}
5. 환경별 설정
5.1 개발 환경
// Python 버전 (권장)
const API_BASE_URL = 'http://localhost:8087';
// Java 버전 (구버전 - 사용 중단 예정)
// const API_BASE_URL = 'http://localhost:8083';
5.2 테스트 환경
const API_BASE_URL = 'https://test-api.hgzero.com';
5.3 운영 환경
// 같은 도메인에서 실행될 경우
const API_BASE_URL = '';
// 또는 환경변수 사용
const API_BASE_URL = process.env.REACT_APP_AI_API_URL;
6. 에러 처리
6.1 연결 실패
eventSource.onerror = function(error) {
console.error('SSE 연결 실패:', error);
// 사용자에게 알림
showErrorNotification('AI 제안사항을 받을 수 없습니다. 다시 시도해주세요.');
// 재연결 시도 (옵션)
setTimeout(() => {
reconnect();
}, 5000);
};
6.2 파싱 오류
try {
const data = JSON.parse(event.data);
} catch (error) {
console.error('데이터 파싱 오류:', error);
console.error('원본 데이터:', event.data);
// Sentry 등 에러 모니터링 서비스에 전송
reportError(error, { eventData: event.data });
}
6.3 네트워크 오류
// Timeout 설정 (EventSource는 기본 타임아웃 없음)
const connectionTimeout = setTimeout(() => {
if (!isConnected) {
console.error('연결 타임아웃');
eventSource.close();
handleConnectionTimeout();
}
}, 10000); // 10초
eventSource.onopen = function() {
clearTimeout(connectionTimeout);
setIsConnected(true);
};
7. 운영 환경 배포 시 변경 사항
7.1 인증 헤더 추가 (운영 환경)
⚠️ 중요: 개발 환경에서는 인증이 해제되어 있지만, 운영 환경에서는 JWT 토큰이 필요합니다.
// EventSource는 헤더를 직접 설정할 수 없으므로 URL에 토큰 포함
const token = getAccessToken();
const apiUrl = `${API_BASE_URL}/api/suggestions/meetings/${meetingId}/stream?token=${token}`;
// 또는 fetch API + ReadableStream 사용 (권장)
const response = await fetch(apiUrl, {
headers: {
'Authorization': `Bearer ${token}`
}
});
const reader = response.body.getReader();
// SSE 파싱 로직 구현
7.2 CORS 설정 확인
운영 환경 도메인이 백엔드 CORS 설정에 포함되어 있는지 확인:
# application.yml
cors:
allowed-origins: https://your-production-domain.com
8. AI 개발 완료 후 변경 사항
8.1 제거할 백엔드 코드
- SuggestionService.java:102 - Mock 데이터 발행 호출
- SuggestionService.java:192-236 - Mock 메서드 전체
- SecurityConfig.java:49 - 인증 해제 설정
8.2 프론트엔드는 변경 불필요
- SSE 연결 코드는 그대로 유지
- API URL만 운영 환경에 맞게 수정
- JWT 토큰 추가 (위 7.1 참고)
8.3 실제 AI 동작 방식 (예상)
STT 텍스트 생성 → Event Hub 전송 → AI 서비스 수신 →
텍스트 축적 (Redis) → 임계값 도달 → Claude API 분석 →
SSE로 제안사항 발행 → 프론트엔드 수신
현재 Mock은 5초, 10초, 15초에 발행하지만, 실제 AI는 회의 진행 상황에 따라 동적으로 발행됩니다.
9. 알려진 제한사항
9.1 브라우저 호환성
- EventSource는 IE 미지원 (Edge, Chrome, Firefox, Safari는 지원)
- 필요 시 Polyfill 사용:
event-source-polyfill
9.2 연결 제한
- 동일 도메인에 대한 SSE 연결은 브라우저당 6개로 제한
- 여러 탭에서 동시 접속 시 주의
9.3 재연결
- EventSource는 자동 재연결을 시도하지만, 서버에서 연결을 끊으면 재연결 안 됨
- 수동 재연결 로직 구현 권장
9.4 Mock 데이터 특성
- 개발 환경 전용: 3개 제안 후 자동 종료
- 실제 AI: 회의 진행 중 계속 발행, 회의 종료 시까지 연결 유지
10. 테스트 방법
10.1 로컬 테스트
# 1. AI 서비스 실행
python3 tools/run-intellij-service-profile.py ai
# 2. HTTP 서버 실행 (file:// 프로토콜은 CORS 제한)
cd design/uiux/prototype
python3 -m http.server 8000
# 3. 브라우저에서 접속
open http://localhost:8000/05-회의진행.html
10.2 디버깅
// 브라우저 개발자 도구 Console 탭에서 확인
// [DEBUG] 로그로 상세 정보 출력
// [ERROR] 로그로 에러 추적
10.3 curl 테스트
# Python 버전 (새 포트)
curl -N http://localhost:8087/api/v1/ai/suggestions/meetings/test-meeting/stream
# Java 버전 (구 포트 - 사용 중단 예정)
# curl -N http://localhost:8083/api/suggestions/meetings/550e8400-e29b-41d4-a716-446655440000/stream
11. 참고 문서
12. FAQ
Q1. 왜 EventSource를 사용하나요?
A: WebSocket보다 단방향 통신에 적합하고, 자동 재연결 기능이 있으며, 구현이 간단합니다.
Q2. 제안사항이 중복으로 표시되는 경우?
A: addSuggestionToUI 함수에 중복 체크 로직이 있는지 확인하세요.
Q3. 연결은 되는데 데이터가 안 오는 경우?
A:
- 백엔드 로그 확인 (
ai/logs/ai-service.log) - Network 탭에서
stream요청 확인 ai-suggestion이벤트 리스너가 등록되었는지 확인
Q4. 운영 환경에서 401 Unauthorized 에러?
A: JWT 토큰이 필요합니다. 7.1절 "인증 헤더 추가" 참고.
문서 이력
| 버전 | 작성일 | 작성자 | 변경 내용 |
|---|---|---|---|
| 1.0 | 2025-10-27 | 준호 (Backend), 유진 (Frontend) | 초안 작성 |