Skip to main content

아키텍처

이 문서는 vivid-ai 프로젝트의 전체 시스템 아키텍처와 생성/후처리 데이터 흐름을 설명합니다.

현재 기준의 인프라 결정은 현재 권장 인프라 플랜을 우선합니다.

현재 기준 요약

local: MacBook Docker Compose + AWS SQS/S3 + local Redis/Postgres
dev: EC2 Docker Compose + AWS SQS/S3 + ElastiCache/RDS
prod: Kubernetes + AWS SQS/S3 + ElastiCache/RDS

생성 파이프라인은 다음처럼 분리합니다.

backend
-> SQS computation queue
-> ai-node-agent
-> ComfyUI
-> external generation API node
-> S3 raw/
-> SQS post-processing queue
-> post-processing-worker
-> S3 creations/
-> backend / Postgres / Redis

핵심 아키텍처 원칙

  • 환경 분리: local, dev, prod 환경은 서로 영향을 주지 않도록 SQS queue, S3 bucket/prefix, Postgres, Redis를 분리합니다.
  • Managed boundary: Postgres/RDS, SQS, S3, ElastiCache는 managed resource로 유지하고, frontend/backend/ComfyUI/ai-node-agent/post-processing-worker는 컨테이너 실행부로 관리합니다.
  • 생성/후처리 분리: ai-node-agent는 generation orchestration을 담당하고, post-processing-worker는 thumbnail, preview, resize/crop, upload, backend sync를 담당합니다.
  • 실시간 피드백: ai-node-agent와 backend는 relay/Redis Pub/Sub을 통해 작업 상태를 공유하고, backend는 사용자에게 실시간 상태를 전달합니다.

공통 인프라

  • Containerized generation runtime: ComfyUI와 ai-node-agent를 컨테이너로 실행합니다. API 노드 기반 workflow를 기본으로 운영합니다.
  • Public Agent Relay + Redis: ai-node-agent는 public relay에 상태/이벤트를 전달하고, backend는 Redis Pub/Sub과 progress cache를 통해 사용자에게 realtime update를 제공합니다.
  • Managed data plane: SQS, S3, RDS, ElastiCache는 실행부와 분리된 managed service로 유지합니다.

Local 개발 환경 아키텍처

로컬 머신(MacBook 등)에서 전체 실행부를 Docker Compose로 띄우고, SQS/S3는 실제 AWS cloud resource를 사용합니다.

구성 요소

  • Frontend container: Next.js 애플리케이션.
  • Backend container: NestJS API, generation queue enqueue, realtime gateway.
  • ComfyUI container: workflow 실행 엔진.
  • ai-node-agent container: SQS generation queue polling, ComfyUI orchestration, S3 raw/ upload, post-processing queue enqueue.
  • post-processing-worker container: S3 raw/ download, thumbnail/preview/resize/crop/transcode, S3 creations/ upload, backend result sync.
  • Local Redis container: progress cache, pub/sub, websocket 보조 상태.
  • Local Postgres container: local 개발 전용 DB.
  • AWS Services:
    • SQS: vivid-ai-computation-queue-local, vivid-ai-post-processing-worker-queue-local
    • S3: local media bucket. raw/, creations/, generation/source/, generation/work-input/, temp/ prefix를 사용합니다.

데이터 흐름


Dev 환경 아키텍처

Dev 환경은 EC2에서 Docker Compose로 실행부를 운영하고, Postgres/Redis/SQS/S3는 AWS managed resource를 사용합니다.

구성 요소

  • EC2 Docker Compose:
    • frontend
    • backend
    • ComfyUI
    • ai-node-agent
    • post-processing-worker
  • AWS Managed Services:
    • SQS: vivid-ai-computation-queue-dev, vivid-ai-post-processing-worker-queue-dev
    • S3: dev media bucket. local과 같은 prefix 구조를 사용합니다.
    • RDS: PostgreSQL database.
    • ElastiCache: dev Redis/Valkey.

데이터 흐름


Prod 환경 아키텍처

