Back to all posts
Tutorial

Cloudflare Whisper로 Chatbot에 $0 비용 다국어 음성 인식 추가하기

Chatbot을 개발하면서 음성 입력을 추가하고 싶지만 Google Speech-to-Text, AWS Transcribe, OpenAI Whisper API 비용은 내고 싶지 않다면 — 이미 필요한 모든 것을 갖고 있습니다. Cloudflare 무료 플랜 안에.

이 글에서는 Cloudflare Workers AI Whisper large-v3-turbo를 제 포트폴리오 사이트 Chatbot에 통합하여 99개 언어 음성 인식을 완전 무료로 구현한 방법을 상세히 설명합니다. 또한 프로덕션에서 마주친 두 가지 버그와 정확한 수정 방법도 공유합니다.


왜 Cloudflare Workers AI인가?

Cloudflare는 Workers AI를 통해 음성 인식 기능을 제공하며, Cloudflare 글로벌 엣지 네트워크에서 실행됩니다. 무료 플랜에는 다음이 포함됩니다:

  • Workers AI 추론: 무료, 한도는 공식 문서 참고 (개인 프로젝트 및 소규모 앱에 충분)
  • 콜드 스타트 없음: 엣지 노드에서 실행, 사용자와 가장 가까운 곳에서 동작
  • API 키 관리 불필요: env.AI를 통해 Worker에 직접 바인딩
  • 99개 언어 지원: 한국어, 중국어, 일본어, 영어, 프랑스어, 스페인어, 아랍어 등

가장 큰 장점: 모델을 호스팅하거나 GPU 인스턴스를 관리하거나 서드파티 API 속도 제한을 걱정할 필요가 없습니다. 바로 사용 가능합니다.


Whisper Base vs Whisper Large v3 Turbo: 상세 비교

Cloudflare Workers AI는 두 가지 Whisper 모델을 제공합니다. 올바른 모델 선택이 중요합니다.

@cf/openai/whisper (기본 버전)

오리지널 Whisper base 모델 (약 3,900만 파라미터)입니다.

장점:

  • 빠른 추론 속도 — 레이턴시에 민감한 애플리케이션에 최적
  • 요청당 리소스 소모 낮음
  • Cloudflare에서 충분히 검증된 안정적인 모델

단점:

  • 단어 오류율(WER) 높음, 특히 비영어권 언어에서
  • 억양, 전문 용어, 혼합 언어 입력 인식 약함
  • audio 파라미터 형식: number[] (Uint8Array를 일반 JS 배열로 전개)

최적 사용 사례: 속도가 정확도보다 중요한 고트래픽 앱, 또는 영어 전용 사용 사례.


@cf/openai/whisper-large-v3-turbo (Turbo 버전)

Whisper large-v3의 경량 증류 버전 (약 8억 900만 파라미터)으로, large-v3보다 약 2배 빠르면서 대부분의 정확도를 유지합니다.

장점:

  • 기본 버전 대비 WER 약 50% 감소 — 한국어, 중국어, 일본어 등에서 극적으로 개선
  • 다양한 억양에서 뛰어난 다국어 정확도
  • 선택적 파라미터 지원: language, task (transcribe/translate), vad_filter, beam_size
  • 풍부한 출력: text, word_count, WebVTT 타이밍이 포함된 segments

단점:

  • 기본 버전보다 무거움 — 요청당 레이턴시 약간 높음
  • audio 파라미터 형식: Base64 문자열 (숫자 배열 아님 — 첫 번째 버그의 원인)
  • webm/opus 컨테이너를 안정적으로 디코딩하지 못함 (두 번째 버그의 원인)

최적 사용 사례: 다국어 Chatbot, 음성 어시스턴트, 접근성 도구, 정확도가 중요한 전사 파이프라인.


비교 요약

| | @cf/openai/whisper | @cf/openai/whisper-large-v3-turbo | |---|---|---| | 파라미터 수 | 약 3,900만 | 약 8억 900만 | | 지원 언어 | 99개 | 99개 | | 인식 정확도 | 양호 | 우수 | | 추론 속도 | 가장 빠름 | 빠름 (large-v3보다 약 2×) | | audio 파라미터 형식 | number[] | Base64 문자열 | | 지원 컨테이너 | webm, wav, mp3 | wav, mp3 (webm 불안정) | | 추가 출력 필드 | text만 | text, word_count, segments | | Cloudflare 비용 | 무료 플랜 | 무료 플랜 |


아키텍처

구축한 전체 파이프라인:

브라우저 마이크
    ↓
MediaRecorder (webm/opus 녹음 — 브라우저 기본 형식)
    ↓ [클라이언트 측 트랜스코딩]
