제품 사진 한 장으로 광고 영상 만들기 — n8n + Firecrawl + Veo 3.1 영상 생성 자동화
이 글과 연결된 글 보기
드래그로 이동 · 휠/두 손가락으로 확대 · 방향키로 글 선택 · 0으로 맞춤. 실선: 본문 링크, 점선: 시리즈·주제의 대표 연결.표시된 글 7편 목록
온라인 스토어의 상품 목록 페이지 URL을 폼에 입력하면, Firecrawl이 상품 이미지를 구조화해서 가져오고 fal.ai의 Veo 3.1 모델이 그 이미지를 4초짜리 세로형(9:16) 영상으로 바꿔줍니다. 이 워크플로우를 만들어보면 스크래핑으로 뽑은 이미지가 곧바로 영상 생성 API의 입력이 되는 흐름을 손에 익히게 되고, 그만큼 자주 등장하지 않는 "영상 생성 API 비용을 직접 계산해봤다"는 경험도 포트폴리오에 남길 수 있습니다.
이 워크플로우 하나로 보여줄 수 있는 것
이 시리즈에서 지금까지는 이미지를 만들거나(카드뉴스) 막힌 사이트를 우회해 텍스트를 긁는(403·Firecrawl) 자동화를 다뤘습니다. 이번 편은 그 둘을 합쳐 한 단계 더 나간 결과물, 즉 영상을 만듭니다. 짧은 흐름이지만 세 가지 역량이 드러납니다.
- 구조화 스크래핑 — 페이지 HTML을 그대로 긁는 게 아니라, AI에게 스키마를 주고 "이 형태로 뽑아 달라"고 시키는 경험
- 멀티모달 생성 API 연동 — 이미지를 입력으로 받아 영상을 출력하는 API를 HTTP Request 노드로 직접 호출하는 경험
- 비용을 계산하고 설계에 반영하는 감각 — 무료 티어가 없는 API 앞에서 "일단 다 돌려보자"가 아니라 처리 개수를 미리 제한하는 판단
세 번째가 가장 실무적입니다. 재고 알림이나 명함 OCR은 실행 비용이 사실상 0에 가까웠지만, 영상 생성은 한 번 잘못 돌리면 몇 달러가 순식간에 나갑니다. 이 워크플로우가 캔버스에 처리 개수 제한을 박아둔 이유도 거기에 있습니다.
배경
403 편에서는 HTTP Request가 막힌 자리에 Firecrawl을 끼워 텍스트와 가격을 긁었습니다. 이 워크플로우는 같은 Firecrawl을 다시 쓰지만 목적이 다릅니다. 텍스트가 아니라 상품 이미지를 뽑아서, fal.ai의 Veo 3.1 모델에 넘겨 짧은 제품 영상을 생성합니다. "스크래핑 → 생성 AI 입력"은 카드뉴스 편에서 이미지를 만들 때와 같은 뼈대이고, 이번에는 결과물만 이미지에서 영상으로 바뀝니다. 설치나 기본 노드 설명은 n8n으로 반복업무 자동화 포트폴리오 만들기를 참고하세요.
시작하기 전에 비용부터 짚습니다. Firecrawl 스크래핑은 403 편에서 다룬 대로 유료이고, 영상 생성 쪽은 단가가 훨씬 높습니다. 이 워크플로우가 호출하는 fal.ai의 Veo 3.1 Fast 이미지-투-비디오 모델은 2026년 8월 기준 720p·1080p에서 오디오 없이 초당 0.10달러, 오디오 포함 초당 0.15달러이고 4K는 세 배인 초당 0.30~0.35달러입니다. 이 워크플로우는 duration 4초·무음으로 지정했으니 영상 한 편에 대략 0.40달러이고, 8초로 늘리면 약 0.80달러입니다.
구조 — Trigger / Task / Target
- Trigger — n8n Form에 스토어의 상품 목록 페이지 URL을 입력하고 제출
- Task — Firecrawl로 상품 배열 추출 → 상품별로 분리 → 1개만 남기기 → 이미지 다운로드 → 프롬프트 조립 → fal.ai Veo 3.1 동기 호출 → 완성된 mp4 다운로드
- Target — 여기가 이 워크플로우의 흥미로운 지점입니다. 본 흐름은 mp4를 내려받는 데서 끝나고, 그 뒤로 연결된 노드가 없습니다. 왜 그런지는 아래 "원본 캔버스에 남아 있는 흔적"에서 다룹니다.
만들기
1. Form Trigger — URL 하나만 받기
Form Trigger 노드에 필드를 하나만 둡니다. 라벨은 "Url", 플레이스홀더는 "상품 목록 페이지 URL을 입력하세요" 정도로 안내하면 됩니다. 이 폼이 제출되면 $json.Url로 다음 노드에 값이 넘어갑니다.
2. Firecrawl — 상품 배열을 구조화해서 뽑기
Firecrawl 노드의 Operation을 Scrape로 두고 URL에 {{ $json.Url }}을 넣습니다. 여기서 핵심은 Format을 JSON으로 두고, Firecrawl 자체에 "이렇게 뽑아 달라"는 프롬프트와 스키마를 같이 넘긴다는 점입니다. 원본의 프롬프트는 이렇습니다.
Extract an array of products from this page. For each product, you must
extract the EXACT image URL string as it appears in the HTML source code.
Look in the <img> tag's src attribute or srcset attribute and copy the
complete URL exactly as written in the HTML, including the full domain,
path, file extension, and all query parameters. Do not modify, shorten,
or transform the URL in any way.
스키마는 products 배열 안에 상품마다 title(상품명)과 image_url(이미지 URL) 두 필드만 요구하도록 짧게 짰습니다. HTML을 직접 파싱하는 대신 Firecrawl에게 "URL을 있는 그대로, 절대 축약하지 말라"고 못박은 부분이 중요합니다. 상대경로나 CDN 리사이즈 파라미터가 섞이면 뒤에서 이미지 다운로드가 깨지기 때문입니다.
3. Split Out + Limit — 상품별로 나누고 하나만 남기기
Split Out 노드에서 Field To Split Out을 data.json.products로 두면 상품 하나가 아이템 하나가 됩니다. 바로 뒤에 Limit 노드가 파라미터 없이 붙어 있는데, 설정을 비워두면 n8n 공식 문서 기준 기본값(Max Items 1, Keep First Items)이 적용됩니다. 즉 상품 목록 페이지에 열 개가 잡혀도 실제로 영상까지 만드는 건 첫 번째 상품 하나뿐입니다. 앞서 계산한 비용을 생각하면, 이 노드가 사실상 "실수로 열 개를 한 번에 돌리는" 사고를 막는 안전장치인 셈입니다.
4. 이미지 다운로드 + 프롬프트 조립
HTTP Request 노드로 {{ $json.image_url }}을 그대로 GET 해서 이미지를 바이너리로 받습니다. 이어지는 Set 노드는 fal.ai에 넘길 프롬프트 문자열 하나를 조립합니다. 원본 텍스트는 다음과 같습니다.
Generate a video that is going to be featured on a product page of an
e-commerce store. This is going to be for a clothing or fashion brand.
This video must feature this exact same person that is provided on the
first and last frame reference images and the article of clothing in
the first and last frame reference images.
In this video, the model should strike multiple poses to feature the
article of clothing so that a person looking at this product on an
ecommerce website has a great idea how this article of clothing will
look and feel.
Constraints:
- No music or sound effects.
- The final output video should NOT have any audio.
- Muted audio.
- Muted sound effects.
이 프롬프트는 패션·의류 카테고리와 "사진 속 인물"을 전제로 쓰여 있습니다. 스크래핑은 카테고리를 가리지 않지만 프롬프트는 하드코딩돼 있어서, 다른 카테고리 제품엔 이 문단을 새로 써야 자연스러운 결과가 나옵니다.
5. fal.ai Veo 3.1 호출
HTTP Request 노드로 fal.ai를 직접 호출합니다.
- Method:
POST - URL:
https://fal.run/fal-ai/veo3.1/fast/image-to-video - Authentication: Generic Credential Type → HTTP Header Auth (fal.ai API 키를 Authorization 헤더로)
- Body(JSON):
{
"prompt": {{ JSON.stringify($node['set_prompt'].json.prompt) }},
"image_url": "{{ $('fetch_image').item.json.image_url }}",
"duration": 4,
"aspect_ratio": "9:16"
}
image_url에는 이미지 다운로드 단계에서 받은 바이너리가 아니라 Firecrawl이 뽑은 원본 URL을 그대로 넘깁니다. fal.ai 쪽에서도 그 URL에 접근할 수 있어야 하므로, 로그인 뒤에만 보이는 이미지에는 이 방식이 통하지 않습니다. duration은 4·6·8초 중에서, aspect_ratio는 9:16 세로형으로 지정해 인스타그램 스토리·릴스에 바로 올릴 수 있는 비율로 뽑습니다.
이어지는 영상 다운로드 노드는 응답의 {{ $json.video.url }}을 GET 해서 mp4를 받아옵니다. 원본에는 HTTP Header Auth 크리덴셜이 붙어 있지만, 복제해 만든 두 번째 사본에서는 이 인증 설정이 빠져 있습니다 — fal.ai 영상 URL은 인증 없이도 열리기 때문에 뒤에서 걷어낸 흔적으로 보입니다.
원본 캔버스에 남아 있는 흔적
이 워크플로우에는 실행 이력 대신 캔버스에 직접 남겨둔 메모와, 완성되지 못한 채 옆에 붙어 있는 실험 노드들이 있습니다.
가장 눈에 띄는 건 "비디오 생성 에러"라는 제목의 스티키 노트입니다. Wan-Pro 모델을 먼저 써봤는데 메모리 에러로 막혔고, 그 모델은 응답 방식이 비동기(Asynchronous)에 처리 시간도 길었다고 적혀 있습니다. 반면 지금 쓰는 Veo 3.1은 동기(Synchronous) 방식으로 정상 동작했다고 표시돼 있습니다. 그래서 이 워크플로우에는 폴링 로직(대기 후 상태 확인을 반복하는 구조)이 없습니다 — fal.ai의 동기 엔드포인트를 호출하면 generate_video 노드가 응답이 올 때까지 그 자리에서 기다리기 때문입니다.
다만 fal.ai의 모든 모델이 이렇게 동작하지는 않습니다. 큐 방식 엔드포인트(queue.fal.run)를 쓰는 모델은 요청을 제출한 뒤 request_id로 상태를 따로 조회해야 합니다. 데이터팝콘의 다른 워크플로우 파일 하나가 이 패턴을 보여줍니다. 이미지 생성 요청 제출 → 10초 대기 → 상태 조회 → COMPLETED가 아니면 다시 대기로 돌아가는 루프를 If·Wait 노드로 구성해뒀습니다. 영상 생성 모델을 바꾸면 이런 폴링 구조가 필요해질 수 있습니다.
두 번째 흔적은 앞서 "구조" 절에서 미뤄둔 Target입니다. 캔버스에는 원본 이미지와 완성 영상을 각각 구글 드라이브 폴더에 올리는 Google Drive 노드가 두 개 더 있지만, 둘 다 disabled 상태로 본 흐름과 연결돼 있지 않습니다. 지금 그대로 실행하면 영상은 n8n 실행 기록의 바이너리 데이터로만 남고 자동으로 어디에도 전달되지 않습니다. 완성된 자동화로 쓰려면 이 두 노드를 켜고 연결하거나 Slack·Drive 노드를 새로 붙여야 합니다.
세 번째는 본 흐름과 분리된 작은 실험 흐름입니다. 수동 트리거로 시작해 상품 이미지를 GPT-4o-mini Vision(Analyze image 노드)에 넘기는데, 이 노드도 disabled 상태이고 옆 스티키 노트에는 "사람인지 여부 체크"라는 한 줄만 적혀 있습니다. 영상 프롬프트가 "사진 속 인물"을 전제로 쓰여 있다는 점과 묶어보면, 인물 착용샷과 제품만 있는 사진을 구분해 프롬프트를 다르게 적용하려던 아이디어로 보이지만 완성되지는 않았습니다.
한계
- 완성 영상의 목적지가 없습니다 — Drive 업로드 노드가 disabled 상태입니다. 켜서 연결하거나 Slack 알림을 붙이는 게 실제로 쓰기 위한 첫 작업입니다.
- 한 번에 상품 하나만 처리합니다 — Limit 노드 기본값 때문입니다. 여러 상품을 돌리려면 Limit을 지우고 SplitInBatches나 Wait 노드로 순차 처리해야 비용이 한꺼번에 튀지 않습니다.
- 프롬프트가 패션 카테고리에 고정돼 있습니다 — 인물이 없는 제품(가전·식품 등)에는 프롬프트를 새로 써야 합니다.
- 에러 처리가 없습니다 — Firecrawl이나 fal.ai 호출이 실패해도 워크플로우는 그대로 멈춥니다. 재고 알림 편의 Filter처럼 "0개면 조용히 종료"하는 안전장치가 이 흐름엔 없습니다.
포트폴리오·면접 포인트
포트폴리오로 가져가려면 두 가지를 직접 채워보길 권합니다. 하나는 disabled 상태인 Drive 업로드 노드를 켜고 연결해 "영상이 실제로 어딘가에 도착하는" 완성된 흐름으로 만드는 것, 다른 하나는 프롬프트를 자기가 고른 제품 카테고리에 맞게 새로 쓰는 것입니다. 후자는 fal.ai의 다른 모델 문서를 읽고 파라미터를 비교해야 하는데, 이 과정 자체가 "생성 AI API를 조사해서 워크플로우에 반영해봤다"는 이야기가 됩니다.
비용은 반드시 미리 계산하고 시작하세요. 4초·무음 기준 한 편에 약 0.40달러이고, 길이나 오디오를 더하면 그 이상입니다. 포트폴리오 시연용이라면 상품 한두 개로 영상 한두 편만 만들어 캔버스 스크린샷과 영상 클립을 남기는 정도로 충분합니다. 여러 상품을 배치로 돌리는 실험은 비용을 감당할 수 있을 때만 하세요.
면접에서는 "왜 폴링 로직이 없나요?"라는 질문이 나올 수 있습니다. fal.ai의 동기 엔드포인트라 응답이 올 때까지 HTTP Request 노드가 그 자리에서 기다린다는 점, 원래 시도했던 모델은 비동기라 메모리 에러로 포기했다는 점까지 답하면 왜 이렇게 구성됐는지 이해하고 있다는 인상을 줍니다.
다음 액션
이 시리즈에서 지금까지는 콘텐츠를 만드는 자동화만 다뤘습니다. 마지막 편에서는 방향이 바뀝니다. 이미 집행한 페이스북 광고의 성과 데이터를 AI가 읽고 리포트로 정리하는 워크플로우로, "만들기"에서 "만든 걸 평가하기"로 넘어가면 콘텐츠 자동화 한 사이클을 포트폴리오에 전부 담을 수 있습니다.
