VoiceScript — 음성 변환(STT) + 이미지 인식(OCR) 통합 툴

Debian OS + Docker Compose 기반 자체 호스팅 서비스
faster-whisper(STT) + PaddleOCR 3.x / Ollama Vision(OCR) 듀얼 백엔드


목차

  1. 기능 개요
  2. 프로젝트 구조
  3. 시스템 요구사항
  4. 설치 전 필수 확인사항 ⚠️
  5. 환경 변수 설정
  6. 빌드 및 실행
  7. Nginx 연동 (SSL)
  8. Ollama 모델 준비
  9. 운영 관리
  10. 트러블슈팅 (알려진 이슈)
  11. API 엔드포인트

기능 개요

🎙 STT — 음성 텍스트 변환

  • 엔진: faster-whisper (OpenAI Whisper 최적화 포크)
  • 지원 형식: mp3 wav m4a ogg flac aac mp4 webm mkv
  • VAD(무음 구간 자동 제거) 적용
  • 타임스탬프 세그먼트 분리 출력
  • TXT 파일 다운로드

🔍 OCR — 이미지 텍스트 인식

  • 지원 형식: jpg png bmp tiff webp gif
  • PaddleOCR 모드: 로컬 실행, 표 구조 분석(PP-Structure), Excel 다운로드
  • Ollama Vision 모드: 기존 Ollama 서버 활용, 자연어 지시 가능, 커스텀 프롬프트

🔐 인증

  • JWT 기반 로그인 (만료 시간 설정 가능)
  • 모든 API 토큰 인증 필수

프로젝트 구조

whisper-stt/
│
├── docker-compose.yml          # 전체 서비스 정의
│
├── app/
│   ├── Dockerfile              # Python 3.11-slim + ffmpeg + PaddlePaddle
│   ├── requirements.txt        # Python 패키지 목록
│   │
│   ├── main.py                 # FastAPI 앱 (인증 + STT + OCR 엔드포인트)
│   ├── auth.py                 # JWT 인증 모듈
│   ├── tasks.py                # Celery STT 태스크 (faster-whisper)
│   ├── ocr_tasks.py            # Celery OCR 태스크 (PaddleOCR / Ollama)
│   │
│   └── static/
│       └── index.html          # 웹 프론트엔드 (로그인 + STT + OCR 탭)
│
└── nginx/                      # 참고용 (호스트 Nginx 사용 시 불필요)
    ├── Dockerfile
    └── nginx.conf

컨테이너 구성

┌─────────────────────────────────────────────────┐
│  호스트 Nginx (SSL/certbot)                      │
│  → 리버스 프록시 → 127.0.0.1:8800               │
└─────────────────────────────────────────────────┘
         │
         ▼
┌──────────────────┐    ┌──────────────────┐
│   whisper_app    │    │  whisper_worker  │
│   FastAPI:8000   │    │  Celery Worker   │
│   (포트 8800)    │    │  STT + OCR 처리  │
└────────┬─────────┘    └────────┬─────────┘
         │                       │
         └──────────┬────────────┘
                    ▼
         ┌──────────────────┐
         │  whisper_redis   │
         │  Redis:6379      │
         │  (작업 큐/결과)  │
         └──────────────────┘

시스템 요구사항

항목 최소 권장
CPU 4코어 이상 AMD 5825u 이상
RAM 8GB 16GB (medium 모델 기준)
디스크 20GB 50GB 이상
OS Debian 11+ Debian 12 (Bookworm)
Docker 24.0+ 최신
Docker Compose v2.0+ 최신

의존 서비스

  • Ollama (OCR Vision 모드 사용 시): 호스트에서 11434 포트로 실행 중이어야 함

설치 전 필수 확인사항

⚠️ 이 섹션을 건너뛰면 빌드 후 오류가 발생할 수 있습니다.

1. 호스트 IP 확인 — OLLAMA_URL 설정

Docker 컨테이너에서 호스트의 Ollama에 접근할 때 host.docker.internal
Linux에서 동작하지 않을 수 있습니다. 반드시 실제 LAN IP를 사용하세요.

# 호스트 IP 확인
ip addr show | grep "inet " | grep -v 127.0.0.1

docker-compose.yml에서 아래 두 곳을 실제 IP로 변경:

