HiAPI
블로그

튜토리얼

hiapi API로 FLUX.1 [schnell]을 사용하여 이커머스 상품 이미지 생성하기

바로 실행 가능한 curl + Python 가이드로, 카탈로그 화이트 배경 이미지, 연출 이미지, 변형 이미지를 다루며 이미지당 $0.005입니다.

Jun 28, 2026
hiapi API로 FLUX.1 [schnell]을 사용하여 이커머스 상품 이미지 생성하기

카탈로그 촬영은 SKU를 온라인에 게시할 때 가장 느린 단계입니다. 스튜디오를 예약하고, 스타일리스트를 부르고, 소프트박스를 설치하고, 리터처를 고용하는 일은 $24짜리 머그컵 하나에는 전혀 말이 되지 않습니다. hiapi의 flux-schnell/text-to-image는 쓸 만한 화이트 온 화이트 배경 상품 이미지, 연출 히어로 이미지, 색상/소재 변형 이미지를 이미지당 $0.005, 엔드투엔드 약 30초에 얻을 수 있게 해줍니다. 이 수준에서는 더 이상 컷당 촬영 비용을 고민하지 않고 프롬프트와 프롬프트 배치를 생각하게 됩니다.

이 가이드는 정확한 워크플로를 안내합니다. 상품 상세 페이지에 실제로 필요한 네 가지 이미지 유형, 모델이 깔끔한 무이음새 배경에 머물도록 하면서 나무 테이블을 지어내지 않게 하는 프롬프트 구조, 터미널이 다운되지 않으면서 /v1/tasks로 100개의 변형을 일괄 생성하는 방법, 그리고 schnell이 할 수 없는 몇 가지 일들에 대해 미리 계획해야 하는 이유입니다.

카탈로그 작업이 flux-schnell에 특히 적합한 이유

hiapi에는 충실도가 더 높은 모델들(flux-2, flux-1.1-pro, nano-banana-pro)도 있으며, 브랜드 캠페인의 메인 이미지 한 장이라면 그것들을 사용해야 합니다. 하지만 카탈로그에서는 계산이 다릅니다.

  • 200개 SKU 신상품 출시에서 SKU당 대략 4장의 이미지(화이트 배경, 연출, 비율 참고, 탑다운)가 필요하다면 = 800장입니다. $0.005로 계산하면 $4입니다. $0.05면 $40입니다. $0.17이면 $136입니다.
  • schnell은 1–4 샘플링 스텝 내에 생성을 완료하므로 엔드투엔드(대기열 + 생성 + 다운로드)가 우리 실행에서 약 30초입니다. flux-2와 nano-banana-pro는 몇 배 더 느립니다.
  • 카탈로그 이미지는 상품 상세 페이지에서 600px 너비로, 그리드에서 200px로 표시됩니다. 이 크기에서는 고급 모델의 충실도 여유가 전혀 눈에 띄지 않습니다. 프롬프트 준수도와 종횡비의 타당성이 더 중요합니다.

이 트레이드오프는 실제로 존재합니다. schnell은 라벨의 작고 읽을 수 있는 텍스트에 약하고, 사람이 제품을 들고 있는 경우에 약하며(이 경우 nano-banana-pro를 사용하세요), 복잡한 브랜드 로고에 분명히 약합니다. "제품을 깔끔한 배경에 보여주고, 세 가지 색상으로, 탑다운 및 4분의 3 각도로"—즉 실제 본업—에서는 탁월합니다.

실제로 생성해야 할 이미지 목록

프롬프트를 쓰기 전에 상품 상세 페이지 템플릿에 무엇이 필요한지 먼저 정하세요. 유용한 시작 세트는 다음과 같습니다.

이미지 유형 종횡비 용도
화이트 배경 상품 이미지 1:1 상품 상세 페이지 메인 이미지, 그리드 썸네일
4분의 3 각도 스튜디오 촬영 이미지 4:3 상품 상세 페이지 갤러리 두 번째 이미지
연출 / 상황 이미지 3:2 상품 상세 페이지 갤러리, 소셜 광고
탑다운 플랫레이 이미지 1:1 상품 상세 페이지 갤러리, 비율 참고
색상/소재 변형 그리드 변형당 1:1 색상 선택기 썸네일