Prod 환경은 Kubernetes에서 실행부를 역할별 Pod/Deployment로 운영하고, Postgres/Redis/SQS/S3는 AWS managed resource를 사용합니다.

구성 요소

  • Kubernetes Deployments:
    • frontend
    • backend
    • ai-node-agent + ComfyUI Pod
    • post-processing-worker
  • AWS Managed Services:
    • SQS: prod generation/post-processing queues.
    • S3: prod media bucket. local/dev와 같은 prefix 구조를 사용합니다.
    • RDS: PostgreSQL database.
    • ElastiCache: prod Redis/Valkey.

ai-node-agent와 ComfyUI는 같은 Pod의 두 컨테이너로 묶고 input/output/temp shared volume을 공유하는 구성을 우선 고려합니다.

Pod: ai-node
- container: comfyui
- container: ai-node-agent
- shared volume: input/output/temp

시스템 아키텍처 상세 (이미지/비디오 생성 파이프라인 중심)

AWS Cloud & Backend Services

  • 진입점:

    • ALB/Ingress: prod Kubernetes 환경의 웹/앱 트래픽 진입점입니다. Host-based 및 Path-based 라우팅을 통해 Front/Back 서비스를 Pod으로 분산합니다.
    • 배스천 호스트: 관리자가 SSH 터널링을 통해 프라이빗 리소스(예: RDS, ElastiCache)에 접근해야 할 때 사용하는 보안 게이트웨이입니다.
    • API Gateway: (향후 도입 예정) AI 연산 요청의 최전방 인증, 스로틀링, 트래픽 통제를 담당합니다.
  • 핵심 실행부:

    • Frontend (Next.js): 사용자 인터페이스 제공.
    • Backend (Nest.js):
      • API 버전 관리: 모든 백엔드 API는 /api/v1 접두사를 사용하여 버전 관리를 시작합니다. 헬스 체크 엔드포인트는 /api/v1입니다.
      • 워크플로우 템플릿 관리: 관리자가 등록한 ComfyUI 워크플로우와 동적 UI 생성을 위한 userInputs 스키마를 DB에 저장하고 관리합니다 (/api/v1/workflows 엔드포인트).
      • 창작물(Creation) 관리: POST /api/v1/generations 요청 시, Creation 테이블에 레코드를 PENDING 상태로 생성합니다. GET /api/v1/creations를 통해 사용자에게 창작물 목록을 제공합니다.
      • 동적 워크플로우 생성: 사용자가 템플릿과 파라미터를 보내면(POST /api/v1/generation/from-template), DB에서 템플릿을 조회한 후 userInputs 스키마를 기반으로 사용자 요청(modifications)을 실제 ComfyUI 워크플로우 JSON의 정확한 위치에 주입하여 최종 워크플로우를 완성합니다.
      • 비즈니스 로직, 이메일/비밀번호 인증, DB/Redis 접근 관리.
      • SqsProducerService를 통해 최종 생성된 워크플로우 JSON과 creationId를 환경에 맞는 연산 SQS 큐에 작업을 발행합니다.
    • AiRealtimeGateway (Nest.js, 백엔드에 포함):
      • relay가 shared Redis에 반영한 채널(ai-comfyui-events)을 구독하여 모든 실시간 이벤트(작업 진행 상황, ComfyUI 상태, AI 연산 노드 시스템 모니터링 데이터)를 수신합니다.
      • 수신한 이벤트를 userId에 따라 적절한 Socket.IO 룸으로 필터링하여 프론트엔드 클라이언트에 실시간으로 전달(Push)합니다. 관리자 클라이언트에게는 모든 노드의 모니터링 데이터를 전송합니다.
      • RedisIoAdapter를 사용하여 백엔드 서버가 여러 인스턴스로 확장되어도 WebSocket 메시지를 안정적으로 브로드캐스팅합니다.
    • 후처리 워커 (Post-processing Worker):
      • 환경에 맞는 후처리 SQS 큐를 폴링하여 메시지(S3 raw/ 경로 포함)를 가져옵니다.
      • S3 raw/ prefix에서 원본 파일을 다운로드합니다.
      • ffmpeg를 사용하여 비디오 최적화(Preview 생성) 및 썸네일 생성 작업을 수행합니다.
      • 최종 결과물과 썸네일(또는 Preview 영상)을 S3 creations/ prefix에 업로드합니다.
      • 백엔드 API(PATCH /api/v1/creations/:id)를 호출하여 데이터베이스의 Creation 레코드 상태를 COMPLETED로 업데이트하고 S3 경로를 저장합니다.
      • Kubernetes 배포 특이사항:
        • 이미지 아키텍처: EKS 워커 노드가 주로 linux/amd64 아키텍처를 사용하므로, Docker 이미지 빌드 시 --platform linux/amd64를 명시해야 합니다. (예: Apple Silicon Mac에서 빌드 시).
        • ECR 이미지 풀 권한: 파드가 ECR(Elastic Container Registry)에서 이미지를 가져오려면 IRSA(IAM Role for Service Accounts)를 통해 Service Account에 ECR pull 권한이 있는 IAM Role이 연결되어야 합니다 (정책 예: AmazonEC2ContainerRegistryReadOnly).
        • 환경 변수 주입: SQS URL, S3 버킷 이름, 백엔드 API URL 등의 민감한 정보는 Kubernetes Secret으로 관리되며, Deployment에서 컨테이너의 환경 변수로 secretKeyRef를 통해 안전하게 주입됩니다.
        • 네트워크 접근: 워커 노드가 프라이빗 서브넷에 배포된 경우, ECR API, ECR Docker, S3 서비스에 대한 VPC Endpoint 생성이 필요할 수 있습니다. 관련 상세 내용은 인프라 및 배포 문서를 참조하십시오.
  • 데이터 흐름:

    • RDS (PostgreSQL) & Shared Redis (ElastiCache):
      • 보안: 주요 데이터와 캐시를 저장하는 이 서비스들은 외부 인터넷에서 직접 접근할 수 없는 프라이빗 서브넷에 위치하여 보안을 강화합니다.
      • 접근 방식:
        • local backend: Docker Compose의 local Redis/Postgres에 연결합니다.
        • dev backend: EC2에서 RDS/ElastiCache와 통신합니다.
        • prod backend: Kubernetes/VPC 내부에서 RDS/ElastiCache와 통신합니다.
        • ai-node-agent: RDS에 직접 붙지 않고 SQS/S3와 relay/backend API를 통해 필요한 상태/작업 정보만 주고받습니다.
    • SQS Queue (Computation Queue): vivid-ai-computation-queue-{env} 큐를 통해 백엔드로부터 ai-node-agent로 전달될 이미지/비디오 생성 작업 큐.
    • SQS Queue (Post-processing Queue): ai-node-agent로부터 post-processing-worker로 전달될 생성 완료 알림 및 임시 파일 경로 큐 (vivid-ai-post-processing-worker-queue-{env}).
    • S3 raw storage: 환경별 media bucket의 raw/ prefix에 생성 raw output을 임시 저장.
    • S3 final storage: 환경별 media bucket의 creations/ prefix에 후처리가 완료된 최종 원본, 썸네일, preview를 저장 및 제공.

