AWS 기술 블로그
보이저엑스의 ComfyUI 워크플로우 기반 AI 비디오 생성 파이프라인 구축 여정
보이저엑스(VoyagerX)의 Vrew는 AI기술을 활용해 쉽고 편한 영상 편집 경험을 제공하는 서비스입니다. 자동 자막 생성으로 영상 속 대사를 자동으로 텍스트로 변환해 주고, 이를 문서처럼 편집할 수 있는 편리한 UX를 제공합니다. 또한 AI를 활용한 대본 생성, 목소리 생성, 이미지 생성으로 영상편집 초보자도 쉽게 사용할 수 있습니다.
최근 오픈소스 비디오 생성 모델이 다수 공개되면서, 저희 서비스에도 AI 비디오 생성 기능을 도입하기 위해 여러 실험을 진행했습니다. 여러 모델 중 인퍼런스 비용 대비 성능이 가장 좋았던 Wan2.1 1.3B모델을 선택했고, 이를 서비스하기 위해 AWS의 다양한 인스턴스 타입에서의 테스트와 추론 최적화를 진행했습니다.
Wan 모델 소개

출처: https://github.com/Wan-Video/Wan2.1?tab=readme-ov-file
Wan의 모델 구조는 Stable Diffusion과 Transformer를 결합한 “Diffusion Transformer” 기반으로 설계되었습니다. 전체 파이프라인은 텍스트 인코더, 비디오 VAE(Wan-VAE), 그리고 Diffusion Transformer(DiT) 세 가지 주요 컴포넌트로 구성됩니다.
우선 Wan-VAE가 고해상도 비디오를 저차원 latent space로 압축/복원하고, DiT가 압축된 latent vector 시퀀스를 기반으로 diffusion 생성 과정을 수행하며, 텍스트 인코더는 사용자 프롬프트를 임베딩합니다.
비디오를 그대로 Transformer에 넣으면 토큰 수가 기하급수적으로 늘어나기 때문에, Wan-VAE는 한 개 영상(프레임)과 미래 시간축 T 프레임을 입력으로 받아 공간은 8배, 시간은 4배로 압축된 잠재 벡터를 생성합니다. Causal 3-D Convolution을 통해 미래 프레임 정보가 과거로 누설되지 않도록 설계된 것이 특징입니다. 이렇게 얻어진 비디오 latent sequence는 Diffusion Transformer에 전달되어 본격적인 생성 과정을 거치게 됩니다.
Diffusion Transformer는 Stable Diffusion의 U-Net대신 Transformer 블록들로 구성된 확산 모델입니다.
패치화 (patchifying) 단계에서 VAE에서 나온 잠재 시퀀스를 Transformer 입력 토큰으로 변환하는데, 시간 차원 1, 공간 2×2 커널의 3D Convolution을 사용해 토큰 시퀀스로 변환합니다. 패치 하나 당 (4프레임, 가로 16px, 세로 16px) 정보를 가지고 있게 됩니다. 시간 차원을 축소할 때는 첫 번째 프레임은 제외하기 때문에, 1280 × 720 (가로 × 세로), 프레임 수 T = 25, Batch Size 1이라면 7 x 45 x 80으로 길이 25200의 토큰 시퀀스를 가집니다.
VAE에서 사용한 causal 3-D Convolution과 반대로, Wan의 DiT에서는 자연스러운 움직임 생성을 위해 non-causal Attention을 사용해 과거와 미래 정보를 함께 학습합니다. 프레임 수나 해상도에 따라 수십만~백만 개 토큰까지 시퀀스 길이가 늘어날 수 있고, 연산량은 O(N²) 이기 때문에 어텐션 최적화와 병렬화가 중요합니다.
Wan모델은 Diffusion Cache기법, Classifier-Free Guidance기법, FlashAttention을 사용해 attention을 최적화하고, xDiT 엔진을 통한 USP(2-D Context Parallel)의 적용으로 병렬 추론을 지원합니다.
추론 최적화/병렬화
Wan은 FSDP(Fully Sharded Data Parallel)로 가중치/옵티마이저 상태를 GPU들에 분산 저장(sharding)을 지원합니다.
또한 xDiT 엔진으로 USP(Unified Sequence Parallelism)를 적용해 시퀀스를 분할하고 attention 계산을 GPU 여러 대에 분산할 수 있습니다. USP는 2-D Context Parallel로 Ulysses(행) x Ring(열) 2차원으로 분할해서 attention을 계산합니다.
Ulysses기법은 Attention Head 차원으로 분할해 각 GPU는 전체 시퀀스를 갖지만 일부 Head에 대해서만 attention을 계산하게 됩니다. --ulysses_size $GPU_NUMS 로 사용할 수 있고 헤드의 수가 ulysses_size로 나누어떨어져야 합니다.
Ring-Attention은 토큰 시퀀스를 GPU별로 청크(chunk) 로 나눈 뒤, 각 GPU가 자기 청크의 Query를 계산하면서, 필요한 Key/Value를 링 모양으로 돌려가며 교환하며 전 시퀀스를 커버하는 방식입니다. --ring_size $GPU_NUMS 로 사용할 수 있고 시퀀스 길이가 ring_size로 나누어떨어져야 합니다.
Wan2.1의 공식 레포에서는 다음 커맨드를 사용해 FSDP와 USP를 적용한 multi-gpu inference를 할 수 있습니다.
pip install "xfuser>=0.4.1"
torchrun --nproc_per_node=8 generate.py --task t2v-14B --size 1280*720 --ckpt_dir ./Wan2.1-T2V-14B --dit_fsdp --t5_fsdp --ulysses_size 8 --prompt "Two anthropomorphic cats in comfy boxing gear and bright glovesfight intensely on a spotlighted stage."
추론 테스트
로컬 테스트
우선 로컬 개발 환경(A6000 GPU)에서 Wan 2.1 공식 레포지토리를 사용해 480×480 해상도의 61프레임 영상을 30 steps로 생성하는 실험을 진행했습니다. 그 결과는 다음 표와 같습니다.