- OLLAMA_URL=http://실제호스트IP:11434

2. 인증 정보 변경 — 기본값 절대 사용 금지

# docker-compose.yml — app, worker 두 곳 모두 변경
- AUTH_USERNAME=원하는아이디
- AUTH_PASSWORD=강력한비밀번호
- JWT_SECRET=랜덤한긴문자열  # openssl rand -hex 32 로 생성 권장

JWT 시크릿 생성:

openssl rand -hex 32

3. 포트 충돌 확인

기본 포트 8800이 사용 중인지 확인:

ss -tlnp | grep 8800

충돌 시 docker-compose.yml에서 변경:

ports:
  - "원하는포트:8000"

4. 디스크 용량 확인

첫 빌드 시 자동 다운로드 크기:

모델 크기 비고
Whisper medium ~1.5GB HuggingFace 자동 다운로드
PaddleOCR korean ~200MB 첫 실행 시 자동 다운로드
PaddlePaddle 3.0.0 ~300MB 빌드 시 pip install
df -h /

여유 공간 5GB 이상 권장.

5. Ollama 서버 실행 확인

curl http://localhost:11434/api/tags

응답이 없으면 Ollama가 실행 중이지 않은 것입니다.

6. Redis 포트 충돌 확인

이 프로젝트의 Redis(whisper_redis)는 내부 네트워크 전용이라 호스트에 포트를 노출하지 않습니다. 별도 포트 충돌 없음.


환경 변수 설정

docker-compose.yml에서 관리. appworker 두 서비스에 동일하게 설정해야 합니다.

인증

변수 기본값 설명
AUTH_USERNAME admin 로그인 아이디
AUTH_PASSWORD changeme1234 로그인 비밀번호
JWT_SECRET (변경 필수) JWT 서명 키
JWT_EXPIRE_HOURS 12 토큰 유효 시간(시간)

Whisper STT

변수 기본값 설명
WHISPER_MODEL medium tiny base small medium large-v3
WHISPER_DEVICE cpu GPU 없는 경우 cpu
WHISPER_COMPUTE_TYPE int8 CPU 최적화: int8 권장
WHISPER_LANGUAGE ko 언어 고정. 비우면 자동 감지
WHISPER_BEAM_SIZE 5 정확도↑ vs 속도↓
WHISPER_INITIAL_PROMPT (비어있음) 도메인 힌트. 예: "고객 상담 녹취록입니다."

모델별 성능 (5825u CPU 기준)

모델 크기 1분 변환 한국어 정확도
tiny 75MB ~5초 보통
base 145MB ~10초 보통
small 484MB ~30초 양호
medium 1.5GB ~90초 우수 ← 권장
large-v3 3GB ~5분+ 최고

PaddleOCR

변수 기본값 설명
OCR_LANG korean korean en japan chinese_cht ch

Ollama OCR

변수 기본값 설명
OLLAMA_URL http://192.168.0.126:11434 호스트 실제 IP로 변경 필수
OLLAMA_TIMEOUT 180 초 단위. 11b 이상 모델은 300 이상 권장

파일 관리

변수 기본값 설명
MAX_UPLOAD_MB 500 업로드 최대 파일 크기 (MB)
OUTPUT_KEEP_HOURS 48 결과 파일 보관 시간. 0=무제한

빌드 및 실행

# 1. 저장소 클론
git clone http://gitea.byunc.com/[사용자명]/whisper-stt.git
cd whisper-stt

# 2. docker-compose.yml 필수 설정 변경
#    - AUTH_USERNAME, AUTH_PASSWORD, JWT_SECRET
#    - OLLAMA_URL (호스트 실제 IP)

# 3. 빌드 및 시작
docker compose up -d --build

# 4. 진행 상황 확인 (Whisper 모델 다운로드 완료까지 대기)
docker compose logs -f worker

[Whisper] 로드 완료celery@... ready. 메시지 확인 후 접속:

http://서버IP:8800

Nginx 연동 (SSL)

호스트 Nginx + certbot으로 SSL을 운용 중인 경우:

Nginx 설정 (/etc/nginx/sites-available/voicescript.conf)

