TM Taeyang Moon
Demo · 아키텍처 해설

Shorts Factory — Sora 2 쇼츠 자동 생성

사람 승인 → Sora 2 영상 생성 → Blob 저장까지의 사설·키리스 비동기 파이프라인

Sora 2Foundry gpt-5.4Service Bus PremiumContainer Apps(KEDA)Private EndpointManaged Identity
🔗
라이브 사이트shorts-factory-web-secure.orangeisland-6a200f98.eastus2.azurecontainerapps.io/
사이트 열기 →

이 문서는 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) · 비동기 큐 · 키리스(시크릿 없음) · 사설 네트워크 격리입니다.

제품 원칙: 공개 트렌드는 추상적 신호로만 사용하고, 원본 영상을 복제·클립·전사·모방하지 않습니다.


아키텍처

                       인터넷 ──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. 사용자가 웹에서 상태를 조회하고 영상을 재생/다운로드.


사용된 Azure 서비스 (실제 배포 검증값)

계층 리소스 검증된 설정 역할
실행환경 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개 키리스 인증(역할 분리)

중요한 설계 결정 (왜 이렇게 했나)

1. 유료 호출 앞에 "사람 승인" 게이트

Sora 영상 생성은 비용이 큰 작업입니다. 그래서 POST /api/jobs아이디어 3개만 만들고 멈춥니다. 사람이 POST /api/jobs/{id}/approve로 1개를 고른 뒤에야 큐에 작업이 들어갑니다. 컴플라이언스 에이전트는 Sora 호출 전에 위험한 기획을 반려할 수 있어, 돈이 나가기 전에 한 번 더 거릅니다. → 초보자 비유: 비싼 주문(영상 생성)은 점원이 "정말 결제할까요?"를 꼭 물어본 뒤에만 진행되는 구조.

2. 웹/워커 분리 + Service Bus 큐 (동기 처리 금지)

영상 한 편 만드는 데 수 분이 걸립니다. 이를 웹 요청 안에서 처리하면 타임아웃·중복 결제·확장 불가 문제가 생깁니다. 그래서 Web은 job_id만 큐에 넣고 즉시 202 를 돌려주고, Worker가 큐를 소비해 무거운 일을 합니다. 워커는 KEDA가 큐 길이를 보고 깨우므로, 일이 없을 때는 리소스를 거의 쓰지 않습니다.

3. Service Bus를 Premium으로 — 처리량이 아니라 Private Endpoint 때문

이 데모의 큐 트래픽은 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·중복감지 없음(앱이 구현) 비용 최적화가 보안보다 중요할 때

4. 멱등하고 재개 가능한(idempotent & resumable) 워커

process_job은 각 단계와 장면별 Sora operation ID를 Blob 작업 JSON에 먼저 저장한 뒤 polling합니다. 워커가 죽거나 메시지가 재전달돼도 새 유료 Sora 작업을 만들지 않고 저장된 operation ID를 조회해 이어갑니다. 저장 키도 {job_id}/short.mp4로 결정적이라 재시도가 같은 Blob을 덮어씁니다. → 중복 결제 방지의 핵심 불변식.

5. 키리스(Managed Identity) + 권한 최소화/분리

연결 문자열·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가 멋대로 새 작업을 보낼 수 없게 막았습니다.

6. 데이터 평면 사설화 (Storage·Service Bus 공개망 Disabled)

Storage는 publicNetworkAccess=Disabled, Blob 공개·공유키 모두 차단. Service Bus도 공개망 차단 + 로컬 키 인증 비활성. 두 서비스는 Private DNS → Private Endpoint 로만 해석/접근됩니다. 외부에 열린 건 Web ingress(HTTPS) 하나뿐입니다. → 주의: 로컬 PC에서 직접 Blob에 업로드하려 하면 사설망 때문에 실패합니다. Azure 런타임(같은 VNet)에서만 동작.

7. 12초 장면 3개 + 마지막 프레임 연결 + 시각 QA

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로 확인하세요.


직접 구축할 때 핵심 포인트 / 함정

  1. Service Bus Private Endpoint는 Premium 전용. Standard로 만들면 PE를 못 붙입니다. 사설 격리가 목표면 처음부터 Premium.
  2. Container Apps 서브넷은 Microsoft.App/environments에 위임해야 하고, Private Endpoint는 별도 서브넷에 둡니다.
  3. Private DNS Zone(privatelink.blob.core.windows.net, privatelink.servicebus.windows.net)을 VNet에 링크해야 이름이 사설 IP로 해석됩니다. 빠지면 워커가 큐/Blob을 못 찾습니다.
  4. 공개망을 끄면 로컬 개발 PC에서의 직접 접근이 막힙니다. 로컬은 mock 공급자로, Azure 런타임에서만 실제 Blob/Service Bus를 쓰도록 분리하세요.
  5. Sender/Receiver 역할 분리를 잊지 마세요. 같은 ID에 둘 다 주면 권한 격리 의미가 사라집니다.
  6. 멱등 재개 불변식을 깨지 마세요. operation ID 저장을 polling 뒤로 옮기면, 재시도마다 새 유료 Sora 작업이 생겨 비용이 폭증합니다.
  7. 남은 운영 강화 과제: Foundry는 현재 공개망이 켜져 있습니다(데이터 평면은 사설인데 AI 평면은 아직). Application Insights 추적, DLQ/비용 경보, Storage ZRS, Entra External ID 사용자 인증이 다음 단계입니다.
  8. 제3자 키 점검: 일반 환경변수로 들어간 외부 API 키가 발견되면 회전 후 Container Apps secret 또는 Key Vault 참조로 옮기세요. 절대 소스/로그에 노출 금지.

한 줄 요약 (고객 가치로 번역)

"비싼 AI 영상 생성을 사람이 승인한 것만, 사설 네트워크 안에서, 시크릿 없이(Managed Identity), 죽어도 중복 결제 없이 이어서 처리하는 안전한 비동기 파이프라인" — 보안·비용 통제·내구성을 모두 만족하는 패턴입니다.


참고 자료 (Microsoft Learn)

← 데모 목록학습 포털 홈