AudioContext.decodeAudioData() → webm/opus 디코딩
    ↓
encodeWAV() → 16-bit PCM WAV (모노, 네이티브 샘플레이트)
    ↓ HTTP POST multipart/form-data
/api/transcribe (Next.js Edge Route on Cloudflare Pages)
    ↓
getRequestContext().env.AI (Cloudflare AI 바인딩)
    ↓
btoa() → 8192바이트 청크로 Base64 인코딩
    ↓
env.AI.run('@cf/openai/whisper-large-v3-turbo', { audio: base64 })
    ↓
{ text: "전사 결과" } → Chatbot 입력창에 반영

프로덕션에서 마주친 두 가지 버그

버그 1: 에러 코드 5006 — 타입 불일치

@cf/openai/whisper에서 whisper-large-v3-turbo로 전환 후 API가 에러 5006을 반환했습니다.

근본 원인: 기본 모델이 기대하는 구식 형식 audio: [...audioBytes] (숫자 배열)을 그대로 사용했습니다. turbo 모델은 Base64 문자열을 요구합니다. 두 모델의 입력 스키마는 완전히 다릅니다 — 하지만 문서에 명시되지 않았습니다.

수정 방법: 서버 측에서 btoa()로 청크 처리하여 Uint8Array를 Base64 문자열로 인코딩 후 전달합니다.


버그 2: 에러 코드 3030 — 오디오 파일 디코딩 실패

Base64 형식 수정 후 에러 3030이 발생했습니다.

근본 원인: 브라우저가 webm/opus 형식으로 녹음합니다. Base64 인코딩은 정확했지만 turbo 모델이 webm/opus 컨테이너를 디코딩하지 못했습니다. Cloudflare Whisper turbo는 WAV와 MP3만 안정적으로 지원합니다.

수정 방법: 브라우저에서 AudioContext.decodeAudioData()로 webm/opus를 디코딩하고, 커스텀 encodeWAV()로 16-bit PCM WAV로 재인코딩하여 업로드합니다. WAV는 Whisper에서 가장 보편적으로 지원되는 입력 형식입니다.


핵심 주의사항과 팁

  1. export const runtime = 'edge'는 필수입니다. 없으면 getRequestContext()가 Cloudflare Pages에서 아무것도 반환하지 않습니다.

  2. Edge Runtime에는 Buffer가 없습니다. Base64 인코딩에 Buffer.from(bytes).toString('base64')를 사용할 수 없습니다. btoa()와 청킹을 사용하여 대용량 오디오 파일에서 콜 스택 오버플로우를 방지하세요.

  3. turbo와 base 모델의 audio 필드 타입이 다릅니다. 이 차이는 문서에 명시되지 않았습니다 — 오류와 실험을 통해 발견했습니다.

  4. AudioContext는 반드시 닫아야 합니다. 브라우저는 동시 AudioContext 인스턴스 수에 한도가 있습니다. 디코딩 후 반드시 await audioCtx.close()를 호출하세요.

  5. 빈 녹음을 방어하세요. 사용자가 실수로 마이크를 탭하면 즉시 놓으면 decodeAudioData가 빈 blob에 예외를 던집니다. blob.size < 1000을 확인하고 조기 반환하세요.

  6. Base64는 약 33% 크기 증가를 야기합니다. 10초 음성 녹음(약 160KB 원시 PCM)은 Base64 후 약 215KB가 되며, Cloudflare 제한 내에 충분히 수용됩니다.

  7. iOS는 사용자 제스처가 필요합니다. AudioContext 생성을 위해 타이머나 useEffect가 아닌 onPointerDown을 사용하세요.


최종 결과

이 파이프라인 구현 후 제 Chatbot은 다음을 지원합니다:

  • 길게 눌러서 말하기: 마이크 버튼을 누른 채로 말하고 놓으면 전사
  • 탭 토글: 한 번 탭하여 녹음 시작, 말한 후 다시 탭하여 전사
  • 99개 언어: 한국어, 중국어, 일본어, 영어 등에서 우수한 인식 정확도
  • 제로 비용: 완전히 Cloudflare 무료 플랜 내에서 완결
  • 서드파티 API 키 불필요: 모든 것이 Cloudflare 엣지에서 실행

다국어 AI 제품을 개발하면서 음성 입력이 필요하다면, Cloudflare Workers AI Whisper가 답입니다. 제가 마주친 두 가지 버그는 문서에 명시되지 않았습니다 — 이 글이 몇 시간의 디버깅을 절약하는 데 도움이 되길 바랍니다.


전체 구현은 elvisli.ca에서 공개 중입니다.