@startuml !theme mono title 음성-텍스트 변환 내부 시퀀스 (UFR-STT-020) participant "API Gateway<>" as Gateway participant "SttController" as Controller participant "SttService" as Service participant "TranscriptionEngine" as Engine participant "Azure Speech<>" as Speech participant "SttRepository" as Repository database "PostgreSQL<>" as DB queue "Event Hub<>" as EventHub Gateway -> Controller: POST /api/v1/stt/transcribe\n{sessionId, audioFile} activate Controller Controller -> Service: transcribeAudio(sessionId, audioFile) activate Service Service -> Repository: findSessionById(sessionId) activate Repository Repository -> DB: STT 세션 조회\n(세션ID 기준) DB --> Repository: session data Repository --> Service: SttSession entity deactivate Repository alt 실시간 변환 모드 Service -> Engine: streamingTranscribe(audioFile) activate Engine Engine -> Speech: createRecognizer()\nsetContinuousRecognition() note right Azure Speech 설정: - Mode: Continuous - Language: ko-KR - Enable diarization - Profanity filter end note Speech --> Engine: recognizer instance loop 오디오 청크 처리 Engine -> Speech: recognizeOnceAsync(audioChunk) Speech --> Engine: recognition result note right 결과 포함: - Text - Confidence - Duration - Speaker ID end note Engine -> Engine: validateConfidence(result) note right 신뢰도 검증: - Threshold: 0.7 - Low confidence 처리 end note Engine --> Service: transcription segment Service -> Repository: saveSttSegment(segment) activate Repository Repository -> DB: STT 세그먼트 저장\n(세션ID, 텍스트, 신뢰도, 타임스탬프) DB --> Repository: saved Repository --> Service: segment saved deactivate Repository Service -> EventHub: publish(TranscriptionSegmentEvent) note right Event: - sessionId - segmentId - text - timestamp end note end Engine --> Service: streaming complete deactivate Engine else 배치 변환 모드 Service -> Engine: batchTranscribe(audioFile) activate Engine Engine -> Speech: batchTranscriptionAsync(audioUrl) note right 배치 처리: - 전체 파일 업로드 - 백그라운드 처리 - Callback URL 제공 end note Speech --> Engine: transcription job ID Engine --> Service: job submitted deactivate Engine Service -> Repository: updateSessionStatus(sessionId, "PROCESSING") activate Repository Repository -> DB: 세션 상태 업데이트\n(상태='처리중') DB --> Repository: updated Repository --> Service: updated deactivate Repository end Service -> Repository: aggregateTranscription(sessionId) activate Repository Repository -> DB: 세그먼트 목록 조회\n(세션ID 기준, 타임스탬프 순 정렬) DB --> Repository: segments Repository --> Service: ordered segments deactivate Repository Service -> Service: mergeSegments(segments) note right 세그먼트 병합: - 화자별 그룹화 - 시간 순서 정렬 - 문장 경계 보정 end note Service -> Repository: saveTranscription(fullText) activate Repository Repository -> DB: 전체 텍스트 저장 및 상태 업데이트\n(전체텍스트, 상태='완료') DB --> Repository: saved Repository --> Service: updated session deactivate Repository Service -> EventHub: publish(TranscriptionCompletedEvent) note right Event: - sessionId - meetingId - fullText - completedAt end note Service --> Controller: TranscriptionResponse\n{sessionId, text, segments} deactivate Service Controller --> Gateway: 200 OK\n{transcription, metadata} deactivate Controller @enduml