hiapi의 flux-schnell은 1:1, 3:2, 2:3, 4:3, 3:4, 5:4, 4:5, 16:9, 9:16, 21:9, auto를 지원합니다. 다른 비율은 작업 생성 시 거부됩니다.

한 번 실행: curl로 단일 상품 이미지 생성하기

토큰이 작동하는지 확인하는 최소 요건입니다. 내 마지막 실행은 엔드투엔드로 31초가 걸렸습니다.

TOKEN="$HIAPI_API_KEY"

# 1. 작업 생성
TASK=$(curl -s -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-schnell/text-to-image",
    "input": {
      "prompt": "Product packshot: matte ceramic coffee mug, soft beige glaze, centered on seamless white cyclorama, soft diffused studio lighting, subtle contact shadow, 50mm photography, no props, ecommerce hero, clean negative space top and bottom",
      "aspect_ratio": "1:1"
    }
  }' | jq -r '.data.taskId')

echo "task: $TASK"

# 2. 완료될 때까지 폴링 (success | fail)
while true; do
  STATE=$(curl -s "https://api.hiapi.ai/v1/tasks/$TASK" \
    -H "Authorization: Bearer $TOKEN")
  STATUS=$(echo "$STATE" | jq -r '.data.status')
  echo "  status=$STATUS"
  [ "$STATUS" = "success" ] && break
  [ "$STATUS" = "fail" ] && { echo "$STATE" | jq '.data.error'; exit 1; }
  sleep 4
done

# 3. 다운로드 (출력 URL은 만료됨; 즉시 가져오세요)
URL=$(echo "$STATE" | jq -r '.data.output[0].url')
curl -s -o packshot.jpg "$URL"
echo "saved packshot.jpg"

flux-schnell에서 특히 주의해야 할 특이점: 이 모델의 입력 스키마는 aspect_ratio를 받지만 resolution은 거부합니다. hiapi의 다른 이미지 모델(예: flux-2, nano-banana-pro)은 둘 다 받습니다. flux-schnell에 {"aspect_ratio": "1:1", "resolution": "1K"}를 제출하면 다음이 반환됩니다.

{
  "code": 400,
  "message": "additional properties not allowed: resolution"
}

resolution을 제거하세요. 필요하지 않습니다. schnell은 선택한 종횡비에 따라 모델 기본 크기로 출력합니다.

프로덕션: Python으로 일괄 생성하기

실제 카탈로그 작업은 많은 작업을 제출하고, 출력이 완료되면 다운로드하며, 실패한 케이스를 재시도합니다. 다음은 한 SKU 시리즈를 프로토타이핑할 때 사용한 스크립트입니다.

"""Batch packshot generator for flux-schnell on hiapi.

Reads a CSV of (sku, prompt, aspect_ratio), submits all jobs, polls in parallel,
downloads finished outputs to ./out/{sku}.jpg. Total cost = $0.005 * len(csv).
"""
import csv
import json
import os
import subprocess
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path

API = "https://api.hiapi.ai/v1/tasks"
TOKEN = os.environ["HIAPI_API_KEY"]
OUT = Path("./out"); OUT.mkdir(exist_ok=True)


def _post(path, body):
    r = subprocess.run(
        ["curl", "-s", "-X", "POST", path,
         "-H", f"Authorization: Bearer {TOKEN}",
         "-H", "Content-Type: application/json",
         "-d", json.dumps(body)],
        capture_output=True, text=True, timeout=60,
    )
    return json.loads(r.stdout)


def _get(path):
    r = subprocess.run(
        ["curl", "-s", path, "-H", f"Authorization: Bearer {TOKEN}"],
        capture_output=True, text=True, timeout=30,
    )
    return json.loads(r.stdout)