External Provider Boundary

  • ComfyUI workflow는 API 노드를 통해 외부 이미지/비디오 생성 provider를 호출합니다.
  • provider API key는 frontend에 노출하지 않고 ComfyUI/ai-node-agent 실행 환경의 secret으로만 주입합니다.
  • provider 비용은 provider 계정에서 먼저 차감되고, 사용자 크레딧 차감은 backend의 billing/credit 로직에서 별도로 처리합니다.
  • provider rate limit, 장애, 비용은 ai-node-agent의 retry/error handling과 backend의 job 상태 관리로 흡수합니다.

실시간 파이프라인

ai-node-agent와 ComfyUI에서 발생하는 작업 진행 상황(시작, 진행률, 완료 등)은 public relay + Redis 기반의 실시간 파이프라인을 통해 프론트엔드에 전달됩니다. 이를 통해 사용자는 자신의 작업 상태를 실시간으로 확인할 수 있습니다.

자세한 내용은 실시간 파이프라인 아키텍처 문서를 참조하십시오.

프론트엔드 낙관적 업데이트 (Optimistic Update)

사용자가 갤러리 페이지에 진입할 때 REST API(GET /creations)를 호출하면, 최신 작업이 아직 데이터베이스에 반영되지 않아 PENDING 상태이거나 이미지가 없는([]) 상태일 수 있습니다. 그러나 화면에는 이미지가 즉시 표시됩니다.

