hgzero/develop/dev/dev-frontend-mock-guide.md
Minseo-Jo 9bf3597cec AI 서비스 Python 마이그레이션 및 프론트엔드 연동 문서 추가
주요 변경사항:
- AI 서비스 Java → Python (FastAPI) 완전 마이그레이션
- 포트 변경: 8083 → 8086
- SSE 스트리밍 기능 구현 및 테스트 완료
- Claude API 연동 (claude-3-5-sonnet-20241022)
- Redis 슬라이딩 윈도우 방식 텍스트 축적
- Azure Event Hub 연동 준비 (STT 텍스트 수신)

프론트엔드 연동 지원:
- API 연동 가이드 업데이트 (Python 버전 반영)
- Mock 데이터 개발 가이드 신규 작성
- STT 개발 완료 전까지 Mock 데이터로 UI 개발 가능

기술 스택:
- Python 3.13
- FastAPI 0.104.1
- Anthropic Claude API 0.42.0
- Redis (asyncio) 5.0.1
- Azure Event Hub 5.11.4
- Pydantic 2.10.5

테스트 결과:
-  서비스 시작 정상
-  헬스 체크 성공
-  SSE 스트리밍 동작 확인
-  Redis 연결 정상

다음 단계:
- STT (Azure Speech) 서비스 연동 개발
- Event Hub를 통한 실시간 텍스트 수신
- E2E 통합 테스트 (STT → AI → Frontend)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 11:52:30 +09:00

9.3 KiB

프론트엔드 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 함수 (권장)

장점: 백엔드 없이 완전 독립 개발 가능

/**
 * 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: 환경 변수로 전환

// 환경 변수로 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:8086/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 파일

# Mock 모드 사용 (개발 중)
REACT_APP_USE_MOCK_AI=true

# 실제 AI 서비스 URL (STT 완료 후)
REACT_APP_AI_SERVICE_URL=http://localhost:8086

package.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 전체 예시

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 모드 테스트

# Mock 모드로 실행
REACT_APP_USE_MOCK_AI=true npm start

확인 사항:

  • 5초마다 제안사항이 추가됨
  • 총 5개의 제안사항이 표시됨
  • 타임스탬프, 신뢰도가 정상 표시됨
  • "추가" 버튼 클릭 시 회의록에 추가됨
  • "무시" 버튼 클릭 시 제안사항이 제거됨

2. 실제 모드 테스트 (STT 완료 후)

# 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 데이터 관리

// ❌ 나쁜 예: 컴포넌트 내부에 하드코딩
function Component() {
  const mockData = [/* ... */];  // 재사용 불가
}

// ✅ 좋은 예: 별도 파일로 분리
// src/mocks/aiSuggestions.ts
export const MOCK_AI_SUGGESTIONS = [/* ... */];

2. 환경 변수 누락 방지

// ❌ 나쁜 예: 하드코딩
const USE_MOCK = true;

// ✅ 좋은 예: 환경 변수 + 기본값
const USE_MOCK = process.env.REACT_APP_USE_MOCK_AI !== 'false';

3. 프로덕션 빌드 시 Mock 제거

// ❌ 나쁜 예: 프로덕션에도 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