hgzero/develop/dev/dev-stt-batch-implementation.md
Minseo-Jo 0209652a90 STT 서비스 배치 방식 구현 완료
주요 구현사항:
- 5초마다 Redis 오디오 버퍼를 배치 처리하여 텍스트 변환
- WebSocket 실시간 오디오 수신 및 Redis Stream 저장
- Azure Speech Service 연동 (시뮬레이션 모드 포함)
- Event Hub 이벤트 발행 (AI 서비스 연동)

아키텍처:
Frontend (오디오 캡처)
  → WebSocket → STT Service
  → Redis Stream (버퍼)
  → @Scheduled(5초) 배치 처리
  → Azure Speech API
  → DB 저장 + Event Hub 발행
  → AI Service (텍스트 분석)

핵심 컴포넌트:
1. AudioWebSocketHandler
   - WebSocket 연결 관리
   - JSON/Binary 메시지 처리
   - Redis Stream에 오디오 저장

2. AudioBufferService
   - Redis Stream 오디오 버퍼링
   - 청크 조회 및 병합
   - 활성 회의 관리

3. AzureSpeechService
   - Azure Speech SDK 연동
   - 배치 단위 음성 인식
   - 시뮬레이션 모드 지원

4. AudioBatchProcessor
   - @Scheduled(5초) 배치 작업
   - 오디오 → 텍스트 변환
   - TranscriptSegment DB 저장
   - Event Hub 이벤트 발행

배치 방식의 장점:
 비용 최적화: Azure API 호출 1/5 감소
 문맥 이해: 5초 분량 한 번에 처리로 정확도 향상
 AI 효율: 일정량 텍스트 주기적 생성
 안정성: 재시도 로직 구현 용이

설정:
- Azure Speech: eastus, ko-KR
- Redis: 포트 6379, DB 3
- WebSocket: /ws/audio
- 배치 주기: 5초 (고정)

다음 단계:
- 프론트엔드 WebSocket 클라이언트 구현
- 실제 Azure Speech API 키 설정
- E2E 통합 테스트 (STT → AI → Frontend)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 13:39:22 +09:00

13 KiB

STT 서비스 배치 방식 구현 완료 보고서

작성일: 2025-10-27 작성자: 준호 (Backend Developer)


📋 개요

STT 서비스를 배치 처리 방식으로 구현 완료

  • 핵심: 5초마다 Redis에 축적된 오디오를 배치 처리하여 Azure Speech로 텍스트 변환
  • 장점: 비용 절감, 문맥 이해 향상, AI 분석 효율 증가
  • 기술: Java/Spring Boot, Azure Speech SDK, Redis Stream, WebSocket

🏗️ 최종 아키텍처