server {
    listen 443 ssl;
    server_name stt.yourdomain.com;

    ssl_certificate     /etc/letsencrypt/live/stt.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/stt.yourdomain.com/privkey.pem;

    # 대용량 음성 파일 업로드 허용
    client_max_body_size 500M;
    client_body_timeout  300s;
    proxy_read_timeout   600s;
    proxy_send_timeout   600s;

    location / {
        proxy_pass         http://127.0.0.1:8800;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name stt.yourdomain.com;
    return 301 https://$host$request_uri;
}
# SSL 인증서 발급
sudo certbot --nginx -d stt.yourdomain.com

# 설정 적용
sudo nginx -t && sudo systemctl reload nginx

주의: Nginx에서 client_max_body_size 500M을 반드시 설정하세요.
기본값(1MB)이면 음성 파일 업로드가 413 에러로 실패합니다.


Ollama 모델 준비

권장 OCR 모델 (호스트에서 미리 pull)

# 문서/표 특화 — 2GB, 가장 균형 잡힌 선택
ollama pull granite3.2-vision

# OCR 전용 경량 모델 — 2GB
ollama pull deepseek-ocr:3b

# 범용 고정확도 — 8GB (RAM 16GB+ 필요)
ollama pull llama3.2-vision:11b

# 최고 정확도 — 9GB (RAM 16GB+ 필요)
ollama pull richardyoung/olmocr2:7b-q8

granite3.2-vision이 설치되어 있으면 즉시 사용 가능합니다.


운영 관리

기본 명령어

# 상태 확인
docker compose ps

# 로그 확인
docker compose logs app --tail=30
docker compose logs worker --tail=30
docker compose logs -f  # 전체 실시간

# 재시작
docker compose restart

# 중지
docker compose down

# 설정 변경 후 재시작 (재빌드 없이)
docker compose up -d --force-recreate app worker

Docker 이미지 정리

빌드를 여러 번 반복하면 dangling 이미지가 누적됩니다.

# 사용량 확인
docker system df

# 불필요한 이미지/컨테이너 정리 (볼륨 제외)
docker system prune -f

# 특정 이미지만 삭제
docker image ls | grep whisper
docker image rm [IMAGE_ID]

볼륨 관리

볼륨 내용 삭제 시 영향
whisper_models Whisper 모델 (~1.5GB) 재다운로드 필요
paddle_models PaddleOCR 모델 (~700MB) 재다운로드 필요
stt_data 업로드/결과 파일 데이터 손실
redis_data 작업 큐 상태 진행 중 작업 손실
# 모델 캐시 포함 완전 초기화 (주의!)
docker compose down -v

결과 파일 수동 정리

# API로 정리 (OUTPUT_KEEP_HOURS 기준)
curl -X POST http://localhost:8800/api/cleanup \
  -H "Authorization: Bearer [토큰]"

트러블슈팅 (알려진 이슈)

실제 배포 과정에서 겪은 오류들과 해결 방법입니다.


No matching distribution found for paddlepaddle==2.6.1

원인: 미러에서 해당 버전 제거됨
해결: Dockerfile에서 버전을 3.0.0으로 변경

RUN pip install --no-cache-dir paddlepaddle==3.0.0 \
    -i https://pypi.tuna.tsinghua.edu.cn/simple

ValueError: password cannot be longer than 72 bytes (bcrypt)

원인: passlib[bcrypt] 초기화 시 버그
해결: auth.py에서 bcrypt 완전 제거, 직접 문자열 비교로 대체
requirements.txt에서 passlib 줄 삭제


AttributeError: 'DisabledBackend' object has no attribute '_get_task_meta_for'

원인: main.py에서 from celery.result import AsyncResult 사용 시 백엔드 설정이 누락됨
해결: celery_app.AsyncResult(task_id) 방식으로 변경

# main.py
from tasks import celery_app
r = celery_app.AsyncResult(task_id)  # ✅
# AsyncResult(task_id)  # ❌

ModuleNotFoundError: No module named 'ocr_tasks'

원인: tasks.pycelery_app.autodiscover_tasks(["ocr_tasks"]) 동작 안 함
해결: autodiscover_tasks 제거, 직접 import로 변경

# tasks.py 상단에 추가
from ocr_tasks import ocr_task  # noqa: F401

KeyError: 'tasks.ocr_task'

원인: worker에 ocr_task가 등록되지 않음 (위와 동일 원인)
해결: 위와 동일 (from ocr_tasks import ocr_task)


Unknown argument: use_gpu / Unknown argument: show_log

원인: PaddleOCR 3.x에서 파라미터 제거됨
해결: ocr_tasks.py에서 해당 파라미터 삭제

# ✅ PaddleOCR 3.x
_ocr_engine = PaddleOCR(use_angle_cls=True, lang=OCR_LANG)

# ❌ 구버전 방식
_ocr_engine = PaddleOCR(use_angle_cls=True, lang=OCR_LANG, use_gpu=False, show_log=False)

PaddleOCR.predict() got an unexpected keyword argument 'cls'

원인: PaddleOCR 3.x에서 API 변경
해결: ocr(img, cls=True)ocr(img)

result = get_ocr().ocr(img)  # ✅ PaddleOCR 3.x

'paddle.base.libpaddle.AnalysisConfig' object has no attribute 'set_optimization_level'

원인: PaddleOCR 3.x와 paddlepaddle 2.x 버전 불일치
해결: Dockerfile에서 paddlepaddle 3.0.0으로 업그레이드


too many values to unpack (expected 2)

원인: PaddleOCR 3.x 결과 구조 변경 ([bbox, (text, conf)] → dict 형태)
해결: ocr_tasks.py의 결과 파싱 로직 변경

# ✅ PaddleOCR 3.x
r = result[0]
texts  = r.get("rec_texts", [])
scores = r.get("rec_scores", [])

# ❌ 구버전
for bbox, (text, conf) in result[0]:
    ...

MISCONF Redis is configured to save RDB snapshots...

원인: 디스크 용량 부족 또는 권한 문제로 Redis RDB 저장 실패 → 쓰기 차단
해결: docker-compose.yml Redis에 옵션 추가 (Celery 브로커 용도는 영속성 불필요)

redis:
  command: redis-server --stop-writes-on-bgsave-error no

Ollama 연결 타임아웃 (ConnectError)

원인: host.docker.internal이 Linux에서 동작하지 않음
해결: OLLAMA_URL을 호스트의 실제 LAN IP로 변경

- OLLAMA_URL=http://192.168.x.x:11434  # ✅ 실제 IP
# - OLLAMA_URL=http://host.docker.internal:11434  # ❌ Linux에서 불안정

STT 5%에서 멈춤 (진행 없음)

원인: Whisper 모델 첫 다운로드 중 (HuggingFace에서 ~1.5GB)
해결: 기다리면 됩니다. worker 로그로 진행 확인:

docker compose logs -f worker

[Whisper] 로드 완료 메시지 나올 때까지 대기 (네트워크 속도에 따라 5~20분).


API 엔드포인트

인증

메서드 경로 설명
POST /api/login 로그인 (form: username, password)
GET /api/me 현재 사용자 확인

STT

메서드 경로 설명
POST /api/transcribe 음성 파일 업로드 및 변환 시작
GET /api/status/{task_id} 작업 진행 상태 조회
GET /api/download/{filename} 결과 파일 다운로드

OCR

메서드 경로 설명
POST /api/ocr 이미지 업로드 및 인식 시작

OCR POST 파라미터

파라미터 기본값 설명
file 이미지 파일
mode text text | structure (표 분석)
backend paddle paddle | ollama
ollama_model granite3.2-vision Ollama 사용 시 모델명
custom_prompt (비어있음) Ollama 사용 시 커스텀 프롬프트

관리

메서드 경로 설명
POST /api/cleanup 오래된 결과 파일 정리

기술 스택

구성요소 버전 역할
Python 3.11 런타임
FastAPI 0.115 API 서버
Celery 5.4 비동기 태스크 큐
Redis 7 (alpine) 메시지 브로커
faster-whisper 1.0.3 STT 엔진
PaddlePaddle 3.0.0 OCR 딥러닝 프레임워크
PaddleOCR 3.x OCR 엔진
httpx 0.27+ Ollama API 호출
Ollama 호스트 운용 Vision 모델 서버
Description
stt와 ocr
Readme 409 KiB
Languages
HTML 64.2%
Python 35.5%
Dockerfile 0.3%