구축 · 고스트 마네킹 자동화
카탈로그용 고스트 마네킹 추출 자동화
한 줄 요약
마지막 실행 이후의 카탈로그 변경분(delta)을 추출하고, 하나의 스타일과 함께 product_images 배열(각 항목은 이미지 URL 및 선택적 back_image)을 /api/v1/ghost-mannequin/batch로 POST한 뒤, 모든 항목이 completed 또는 failed가 될 때까지 배치 ID를 3초마다 폴링하고, 각 상품 행을 해당 output_url로 업데이트하세요. 동시 실행 배치는 플랜별로 제한됩니다.
- 크레딧: 2K에서 4 크레딧 · 4K에서 5 크레딧
- 일반적인 소요 시간: 1.5–4 min
- 처리 방식: 비동기, 폴링
이 페이지의 모든 수치는 다음 출처를 기반으로 합니다: Photta API 문서 · 확인 완료
디자이너의 손을 거치지 않고 모든 PDP 사진에서 마네킹을 제거하세요. Photta의 고스트 마네킹 endpoint는 자연스럽게 배치를 지원합니다. 야간 cron 작업으로 50K SKU 피드를 몇 주가 아닌 몇 시간 만에 인비저블 마네킹 카탈로그로 변환할 수 있습니다.
RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/ghost-mannequin \
-H "Authorization: Bearer $PHOTTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front",
"angle": "side",
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, cream interior lining",
"brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
}')
ID=$(echo "$RESPONSE" | jq -r '.data.id')const res = await fetch("https://api.photta.app/api/v1/ghost-mannequin", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front",
"angle": "side",
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, cream interior lining",
"brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
}),
});
const { data } = await res.json(); // data.id, data.statusimport os, requests
res = requests.post(
"https://api.photta.app/api/v1/ghost-mannequin",
headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
json={
"product_image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front",
"angle": "side",
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, cream interior lining",
"brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
},
)
data = res.json()["data"] # data["id"], data["status"]동작 원리
카탈로그 전체 대상 야간 실행
무인 자동화 실행에 필요한 5가지 구성 요소.
단계 1
카탈로그 매핑
고스트 마네킹 이미지가 필요한 상품 행, 이미 보유한 행, 비대상 카테고리 행을 파악하세요. last_photographed_at 타임스탬프를 저장하여 야간 작업이 변경분만 처리하도록 구성합니다.
단계 2
카테고리별 3D 또는 플랫레이(flat-lay) 선택
볼륨감이 필요한 의류(아우터, 데님, 맞춤복 등)에는 `3d`를 사용하세요. 바닥에 펼쳐놓고 촬영하는 것이 더 좋은 액세서리와 니트웨어에는 `flat_lay`를 사용합니다. 배치 빌더가 수작업 조정 없이 알맞은 스타일을 선택할 수 있도록 상품 카테고리별로 스타일을 저장해 두세요.
단계 3
변경분 배치 제출
image, 선택적 back_image, image_side가 포함된 product_images 배열과 전체 배치에 적용될 하나의 style, background_color, resolution을 구성하여 POST /api/v1/ghost-mannequin/batch를 호출하세요. 배열 인덱스와 상품 ID 간의 매핑을 자체적으로 유지해야 합니다. 응답에서는 동일한 순서로 항목당 하나의 생성 ID가 반환됩니다.
단계 4
상위 배치 폴링
배치 endpoint는 집계 카운터(total, processing, completed, failed)를 반환합니다. completed + failed의 합이 total과 같아질 때까지 3초마다 폴링하세요. Photta는 배치 내 항목들을 병렬로 처리하므로 개별 생성을 따로 폴링할 필요가 없습니다.
단계 5
데이터베이스에 영구 저장
status.data.items를 순서대로 순회하며(배치는 입력 배열 순서를 유지함) 각 상품 행을 해당 output_url로 업데이트하세요. 실패 항목은 로그에 기록하세요. 대부분 피드의 원본 사진 불량이 원인이므로, 수정 후 다음 실행 시 다시 제출할 수 있습니다.
Python으로 구현하는 3단계
변경분 추출, 배치 제출, 상위 폴링 — 최소 기능 카탈로그 자동화.
from datetime import datetime, timedelta
yesterday = datetime.utcnow() - timedelta(days=1)
rows = db.execute(
"SELECT id, source_image_url FROM products WHERE updated_at > %s",
(yesterday,),
).fetchall()import os, requests
res = requests.post(
"https://api.photta.app/api/v1/ghost-mannequin/batch",
headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
json={
"product_images": [
{
"image": "https://cdn.yourstore.com/products/jacket-front.jpg",
"back_image": "https://cdn.yourstore.com/products/jacket-back.jpg",
"image_side": "front"
},
{
"image": "https://cdn.yourstore.com/products/shirt-front.jpg",
"image_side": "front"
},
{
"image": "https://cdn.yourstore.com/products/trousers-front.jpg",
"image_side": "front"
}
],
"style": "3d",
"background_color": "white",
"resolution": "2K",
"aspect_ratio": "3:4",
"user_instruction": "closed front, buttoned up",
"brand_label_id": "7cc9f988-068b-44cd-8004-22f486b10e67"
},
)
data = res.json()["data"] # data["id"], data["status"]import os, time, requests
BASE = "https://api.photta.app/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"}
# data["items"] comes back in the order of the product_images you submitted.
batch_id = data["batch_id"]
for _ in range(240): # 240 × 3s = 12 minutes
status = requests.get(f"{BASE}/ghost-mannequin/batch/{batch_id}", headers=HEADERS).json()["data"]
if status["completed"] + status["failed"] == status["total"]:
break
time.sleep(3)
for row, item in zip(rows, status["items"]):
if item["status"] == "completed":
db.execute(
"UPDATE products SET ghost_image_url = %s WHERE id = %s",
(item["output_url"], row.id),
)개발자가 자주 묻는 질문
카탈로그 규모의 고스트 마네킹 운영
단일 배치는 최대 어느 크기까지 가능한가요?
문서에 따르면 배치 endpoint는 호출당 최대 수백 개의 항목을 허용합니다. 50K SKU 규모의 야간 실행의 경우, 변경분을 100–200개 단위의 배치로 나누어 순차적으로 전송하면 플랜의 rate limit 내에서 안정적으로 유지할 수 있습니다.
한 배치에 3D와 플랫레이를 혼합할 수 있나요?
네 — style은 항목별 필드이므로 단일 배치에 두 스타일을 모두 포함할 수 있습니다. 다만 실무에서는 배치의 모든 행이 동일한 데이터베이스 컬럼에 저장될 때 다운스트림 영구 저장 로직이 더 단순해지므로, 대부분의 팀이 카테고리별로 분할하여 처리합니다.
실패한 항목은 어떻게 처리되나요?
배치 내 실패한 항목은 자동으로 credits가 환불되며, 최종 항목 payload에 error_code와 error_message가 포함됩니다. 실패 항목을 로그에 기록한 후, 담당 팀이 원본 사진을 수정하거나 해당 행을 영구적 입력 불량으로 표시할 수 있도록 조치한 뒤 다음 날 밤에 재시도하세요.
결과를 상품 ID에 어떻게 다시 매핑하나요?
제출 시 배열 위치와 상품 ID 간의 매핑을 자체적으로 유지하세요. 배치 응답은 product_images와 동일한 순서로 항목당 하나의 생성 ID를 반환하며, 배치 상태에서도 항목들이 해당 순서대로 나열됩니다.
이 작업을 일일 cron으로 예약할 수 있나요?
예 — 지난 24시간 동안의 변경분을 가져와 청크로 나누고, 배치를 제출한 다음, 결과를 데이터베이스에 기록하는 단순한 일일 cron은 카탈로그 규모의 팀에서 널리 쓰이는 안정적인 패턴입니다. 실패한 날의 작업을 안전하게 재실행할 수 있도록 실행기는 멱등성을 유지하도록 구성하세요.
오늘 밤 카탈로그 규모의 고스트 마네킹 배포하기
Python 함수 3개, cron 1개, 50K장의 사진. API 키를 발급받아 카탈로그에 바로 연결하세요.