┌──────────────────────────────────────────────────────────┐
│                    Frontend (회의 화면)                   │
│  - 오디오 캡처 (매초)                                      │
│  - WebSocket으로 실시간 전송                               │
└────────────────────┬─────────────────────────────────────┘
                     │ WebSocket (ws://localhost:8084/ws/audio)
                     ↓
┌──────────────────────────────────────────────────────────┐
│             STT Service (Java/Spring Boot)                │
│                     포트: 8084                             │
├──────────────────────────────────────────────────────────┤
│  1. AudioWebSocketHandler                                 │
│     - WebSocket 메시지 수신 (JSON/Binary)                 │
│     - Base64 디코딩                                        │
│     ↓                                                      │
│  2. AudioBufferService                                    │
│     - Redis Stream에 오디오 청크 저장                     │
│     - Key: audio:stream:{meetingId}                       │
│     - TTL: 1분                                            │
│     ↓                                                      │
│  3. Redis Stream (버퍼)                                   │
│     - 오디오 청크 임시 저장                                │
│     - 5초 분량 축적                                        │
│     ↓                                                      │
│  4. AudioBatchProcessor (@Scheduled)                      │
│     - 5초마다 실행                                         │
│     - Redis에서 청크 조회 → 병합                          │
│     - Azure Speech API 호출                               │
│     - TranscriptSegment DB 저장                           │
│     - Event Hub 이벤트 발행                               │
└────────────────────┬─────────────────────────────────────┘
                     │ Event Hub (transcription-events)
                     ↓
┌──────────────────────────────────────────────────────────┐
│             AI Service (Python/FastAPI)                   │
│                     포트: 8086                             │
│  - Event Hub에서 텍스트 수신                              │
│  - Redis에 텍스트 축적 (슬라이딩 윈도우 5분)              │
│  - Claude API 분석 → SSE로 프론트엔드 전송               │
└──────────────────────────────────────────────────────────┘

🔧 구현 컴포넌트

1. Redis Stream 설정

파일: stt/src/main/java/com/unicorn/hgzero/stt/config/RedisStreamConfig.java

@Configuration
public class RedisStreamConfig {
    @Bean(name = "audioRedisTemplate")
    public RedisTemplate<String, byte[]> audioRedisTemplate(...) {
        // 오디오 데이터 저장용
    }
}

2. AudioChunkDto

파일: stt/src/main/java/com/unicorn/hgzero/stt/dto/AudioChunkDto.java

@Data
@Builder
public class AudioChunkDto {
    private String meetingId;
    private byte[] audioData;      // 오디오 데이터
    private Long timestamp;         // 타임스탬프 (밀리초)
    private Integer chunkIndex;     // 순서
    private String format;          // audio/webm
    private Integer sampleRate;     // 16000
}

3. AudioBufferService

파일: stt/src/main/java/com/unicorn/hgzero/stt/service/AudioBufferService.java

핵심 기능:

  • bufferAudioChunk(): 오디오 청크를 Redis Stream에 저장
  • getAudioChunks(): 회의별 오디오 청크 조회
  • mergeAudioChunks(): 청크 병합 (5초 분량)
  • clearProcessedChunks(): 처리 완료 후 Redis 정리

4. AudioWebSocketHandler

파일: stt/src/main/java/com/unicorn/hgzero/stt/controller/AudioWebSocketHandler.java

핵심 기능:

  • WebSocket 연결 관리
  • 텍스트 메시지 처리 (JSON 형식)
    • type: "start": 녹음 시작
    • type: "chunk": 오디오 청크 (Base64)
    • type: "stop": 녹음 종료
  • 바이너리 메시지 처리 (직접 오디오 데이터)

5. AzureSpeechService

파일: stt/src/main/java/com/unicorn/hgzero/stt/service/AzureSpeechService.java

핵심 기능:

  • recognizeAudio(): 오디오 데이터를 텍스트로 변환
  • Azure Speech SDK 사용
  • 시뮬레이션 모드 지원 (API 키 없을 때)

설정:

azure:
  speech:
    subscription-key: ${AZURE_SPEECH_SUBSCRIPTION_KEY:}
    region: ${AZURE_SPEECH_REGION:eastus}
    language: ko-KR

6. AudioBatchProcessor

파일: stt/src/main/java/com/unicorn/hgzero/stt/service/AudioBatchProcessor.java

핵심 기능:

  • @Scheduled(fixedDelay = 5000): 5초마다 실행
  • 활성 회의 목록 조회
  • 각 회의별 오디오 처리:
    1. Redis에서 오디오 청크 조회
    2. 청크 병합 (5초 분량)
    3. Azure Speech API 호출
    4. TranscriptSegment DB 저장
    5. Event Hub 이벤트 발행
    6. Redis 정리

📊 데이터 흐름

1. 오디오 수신 (실시간)

Frontend → STT Service:

// WebSocket 연결
const ws = new WebSocket('ws://localhost:8084/ws/audio');

// 녹음 시작
ws.send(JSON.stringify({
  type: 'start',
  meetingId: 'meeting-123'
}));

// 오디오 청크 전송 (매초)
ws.send(JSON.stringify({
  type: 'chunk',
  meetingId: 'meeting-123',
  audioData: base64AudioData,
  timestamp: Date.now(),
  chunkIndex: 0,
  format: 'audio/webm',
  sampleRate: 16000
}));

STT Service:

// AudioWebSocketHandler
handleTextMessage() {
    AudioChunkDto chunk = parseMessage(message);
    audioBufferService.bufferAudioChunk(chunk);
}

// AudioBufferService
bufferAudioChunk(chunk) {
    redis.opsForStream().add("audio:stream:meeting-123", chunk);
}

2. 배치 처리 (5초마다)

@Scheduled(fixedDelay = 5000)
public void processAudioBatch() {
    // 1. 활성 회의 조회
    Set<String> meetings = audioBufferService.getActiveMeetings();

    for (String meetingId : meetings) {
        // 2. 오디오 청크 조회 (최근 5초)
        List<AudioChunkDto> chunks = audioBufferService.getAudioChunks(meetingId);

        // 3. 청크 병합
        byte[] mergedAudio = audioBufferService.mergeAudioChunks(chunks);

        // 4. Azure Speech 인식
        RecognitionResult result = azureSpeechService.recognizeAudio(mergedAudio);

        // 5. DB 저장
        saveTranscriptSegment(meetingId, result);

        // 6. Event Hub 발행
        publishTranscriptionEvent(meetingId, result);

        // 7. Redis 정리
        audioBufferService.clearProcessedChunks(meetingId);
    }
}

3. Event Hub 이벤트 발행

TranscriptionEvent.SegmentCreated event = TranscriptionEvent.SegmentCreated.of(
    segmentId,
    meetingId,
    result.getText(),
    "참석자",
    LocalDateTime.now(),
    5.0, // duration
    result.getConfidence(),
    warningFlag
);

eventPublisher.publishAsync("transcription-events", event);

4. AI 서비스 수신 (Python)

# AI Service (Python)
async def on_event(partition_context, event):
    event_data = json.loads(event.body_as_str())

    if event_data["eventType"] == "TranscriptSegmentReady":
        meetingId = event_data["meetingId"]
        text = event_data["text"]

        # Redis에 텍스트 축적
        await redis_service.add_transcript_segment(meetingId, text, timestamp)

        # Claude API 분석 트리거
        await analyze_and_emit_suggestions(meetingId)

⚙️ 설정 및 실행

1. 환경 변수 설정

IntelliJ 실행 프로파일 (.run/STT.run.xml):

<option name="env">
  <map>
    <entry key="AZURE_SPEECH_SUBSCRIPTION_KEY" value="your-key-here"/>
    <entry key="AZURE_SPEECH_REGION" value="eastus"/>
    <entry key="REDIS_HOST" value="20.249.177.114"/>
    <entry key="REDIS_PORT" value="6379"/>
    <entry key="REDIS_PASSWORD" value="Hi5Jessica!"/>
    <entry key="EVENTHUB_CONNECTION_STRING" value="Endpoint=sb://..."/>
  </map>
</option>

2. 서비스 시작

# IntelliJ에서 STT 실행 프로파일 실행
# 또는

# Gradle로 실행
cd stt
./gradlew bootRun

3. 로그 확인

tail -f stt/logs/stt.log

예상 로그:

2025-10-27 12:00:00 - Azure Speech Service 초기화 완료 - Region: eastus, Language: ko-KR
2025-10-27 12:00:05 - WebSocket 연결 성공 - sessionId: abc123
2025-10-27 12:00:10 - 오디오 청크 버퍼링 완료 - meetingId: meeting-123
2025-10-27 12:00:15 - 배치 처리 시작 - 활성 회의: 1개
2025-10-27 12:00:15 - 음성 인식 성공: 신제품 개발 일정에 대해 논의하고 있습니다.
2025-10-27 12:00:15 - Event Hub 이벤트 발행 완료

🧪 테스트 방법

1. WebSocket 테스트 (JavaScript)

const ws = new WebSocket('ws://localhost:8084/ws/audio');

ws.onopen = () => {
  console.log('WebSocket 연결 성공');

  // 녹음 시작
  ws.send(JSON.stringify({
    type: 'start',
    meetingId: 'test-meeting'
  }));

  // 5초 동안 오디오 청크 전송 (시뮬레이션)
  for (let i = 0; i < 5; i++) {
    setTimeout(() => {
      ws.send(JSON.stringify({
        type: 'chunk',
        meetingId: 'test-meeting',
        audioData: 'dGVzdCBhdWRpbyBkYXRh', // Base64
        timestamp: Date.now(),
        chunkIndex: i
      }));
    }, i * 1000);
  }

  // 10초 후 녹음 종료
  setTimeout(() => {
    ws.send(JSON.stringify({
      type: 'stop'
    }));
  }, 10000);
};

ws.onmessage = (event) => {
  console.log('응답:', JSON.parse(event.data));
};

2. Redis 확인

redis-cli -h 20.249.177.114 -p 6379 -a Hi5Jessica!

# 활성 회의 목록
SMEMBERS active:meetings

# 오디오 스트림 확인
XRANGE audio:stream:test-meeting - +

# 스트림 길이
XLEN audio:stream:test-meeting

3. 데이터베이스 확인

-- 텍스트 세그먼트 조회
SELECT * FROM transcript_segment
WHERE recording_id = 'test-meeting'
ORDER BY timestamp DESC
LIMIT 10;

📈 성능 특성

항목 비고
배치 주기 5초 @Scheduled(fixedDelay = 5000)
지연 시간 최대 5초 사용자 경험에 영향 없음
Azure API 호출 빈도 1/5초 실시간 방식 대비 1/5 감소
Redis TTL 1분 처리 지연 대비
오디오 청크 크기 가변 프론트엔드 전송 주기에 따름

장점

  1. 비용 최적화

    • Azure Speech API 호출 빈도 1/5 감소
    • 비용 절감 효과
  2. 문맥 이해 향상

    • 5초 분량을 한 번에 처리
    • 문장 단위 인식으로 정확도 향상
  3. AI 분석 효율

    • 일정량의 텍스트가 주기적으로 생성
    • AI가 분석하기 적합한 분량
  4. 안정성

    • 재시도 로직 구현 용이
    • 일시적 네트워크 오류 대응
  5. 확장성

    • 여러 회의 동시 처리 가능
    • Redis로 분산 처리 가능

⚠️ 주의사항

1. Azure Speech API 키 관리

  • .run/STT.run.xml에 실제 API 키 설정 필요
  • Git에 커밋하지 않도록 주의

2. Redis 연결

  • Redis 서버가 실행 중이어야 함
  • 연결 정보 확인 필요

3. Event Hub 설정

  • Event Hub 연결 문자열 필요
  • AI 서비스와 동일한 Event Hub 사용

4. 배치 주기 조정

  • 5초 주기는 기본값
  • 필요시 application.yml에서 조정 가능

🔄 다음 단계

  1. 프론트엔드 연동

    • WebSocket 클라이언트 구현
    • 오디오 캡처 및 전송
  2. E2E 테스트

    • 실제 음성 데이터로 테스트
    • Azure Speech API 연동 검증
  3. AI 서비스 통합 테스트

    • Event Hub 통신 확인
    • SSE 스트리밍 검증
  4. 성능 최적화

    • 배치 주기 조정
    • Redis 메모리 사용량 모니터링

📞 문의

STT 서비스: 준호 (Backend Developer) AI 서비스: 서연 (AI Specialist)


최종 업데이트: 2025-10-27