# 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`

```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`

```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 키 없을 때)

**설정**:
```yaml
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:**
```javascript
// 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:**
```java
// AudioWebSocketHandler
handleTextMessage() {
    AudioChunkDto chunk = parseMessage(message);
    audioBufferService.bufferAudioChunk(chunk);
}

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

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

```java
@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 이벤트 발행

```java
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)

```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`):
```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. 서비스 시작

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

# Gradle로 실행
cd stt
./gradlew bootRun
```

### 3. 로그 확인

```bash
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)

```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 확인

```bash
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. 데이터베이스 확인

```sql
-- 텍스트 세그먼트 조회
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