이는 프론트엔드가 **WebSocket(IMAGE_ADDED 이벤트)을 통해 수신한 이미지 데이터를 React Query 캐시(['creations'])에 직접 주입(Injection)**하기 때문입니다. API를 다시 호출하지 않고도 로컬 캐시를 즉시 업데이트함으로써, 사용자는 지연 없이 실시간으로 생성된 결과물을 확인할 수 있습니다.

워크플로우 템플릿 기반 생성 흐름 (Multi-Variant System)

이 시스템의 핵심은 워크플로우 템플릿의 유연성과 Feature Key 기반 추상화입니다.
다양한 모델(예: OmniGen, Qwen)은 입력 이미지/비디오 개수에 따라 서로 다른 ComfyUI 워크플로우 구조를 요구하므로, 워크플로우 그룹(Workflow Group) + 워크플로우 변형(Workflow Variant) + 워크플로우 매핑(Workflow Mapping) 3계층을 사용합니다.

핵심 개념: 1:N 구조

  • Workflow Group (1): 갤러리에 표시되는 '상품'의 단위입니다. (예: "OmniGen Image Edit") 제목, 설명, 가격, 카테고리 등 메타데이터를 관리합니다.
  • Workflow Variant (N): 실제 '구현체'입니다. 입력 데이터의 형태(예: 텍스트 1개 + 이미지 2개)에 따라 실행되어야 할 구체적인 ComfyUI JSON과 매핑 정보를 담고 있습니다.
  • Workflow Mapping (Feature Key): 서비스 기능 키(PERSONA_PROFILE_IMAGE_GEN 등)와 워크플로우 그룹을 연결합니다.

4단계 워크플로우 생성 프로세스 (관리자)

관리자는 다음 4단계를 통해 하나의 완벽한 기능을 정의합니다.

  1. 1단계: 기본 정보 (Basic Info)
    • 워크플로우 그룹의 제목, 설명, 가격, 카테고리(TEXT_TO_IMAGE, IMAGE_TO_IMAGE 등)를 설정합니다.
  2. 2단계: 변형 구성 (Variant Configuration)
    • 해당 기능이 지원할 다양한 입력 케이스를 정의합니다.
    • 비합성 미디어 입력은 최대 개수로 설정합니다. 입력 생략이 가능한 그래프는 [변형 A: 이미지 최대 16장] 하나로 여러 입력 개수를 처리할 수 있습니다. 그래프 구조나 처리 방식이 다른 경우 별도 변형을 추가합니다.
  3. 3단계: 필수 매핑 (Required Mappings)
    • 각 변형별로 시스템이 제어할 표준 입력을 매핑합니다.
    • 통일성을 위해 prompt 대신 text_1, **text_2**를 사용합니다.
    • 이미지 입력은 image_1, image_2, …, image_N 형식으로 설정한 슬롯 수만큼 매핑합니다. 기능별 입력 역할 계약은 별도로 유지합니다.
    • 현재 Persona/Coordi/Scene MVP 계약에서는 Scene 생성 시 image_1을 반드시 내 outfit으로 사용합니다.
  4. 4단계: 사용자 입력 설정 (User Inputs)
    • 각 변형별로 사용자에게 보여줄 UI 폼(슬라이더, 드롭다운 등)을 독립적으로 설정합니다.

실행 흐름 (Execution Logic)

