사람 승인 → Sora 2 영상 생성 → Blob 저장까지의 사설·키리스 비동기 파이프라인
이 문서는 Sora 2로 세로형 쇼츠 영상을 자동 생성하는 데모(Shorts Factory) 를 하나의 Azure 아키텍처 사례로 보고, 어떤 서비스로 어떻게 구성했는지, 왜 그렇게 설계했는지를 직접 구축하려는 엔지니어 관점에서 설명합니다. (기준일: 2026-06-14, 리전: East US 2)
라이브 데모는 접속 비밀번호로 보호되어 있습니다. 위 "사이트 열기"는 Container Apps 웹앱 주소이며, 로그인 화면이 먼저 뜹니다. 공개 시연용이 아니라 사설 파일럿 환경입니다.
Shorts Factory는 "주제 한 줄 → AI가 아이디어 3개 제안 → 사람이 1개 승인 → Sora 2가 영상 생성 → Blob 저장 → 웹에서 재생/다운로드"까지를 자동화한 멀티 에이전트 파이프라인입니다.
핵심 키워드는 사람 승인(Human-in-the-loop) · 비동기 큐 · 키리스(시크릿 없음) · 사설 네트워크 격리입니다.
202를 돌려주고, 무거운 생성은 별도 워커가 큐를 소비해 처리합니다.gpt-5.4 로 5개 논리 에이전트(전략·연출·스토리 검수·컴플라이언스·시각 QA)를 구동.제품 원칙: 공개 트렌드는 추상적 신호로만 사용하고, 원본 영상을 복제·클립·전사·모방하지 않습니다.
인터넷 ──HTTPS(유일한 공개 입구)──▶ Container Apps 공개 ingress
│
┌───────────────────────────────────────────────────────────────────────────────────────┐
│ VNet vnet-shorts-app (10.42.0.0/16) │
│ │
│ snet-containerapps (10.42.0.0/27, Microsoft.App/environments 위임) │
│ ┌───────────────────────────┐ 승인된 job_id 1개만 전송 ┌───────────────────┐ │
│ │ Container App: Web │ ──────────────────────────────────▶ │ (Service Bus 큐) │ │
│ │ shorts-factory-web-secure │ └─────────┬─────────┘ │
│ │ - FastAPI + 웹 UI │ │ KEDA 규칙 │
│ │ - 아이디어 3개 생성/승인 │ ┌──────────────────────────────────────▼─────────┐ │
│ │ - replica 1~1 (상시) │ │ Container App: Worker shorts-factory-worker │ │
│ └──────────┬─────────────────┘ │ - 큐 소비 → 연출·검수·컴플라이언스·Sora·조립·QA │ │
│ │ │ - ingress 없음, 큐에 메시지 있을 때만 동작 │ │
│ │ └──────────┬───────────────────────────┬──────────┘ │
│ snet-private-endpoints (10.42.1.0/24) │ 작업 JSON 읽기/쓰기 │ MP4 저장 │
│ ┌───────────────────┐ ┌───────────────────┐ │ │ │
│ │ pe-shorts-blob │ │ pe-shorts-servicebus│◀─┘ (Sender=Web / Receiver=Worker) │
│ └─────────┬─────────┘ └──────────┬─────────┘ │
└────────────┼────────────────────────┼─────────────────────────────────────────────────────┘
│ Private DNS │ Private DNS
▼ ▼
Azure Blob Storage Azure Service Bus (Premium) ┌──────────────────────────────┐
stshortsfacbc56b85d17 sbshortsfac6c7f27 │ Microsoft Foundry (East US 2) │
- shorts-jobs (작업 JSON) - 큐 shorts-generation │ foundry-ncxnghbrr7n6w │
- shorts-videos (MP4) - 중복감지/DLQ/5회 전달 │ - gpt-5.4 (텍스트 5 에이전트)│
* 공개망 Disabled, 키 Disabled * 공개망 Disabled, Private Link │ - sora-2 (세로 영상) │
└──────────────────────────────┘
Worker ──Managed Identity(키리스)──▶ Foundry(gpt-5.4 / sora-2)
흐름 요약
1. 사용자가 웹에서 주제·대상·톤·길이(기본 36초)를 입력 → Web이 gpt-5.4(Strategy)로 원본 아이디어 3개 생성, 작업을 awaiting_approval로 Blob에 저장.
2. 사용자가 아이디어 1개를 승인 → Web은 job_id만 Service Bus 큐에 넣고 즉시 202 Accepted 반환.
3. 큐에 메시지가 생기면 KEDA가 Worker를 깨움 → Worker가 연출(Director) → 스토리 검수(Story Editor) → 컴플라이언스 → Sora 2로 12초 장면 3개 순차 생성 → FFmpeg 조립 → 완성본 시각 QA → Blob(shorts-videos)에 MP4 저장 → 작업 completed.
4. 사용자가 웹에서 상태를 조회하고 영상을 재생/다운로드.
| 계층 | 리소스 | 검증된 설정 | 역할 |
|---|---|---|---|
| 실행환경 | cae-shorts-secure (Container Apps Env) |
VNet 연결, East US 2 | Web/Worker 공통 런타임 |
| 웹 | shorts-factory-web-secure |
외부 HTTPS ingress, 1 vCPU / 2 GiB, replica 1~1 | FastAPI + 웹 UI, 아이디어 생성·승인 |
| 워커 | shorts-factory-worker |
ingress 없음, 1 vCPU / 2 GiB, KEDA(Service Bus) 규칙 | 큐 소비 + 영상 생성 파이프라인 |
| 레지스트리 | acrue2423kqbdy4k |
Basic | Web/Worker 컨테이너 이미지 |
| 메시징 | sbshortsfac6c7f27 |
Premium, 1 MU, 큐 shorts-generation, 공개망 Disabled |
승인 작업 큐(내구성·중복감지·DLQ) |
| 큐 정책 | shorts-generation |
lock 5분, 최대 전달 5회, 중복감지 10분 window, 만료 시 DLQ | 한 번에 1건, 안전한 재시도 |
| 저장 | stshortsfacbc56b85d17 |
StorageV2, Standard LRS, 공개망 Disabled, Blob 공개 차단, 공유키 차단 | 작업 JSON + 완성 MP4 |
| 컨테이너 | shorts-jobs / shorts-videos |
비공개 | jobs/{id}.json / {id}/short.mp4 |
| AI | foundry-ncxnghbrr7n6w |
Azure AI Services, East US 2 | Foundry 프로젝트/모델 호스팅 |
| 모델 | gpt-5.4 (ver 2026-03-05) |
GlobalStandard | 전략·연출·검수·컴플라이언스·시각 QA |
| 모델 | sora-2 (ver 2025-12-08) |
GlobalStandard | 720×1280 세로 영상 생성 |
| 네트워크 | vnet-shorts-app + PE 2개 + Private DNS 2개 |
Blob/Service Bus용 Private Endpoint | 사설 경로 격리 |
| ID | mi-ue2423kqbdy4k / mi-shorts-worker |
사용자 할당 Managed Identity 2개 | 키리스 인증(역할 분리) |
Sora 영상 생성은 비용이 큰 작업입니다. 그래서 POST /api/jobs는 아이디어 3개만 만들고 멈춥니다.
사람이 POST /api/jobs/{id}/approve로 1개를 고른 뒤에야 큐에 작업이 들어갑니다. 컴플라이언스 에이전트는
Sora 호출 전에 위험한 기획을 반려할 수 있어, 돈이 나가기 전에 한 번 더 거릅니다.
→ 초보자 비유: 비싼 주문(영상 생성)은 점원이 "정말 결제할까요?"를 꼭 물어본 뒤에만 진행되는 구조.
영상 한 편 만드는 데 수 분이 걸립니다. 이를 웹 요청 안에서 처리하면 타임아웃·중복 결제·확장 불가 문제가 생깁니다.
그래서 Web은 job_id만 큐에 넣고 즉시 202 를 돌려주고, Worker가 큐를 소비해 무거운 일을 합니다.
워커는 KEDA가 큐 길이를 보고 깨우므로, 일이 없을 때는 리소스를 거의 쓰지 않습니다.
이 데모의 큐 트래픽은 Standard로도 충분합니다. 그런데도 Premium을 쓴 유일하고 결정적인 이유는, Service Bus의 Private Link/Private Endpoint가 Premium 티어에서만 지원되기 때문입니다. 공개 엔드포인트를 닫고 VNet 사설 경로로만 큐에 접근하려면 Premium이 필요합니다(부가로 전용 처리량·AZ 중복 옵션도 따라옵니다). → 트레이드오프: Premium은 유휴에도 고정비가 발생합니다. 보안 격리 < 비용이 더 중요하면 Standard(+공개망+Entra 인증)나 Storage Queue가 대안입니다.
| 선택지 | 장점 | 단점 | 적합한 때 |
|---|---|---|---|
| 현재: Service Bus Premium | Private Endpoint, DLQ, 중복감지, 전용 처리량 | 고정 비용 | 사설 네트워크 격리를 유지할 때 |
| Service Bus Standard | 비용↓, 큐 기능 대부분 유지 | Private Endpoint 불가(공개 엔드포인트 필요) | 공개망+Entra 인증 허용 시 |
| Azure Storage Queue | 기존 Storage 재사용, Queue PE 지원 | 기본 DLQ·중복감지 없음(앱이 구현) | 비용 최적화가 보안보다 중요할 때 |
process_job은 각 단계와 장면별 Sora operation ID를 Blob 작업 JSON에 먼저 저장한 뒤 polling합니다.
워커가 죽거나 메시지가 재전달돼도 새 유료 Sora 작업을 만들지 않고 저장된 operation ID를 조회해 이어갑니다.
저장 키도 {job_id}/short.mp4로 결정적이라 재시도가 같은 Blob을 덮어씁니다. → 중복 결제 방지의 핵심 불변식.
연결 문자열·API 키를 전혀 쓰지 않고 전부 DefaultAzureCredential(런타임에선 Managed Identity)로 인증합니다.
- Web ID mi-ue2423kqbdy4k: AcrPull, Foundry User, Cognitive Services User, Storage Blob Data Contributor, Service Bus Data Sender
- Worker ID mi-shorts-worker: 위와 동일 + Service Bus Data Receiver
Sender/Receiver를 다른 ID로 분리해, Web이 큐를 소비하거나 Worker가 멋대로 새 작업을 보낼 수 없게 막았습니다.
Storage는 publicNetworkAccess=Disabled, Blob 공개·공유키 모두 차단. Service Bus도 공개망 차단 + 로컬 키 인증 비활성.
두 서비스는 Private DNS → Private Endpoint 로만 해석/접근됩니다. 외부에 열린 건 Web ingress(HTTPS) 하나뿐입니다.
→ 주의: 로컬 PC에서 직접 Blob에 업로드하려 하면 사설망 때문에 실패합니다. Azure 런타임(같은 VNet)에서만 동작.
Sora SDK는 장면당 4/8/12초만 지원합니다. 그래서 36초를 12초 훅 → 12초 상승 → 12초 보상 세 장면으로 쪼개고,
한 장면의 마지막 프레임(720×1280 PNG)을 다음 장면의 input_reference로 넘겨 연속성을 만듭니다. FFmpeg가
0.12초 xfade/acrossfade로 이어붙여 H.264/AAC MP4로 인코딩합니다. 조립 후 시각 QA 에이전트가 실제 프레임
15장을 검사해 종합 82점·항목별 75점 미만이면 실패 장면부터 최대 2회 부분 재생성합니다(합격 장면은 보존).
[*] ─▶ awaiting_approval ──사람 승인──▶ generating ──컴플라이언스 반려──▶ rejected
│
├─ 임시 조립 ─▶ visual_review ─ 품질 승인 ─▶ completed
│ │
│ └ 실패 장면부터 부분 재생성 ─▶ generating
└─ 공급자 오류 ─▶ failed ── 일시 오류 재전달 ─▶ generating
Worker가 Blob 작업 JSON에 남기는 단계: directing → creative_review → compliance → video → visual_review → storage → completed.
큐 lock은 5분이지만 SDK AutoLockRenewer가 최대 75분까지 갱신, Sora timeout은 장면당 20분. 5회 실패 시 메시지는 DLQ로 이동합니다.
가격은 변동되므로 단정하지 마세요. 실제 견적은 Azure Pricing Calculator와 구독의 Cost Management로 확인하세요.
Microsoft.App/environments에 위임해야 하고, Private Endpoint는 별도 서브넷에 둡니다.privatelink.blob.core.windows.net, privatelink.servicebus.windows.net)을 VNet에 링크해야 이름이 사설 IP로 해석됩니다. 빠지면 워커가 큐/Blob을 못 찾습니다."비싼 AI 영상 생성을 사람이 승인한 것만, 사설 네트워크 안에서, 시크릿 없이(Managed Identity), 죽어도 중복 결제 없이 이어서 처리하는 안전한 비동기 파이프라인" — 보안·비용 통제·내구성을 모두 만족하는 패턴입니다.