def render(sku: str, prompt: str, ratio: str) -> tuple[str, str]:
    """Submit → poll → download. Returns (sku, status). status='ok' or 'fail:<reason>'."""
    body = {"model": "flux-schnell/text-to-image",
            "input": {"prompt": prompt, "aspect_ratio": ratio}}
    resp = _post(API, body)
    task_id = (resp.get("data") or {}).get("taskId")
    if not task_id:
        return sku, f"fail:submit:{resp.get('message')}"

    deadline = time.time() + 180
    while time.time() < deadline:
        state = _get(f"{API}/{task_id}")
        data = state.get("data") or {}
        status = data.get("status")
        if status == "success":
            url = (data.get("output") or [{}])[0].get("url")
            if not url:
                return sku, "fail:no-output-url"
            subprocess.run(["curl", "-s", "-o", str(OUT / f"{sku}.jpg"), url],
                           timeout=60, check=True)
            return sku, "ok"
        if status == "fail":
            return sku, f"fail:model:{data.get('error', {}).get('code')}"
        time.sleep(4)
    return sku, "fail:timeout"


def main():
    rows = list(csv.DictReader(open("skus.csv")))
    print(f"submitting {len(rows)} renders (cost ${0.005 * len(rows):.3f})")
    with ThreadPoolExecutor(max_workers=8) as ex:
        futures = {ex.submit(render, r["sku"], r["prompt"], r["aspect_ratio"]): r
                   for r in rows}
        for f in as_completed(futures):
            sku, status = f.result()
            print(f"  {sku}: {status}")


if __name__ == "__main__":
    main()

skus.csv의 한 행은 다음과 같습니다.

sku,prompt,aspect_ratio
mug-beige-01,"Product packshot: matte ceramic mug, beige glaze, seamless white cyclorama, soft studio lighting, subtle contact shadow, 50mm photography, ecommerce hero, clean negative space",1:1
mug-charcoal-01,"Product packshot: matte ceramic mug, charcoal glaze, seamless white cyclorama, soft studio lighting, subtle contact shadow, 50mm photography, ecommerce hero, clean negative space",1:1

프로덕션 코드에 대한 설명:

  • ThreadPoolExecutor(max_workers=8)은 schnell에 안전한 병렬도입니다. 더 높이면 대기열이 깊어질 뿐 더 빨리 완료되지 않습니다.
  • 작업당 180초 타임아웃은 보수적 설정입니다. 실제로 성공한 작업은 모두 30–60초 내에 완료됩니다. 타임아웃이 발생하면 작업이 멈춘 것이며, 네 가지 원인은 hiapi 작업 행/타임아웃 가이드를 참고하세요.
  • 우리는 success 후 즉시 다운로드합니다. output[].url은 expireAt 타임스탬프가 있는 임시 링크이므로 CMS에 저장하지 마세요.

지루하지만 유용한 상품 이미지를 주는 프롬프트 템플릿

카탈로그 프롬프트는 창의적 프롬프트가 아닙니다. 매번 같은 조명, 같은 배경, 같은 카메라를 원하고 제품만 바꾸고 싶을 뿐입니다. 하나의 템플릿:

Product packshot: {MATERIAL} {PRODUCT}, {COLOR} {FINISH},
{COMPOSITION},
{BACKGROUND},
{LIGHTING},
{LENS_NOTE},
ecommerce hero, no props, clean negative space {AREA}

채우면 다음과 같습니다.

Product packshot: matte ceramic coffee mug, soft beige glaze,
centered single subject filling about 60% of frame,
seamless white cyclorama background,
soft diffused studio lighting from the upper left, subtle contact shadow underneath,
shot on 50mm with shallow depth of field,
ecommerce hero, no props, clean negative space top and bottom

순서는 구조보다 중요하지 않습니다. 다음은 출력을 지속적으로 개선하는 요소들입니다.

  • "seamless white cyclorama"가 "white background"보다 좋습니다. 전자는 스튜디오의 무한 곡선 배경 조명을 암시하고, 후자는 때때로 테이블보를 만들어 줍니다.
  • "subtle contact shadow underneath"는 제품이 떠 보이는 것을 방지합니다.
  • "filling about 60% of frame"은 구도를 고정합니다. 이것이 없으면 schnell이 때때로 너무 멀리 당기거나 제품을 구석으로 밀어냅니다.
  • "no props"는 머그컵 옆에 여분의 접시, 잎, 문구류가 나타나는 것을 방지합니다.