사용자는 복잡한 내부 구조를 알 필요가 없습니다.

  1. 사용자 요청: 사용자가 "OmniGen" 모델을 선택하고 이미지 2장을 업로드하여 생성을 요청합니다.
  2. 전송: 프론트엔드는 featureKey(권장) 또는 workflowId와 입력 데이터(text_1, image_1, image_2...)를 전송합니다.
  3. 라우팅 (Backend): text 개수는 정확히 일치해야 합니다. 비합성 Variant는 요청의 image/video/audio 개수가 각각 설정한 최대 개수 이하면 후보가 됩니다. 합성 참조 이미지 Variant는 라우팅 시점의 미디어 개수도 정확히 일치해야 합니다.
  4. 우선순위: 미디어 여유 슬롯 수의 합이 작은 후보를 우선 선택합니다. 합성 Variant의 점수는 0이며, 동률이면 sortOrder 오름차순, 이후 전달된 Variant 배열 순서를 적용합니다.
  5. 실행용 JSON 구성: 비합성 Variant에서 미디어 개수가 설정과 다르면 매핑된 미사용 미디어 입력과 삭제된 로더를 참조하는 연결을 제거합니다. 원본 템플릿은 유지하며, 합성 Variant와 미디어 개수가 모두 일치하는 경우에는 제거하지 않습니다. 필수 전처리 분기 전체를 자동으로 정리하지는 않으므로 입력 생략이 가능한 그래프로 설계해야 합니다. 등록 예시와 제한은 워크플로우 API 명세를 참고합니다.

End-to-End 전체 프로세스 상세

1단계: 생성 요청 (Frontend → Backend)

  1. 사용자 액션 (Frontend): 사용자가 웹사이트에서 프롬프트를 입력하거나 옵션을 선택한 후 '생성하기' 버튼을 클릭합니다.
  2. API 호출 (Frontend): 프론트엔드는 인증 토큰(JWT)과 함께 백엔드의 POST /api/v1/generation/... API를 호출합니다.

2단계: 작업 접수 및 대기열 추가 (Backend)

  1. 요청 접수 (Backend): GenerationService는 사용자의 요청을 검증합니다.
  2. DB 레코드 생성 (Backend): GenerationService는 데이터베이스(PostgreSQL)에 이 생성 작업에 대한 Creation 레코드를 생성합니다. 이때 초기 상태는 status: 'PENDING' 입니다.
  3. SQS 메시지 발행 (Backend): 백엔드는 처리해야 할 작업 정보(userId, creationId, workflow 등)를 담아 해당 환경의 연산 SQS 큐 (Computation Queue) 에 메시지를 보냅니다.

3단계: AI 연산 및 원본 저장 (AI Node Agent)

  1. 작업 수신 (AI Agent): ai-node-agentlocaldev 연산 SQS 큐를 동시에 주시하다가, 새 작업 메시지를 받습니다.
  2. AI 연산 (AI Agent): 메시지 정보를 바탕으로 ComfyUI API를 호출하여 이미지/비디오 생성을 시작합니다. 진행 상황과 heartbeat는 agent 전용 WebSocket을 통해 relay로 실시간 중계됩니다.
  3. 결과물 업로드 (AI Agent): 생성이 완료되면, 결과물(이미지/비디오 원본)을 해당 환경 media bucket의 raw/ prefix 에 업로드합니다.
  4. 후처리 요청 (AI Agent): 업로드 완료 후, ai-node-agentpost_processing 상태를 relay의 HTTP ingress로 반영하고, 후처리 SQS 큐 에 메시지를 보냅니다.

4단계: 후처리 및 최종 저장 (Post-processing Worker)

  1. 작업 수신 (후처리 작업자): post-processing-worker후처리 SQS 큐를 주시하다가 새 작업을 받습니다.
  2. 파일 다운로드 (후처리 작업자): 메시지에 포함된 raw_s3_path를 이용해 S3 raw/ prefix에서 원본 파일을 다운로드합니다.
  3. 후처리 실행 (후처리 작업자):
    • 이미지: 썸네일 이미지 생성.
    • 비디오: ffmpeg를 사용하여 웹 최적화된 미리보기 MP4 (H.264, Muted) 생성.
  4. 최종 업로드 (후처리 작업자): 후처리가 완료된 최종 결과물과 썸네일/미리보기를 S3 creations/ prefix에 업로드합니다.