동일한 환경에서 ComfyUI-WanVideoWrapper를 활용해 테스트한 결과는 아래와 같습니다. 해당 래퍼는 기본적으로 multi-GPU inference를 지원하지 않아, 이 부분은 별도의 엔지니어링을 통해 직접 구현·실험했습니다.

AWS 환경 테스트
다음으로 AWS 인스턴스에서 실험을 진행했습니다. 다만, EBS 성능 한계(io1 3000 IOPS 사용) 때문인지 로컬보다 모델 로딩 시간이 크게 늘어났습니다. 실제로 Wan 2.1 공식 레포지토리 기준 모델 로딩 및 추론 준비에 300초 이상 소요되었습니다.
이에 비해 ComfyUI 기반 추론 파이프라인은 메모리 관리가 훨씬 효율적이어서, FP8 scaled 모델을 함께 사용하면 VRAM 점유를 줄이는 동시에 모델 로딩 속도까지 크게 단축할 수 있었습니다. 최초 로드의 경우 공식 레포보다 ComfyUI의 텍스트 인코더 로드 시간이 더 오래 걸리는 단점이 있었지만, 캐싱된 이후부터는 Text encoder 0.54초, VAE 0.83초, DiT 0.13초로 매우 빨라져 오토스케일링 시 콜드 스타트 지연을 최소화할 수 있었습니다.
또한 ComfyUI-WanVideoWrapper는 TeaCache와 Enhance-A-Video를 지원하고, 추후 다른 모델 서빙에도 그대로 활용할 수 있는 확장성이 있어 서비스 환경을 ComfyUI 중심으로 구성하기로 결정했습니다.

최근 출시된 Wan 2.2 또한 기존 ComfyUI기반 추론 환경에서 워크플로우를 수정할 필요 없이, 모델 교체와 일부 하이퍼파라미터 조정만으로 쉽게 적용할 수 있었습니다. 이를 통해 2.1 대비 더 높은 해상도(예: 720p)와 품질을 확보할 수 있음을 확인했습니다.

실험 결과 g6e.xlarge에서 추론하는 것이 가장 비용 면에서 효율적이었고, 이 인스턴스로 서비스 환경을 구성하기로 결정했습니다.
서비스 환경 설정