연출 변형의 경우 배경 + 조명 블록을 교체하세요.

... matte ceramic coffee mug, soft beige glaze, single subject filling about 40% of frame,
on a light oak countertop with morning sunlight from a kitchen window,
soft golden-hour rim lighting, gentle defocused background showing a blurred linen curtain,
shot on 35mm at f/2.8, lifestyle photography, no people, no other props

탑다운 플랫레이의 경우:

... single subject centered, top-down 90-degree angle, ceramic coffee mug, soft beige glaze,
on a light grey paper surface, soft even overhead lighting, soft shadow directly under the rim,
shot on 50mm, flatlay product photography, no props, clean negative space all sides, 1:1

이 세 프롬프트를 같은 SKU에 실행하면 $0.015 비용으로 쓸 만한 상품 상세 페이지 갤러리를 얻습니다.

재촬영 없는 색상/소재 변형

schnell이 이커머스에서 빛나는 이유는 변형 생성입니다. 한 프롬프트가 한 색상에 효과가 있으면, 색상 설명자를 문자열 치환하여 나머지 색상을 일괄 생산할 수 있습니다.

COLORS = [
    ("beige", "soft beige glaze"),
    ("charcoal", "deep charcoal matte glaze"),
    ("sage", "muted sage green glaze"),
    ("terracotta", "warm terracotta clay glaze"),
    ("cream", "off-white cream glaze with subtle speckle"),
]

TEMPLATE = (
    "Product packshot: matte ceramic coffee mug, {color_desc}, "
    "centered single subject filling about 60% of frame, "
    "seamless white cyclorama background, soft diffused studio lighting from the upper left, "
    "subtle contact shadow underneath, shot on 50mm, ecommerce hero, no props, "
    "clean negative space top and bottom"
)

rows = [{"sku": f"mug-{c}-01", "prompt": TEMPLATE.format(color_desc=desc),
         "aspect_ratio": "1:1"} for c, desc in COLORS]

다섯 가지 변형을 $0.005로 계산하면 전체 색상 선택기 $0.025입니다. 문제는 schnell이 서로 다른 렌더링 간에 형태를 완벽하게 유지하지 못한다는 것입니다. 손잡이 두께, 테두리 곡선, 비율이 색상 간에 약간씩 달라집니다. 스타일화된 상품 페이지에는 괜찮습니다. 픽셀 단위로 일관된 변형이 필요하면 참조 이미지 조건화가 필요하며, 이는 flux-1.1-pro에 참조 이미지를 사용하거나 schnell 대신 nano-banana-pro를 사용해야 함을 의미합니다.

상점 프론트엔드에 연결하기

출력은 다운로드된 JPG 파일입니다. 거기서 상품 상세 페이지를 게시하기까지의 경로는 플랫폼에 따라 다릅니다.

  • Shopify: POST /admin/api/2025-01/products/{id}/images.json, 요청 본문은 {"image": {"attachment": "<base64>"}}. Shopify가 호스팅하는 CDN URL을 반환합니다.
  • WooCommerce: WP 미디어 라이브러리를 통해 업로드(POST /wp-json/wp/v2/media)한 다음, 반환된 미디어 ID로 images: [{id: ...}]를 사용하여 제품을 PATCH합니다.
  • Headless / 자체 CMS: 자체 객체 스토리지(S3, R2, Cloudflare Images)에 업로드하고 제품 레코드에서 생성된 URL을 참조합니다.

어떤 경우든 클라이언트가 아닌 서버 측에서 업로드하여 hiapi 키가 절대 브라우저에 도달하지 않도록 하세요.

계획을 위한 비용 계산

이미지당 $0.005로 계산하면 예산은 거의 제약이 되지 않습니다. 하지만 배치 단위로 생각하면 도움이 됩니다.

배치 이미지 수 비용
SKU 하나, 4장 이미지 4 $0.02
50개 SKU 신상품 출시, 각 4장 200 $1.00
200개 SKU 카탈로그 갱신 800 $4.00
일일 변형 테스트, 매일 100장, 30일간 3,000 $15.00

hiapi에서는 이미지 엔드포인트의 토큰 측 비용(프롬프트)이 무료입니다. 출력 이미지에 대해서만 지불합니다.