5단계: 상태 업데이트 및 완료 (Post-processing Worker → Backend)

  1. 상태 업데이트 API 호출 (후처리 작업자): post-processing-worker는 백엔드의 PATCH /api/v1/creations/:id API를 호출합니다.
  2. DB 업데이트 요청 (후처리 작업자): API 요청 본문에 최종 상태(status: 'COMPLETED')와 S3 경로(s3Path, thumbnailPath)를 전달합니다.
  3. DB 최종 업데이트 (Backend): 백엔드는 DB를 업데이트하고, Redis Pub/Sub으로 최종 completed 이벤트를 발행합니다.
  4. 클라이언트 업데이트 (Frontend): 프론트엔드는 이벤트를 수신하여 작업 상태를 '완료'로 변경하고 결과물을 표시합니다.

6단계: 갤러리 조회 (Frontend ↔ Backend)

  1. 갤러리 페이지 접속 (Frontend): 사용자가 '내 생성물' 갤러리 페이지로 이동합니다.
  2. 생성물 목록 요청 (Frontend): 프론트엔드는 백엔드의 GET /api/v1/creations API를 호출합니다.
  3. Pre-signed URL 생성 (Backend): 백엔드는 DB에서 조회한 생성물(COMPLETED)의 S3 경로를 Pre-signed URL로 변환하여 반환합니다.

워크플로우 매핑 시스템 (Workflow Mapping System)

단순 도구를 넘어 서비스 중심의 기능을 제공하기 위한 추상화 레이어입니다.

핵심 개념

  • Feature Key: 서비스 기능 단위 고유 키 (예: PERSONA_PROFILE_IMAGE_GEN, PERSONA_COORDI_GEN, PERSONA_OUTFIT_GEN, PERSONA_TO_IMAGE_GEN, PERSONA_TO_VIDEO_GEN)
  • Visibility: PUBLIC / ADMIN_ONLY / PRIVATE 단계로 노출 제어
  • Workflow Group Mapping: Feature Key를 실제 구현체(WorkflowGroup)에 연결
  • Variant Routing: 텍스트 개수 일치 + 비합성 미디어 최대 개수 조건(합성은 개수 일치), 미디어 여유 슬롯 점수와 sortOrder 우선순위 적용

작동 원리 (Persona → Coordi → Scene 기준)

  1. Selection: 유저가 기능(예: 페르소나 생성, 페르소나 i2i)을 선택합니다.
  2. Mapping Resolution: 시스템이 Feature KeyworkflowGroupId를 조회합니다.
  3. Payload Assembly:
    • image_1: 내 outfit
    • image_2, image_3: Scene 선택 참조
    • text_1, text_2: coordi + scene 최종 프롬프트
  4. Smart Routing: 백엔드는 텍스트 개수가 일치하고 미디어 개수 조건을 만족하는 Variant 중 여유 슬롯 점수와 sortOrder 기준으로 선택합니다. 비합성은 미디어 최대 개수, 합성은 라우팅 시점의 정확한 미디어 개수를 사용합니다.
  5. Persistence: creations.snapshot(jsonb)를 재현성 원본으로 저장하고(기본 키: text_1, text_2, personaId, featureKey, workflowGroupId, variantId, 조건부 키: coordiId, outfitId), 조회 성능을 위해 resolved_prompt/resolved_negative_prompt/ratio/refs를 projection 컬럼으로 함께 저장합니다. 결과 단위 메타(seed, resultIndex, s3Path, thumbnailS3Path)는 creation_results에 저장하며, 관계는 Creation(1):CreationResult(N)를 따릅니다.

가시성 제어 (Visibility)

  • PUBLIC: 모든 일반 유저에게 공개.
  • ADMIN_ONLY: 관리자 페이지 및 관리자 권한 유저에게만 노출 (테스트 단계).
  • PRIVATE: 비공개 상태. 모든 UI에서 숨겨짐.