서비스 운영을 위해서는 확장 가능한 추론 환경이 필수적이기 때문에 Ray Serve를 도입했습니다. Ray Serve는 머신러닝 모델을 프로덕션 환경에 배포하고 확장하기 위한 라이브러리입니다. 여러 모델을 조합하거나 복잡한 비즈니스 로직을 파이프라인으로 구성할 수 있으며, 요청량에 따른 오토스케일링 기능을 제공해 유연하고 확장 가능한 추론 서비스를 구축할 수 있도록 돕습니다.
클라이언트에서 비디오 생성 요청을 보내면 람다 함수를 Ray Cluster로 요청을 보내게 됩니다. 이때 람다 함수는 서울 리전에, GPU 인스턴스가 많이 필요한 Ray Cluster는 오레곤 리전에 존재하기 때문에 VPC Peering을 사용했습니다. Ray Cluster의 HTTP Proxy를 EveryNode로 띄워 Head/Worker 노드 모두 ALB타깃에 넣어 분산받도록 구성했고, 이를 통해 네트워크 부하를 줄였습니다. Worker Node에서 생성된 비디오는 S3에 저장되고, 클라이언트는 /get-video를 폴링해 S3의 상태를 확인하고, 생성된 비디오가 있다면 url을 받습니다. 추후 이 구조에 Amazon EKS를 적용해 더 세밀한 오토스케일링 및 namespace별 환경 관리가 가능하도록 발전시켰습니다.
이를 위해 필요한 모델을 미리 다운로드해 둔 AMI를 준비하고, ComfyUI-to-Python-Extension을 이용해 ComfyUI 워크플로우를 Python 스크립트로 변환, Ray worker 구성을 진행했습니다. ComfyUI와 custom_nodes를 포함한 폴더를 .whl 패키지로 빌드해 Conda 환경에 추가했습니다. 덕분에 환경 설정이 완료된 AMI 하나만 있으면, 해당 AMI ID를 ray_config.yaml의 ImageId에 넣어 손쉽게 동일 환경의 Ray worker를 생성할 수 있습니다. AMI 환경 구성이 끝난 후에는, ComfyUI-to-Python-Extension으로 변환된 스크립트를 직접 실행해 환경이 정상적으로 세팅되었는지 확인할 수 있습니다.
AMI에서 ComfyUI 환경 설정
ComfyUI는 원래 ComfyUI 폴더 내부에서 실행하는 것을 전제로 설계되어 있습니다. 따라서 .whl 빌드 전 import ComfyUI 호출 시 자동으로 sys.path에 경로가 추가되도록 __init__.py 파일을 보강했습니다.
wheel build를 위한 setup.py는 다음과 같이 간단히 작성했습니다.
from setuptools import setup, find_namespace_packages
import os
requirements = []
if os.path.exists('ComfyUI/requirements.txt'):
with open('ComfyUI/requirements.txt') as f:
requirements.extend(f.read().splitlines())
if os.path.exists('ComfyUI/custom_nodes') and os.path.isdir('ComfyUI/custom_nodes'):
for root, dirs, files in os.walk('ComfyUI/custom_nodes'):
if 'requirements.txt' in files:
req_path = os.path.join(root, 'requirements.txt')
with open(req_path) as f:
requirements.extend(f.read().splitlines())
requirements = list(set(requirements))
setup(
name="ComfyUI",
version="0.0.1",
description="A ComfyUI fork for vrew video generation",
author="VoyagerX",
author_email="v6x@voyagerx.com",
url="<https://github.com/v6x/ComfyUI>",
packages=find_namespace_packages(exclude=["input", "output", "models", "notebooks", "script_examples", "scripts", "tests", "tests-unit"]),
include_package_data=True,
python_requires=">=3.11",
package_data={
"": ["*.*"],
},
install_requires=requirements,
)
또한 Ray worker 초기화 과정에서는 extra_model_paths.yml에 정의된 모델 경로를 등록하는 함수(add_extra_model_paths)와, custom_nodes를 불러오는 함수(import_custom_nodes)를 실행하도록 구성했습니다.
worker 초기화 과정에서 모델 경로와 custom_nodes설정이 끝난 후에는 모델·텍스트 인코더·VAE를 미리 로딩해 두어, 실제 요청 시에는 generate 함수에서 30 steps 동안 영상 생성과 mp4 저장만 수행하도록 설계했습니다. 이 방식으로 불필요한 로딩 과정을 제거해, 서비스 응답 속도를 한층 효율적으로 최적화할 수 있었습니다. (Inference time 50초 → 48초)
마무리
이번 여정을 통해 저희는 단순히 오픈소스 모델을 도입하는 데 그치지 않고, 실제 서비스 환경에서 안정적이고 비용 효율적으로 운영할 수 있는 AI 비디오 생성 파이프라인을 구축했습니다. 로컬 대비 AWS 서비스 환경에서는 초기 모델 로딩과 추론 속도 최적화가 중요한 과제였는데, ComfyUI 기반 워크플로우와 Ray Serve를 결합한 구조 덕분에 이러한 문제를 효과적으로 해결할 수 있었습니다. 특히 g6e.xlarge 인스턴스가 비용 대비 성능 면에서 가장 합리적인 선택지임을 실험을 통해 알게 되었고, 동일한 AMI 기반 환경을 손쉽게 확장할 수 있어 운영 효율성까지 확보했습니다.
앞으로는 더 다양한 오픈소스 비디오 생성 모델들을 같은 파이프라인 위에 올려 실험할 수 있고, EKS 기반 세밀한 오토스케일링까지 적용 가능하기 때문에, 빠르게 변화하는 AI 비디오 생성 기술을 안정적으로 서비스에 녹여낼 수 있을 것으로 기대합니다.