flux-schnell이 잘 못하는 것들

투자하기 전에 계획해야 할 사항들:

  • 읽을 수 있는 라벨 텍스트. 상품 이미지에 브랜드 워드마크가 선명하게 읽혀야 한다면, 먼저 schnell로 기본 이미지를 생성한 다음 이미지 편집기에서 실제 라벨 PNG를 합성하세요. schnell에 "컵에 'OAK ROAST' 글자가 있다"를 렌더링하도록 요청하면 약 80%의 경우 깨진 글자가 나옵니다.
  • 사람이 제품을 들고 있는 경우. 인체 해부는 schnell의 약점입니다. 사람이 있는 연출 이미지에는 nano-banana-pro 또는 flux-1.1-pro를 사용하세요.
  • 변형 간 픽셀 단위로 일관된 제품. 이 경우 참조 이미지를 지원하는 모델을 사용하세요.
  • 서브밀리미터 세부 사항(주얼리, 보이는 바늘이 있는 시계, 에칭된 금속). 이는 고급 모델의 영역입니다.

상품 상세 페이지의 다른 모든 것—배경, 조명, 색상 변형, 상황 이미지, 분위기—에는 flux-schnell이 올바른 도구이며, 올바른 가격대에 있고, 올바른 처리 시간을 가집니다. 전체 루프는 while true: poll; download 스크립트와 SKU CSV 하나에 담을 수 있으며, 비용이 충분히 낮아 예산 회의 없이 프롬프트를 반복할 수 있습니다.

자주 묻는 질문

문: flux-schnell에 참조 이미지를 보낼 수 있나요? 답: 아니요. flux-schnell/text-to-image는 텍스트-투-이미지만 지원합니다. input 필드는 prompt와 aspect_ratio만 받으며 그게 전부입니다. 참조 조건화 변형에는 hiapi에서 이미지 입력을 받는 flux-1.1-pro 또는 nano-banana-pro를 사용하세요.

문: 어떤 종횡비가 지원되나요? 답: 1:1, 3:2, 2:3, 4:3, 3:4, 5:4, 4:5, 16:9, 9:16, 21:9, auto입니다. 다른 비율(예: 5:7, 2:1)은 작업 생성 시 거부되며 지원 집합을 가리키는 오류를 반환합니다.

문: resolution을 포함하면 작업 생성이 400을 반환하는 이유는 무엇인가요? 답: flux-schnell의 input은 aspect_ratio만 받습니다. hiapi의 다른 이미지 모델(예: flux-2, nano-banana-pro)은 resolution(1K/2K/4K)을 받습니다. schnell의 경우 제거하세요.

문: 출력 URL의 유효 기간은 얼마나 되나요? 답: 각 output[].url에는 expireAt 타임스탬프가 있습니다(보통 수십 분). status가 success로 바뀌면 즉시 이미지 바이트를 다운로드하여 자체 스토리지 버킷에 저장하세요. 임시 URL을 제품 데이터베이스에 핫링크하지 마세요.

문: 8개 이상의 동시 작업을 실행할 수 있나요? 답: 가능하지만 실제로 더 빨리 완료되지는 않습니다. schnell은 엔드투엔드로 이미 빠르며, 약 8개를 넘는 병렬은 대기열만 쌓습니다. 진정한 대량(수천 개)에는 하나의 프로세스에서 스레드를 사용하는 대신 여러 작업 머신에서 병렬로 처리하세요.

문: 같은 제품의 픽셀 단위로 일관된 변형을 얻으려면 어떻게 해야 하나요? 답: schnell만으로는 불가능합니다. 먼저 메인 이미지를 한 번 생성한 다음, 참조 이미지를 지원하는 모델(flux-1.1-pro 또는 nano-banana-pro)을 사용하여 메인 이미지를 참조로 변형 실행을 하세요. 비용은 올라가지만 형태 일관성이 크게 개선됩니다.

영구 AI 아티팩트로 빌드 시작하기

API 키를 받아 태스크 기반의 하나의 API로 이미지, 비디오, 오디오 모델을 호출하세요.

API 문서 보기