구현하기 · 가상 Try-On
앱에 가상 try-on 기능 구축하기
한 줄 요약
백엔드에서 제품 사진을 받아 mannequin ID와 함께 /api/v1/tryon/apparel에 POST하고, 반환된 생성 ID를 백그라운드 워커(HTTP 요청 스레드가 아님)에서 status가 completed로 바뀔 때까지 polling한 뒤, output_url을 자체 오브젝트 스토리지에 영구 저장하고 UI에 렌더링합니다.
- 크레딧: 2K에서 5 크레딧 · 4K에서 6 크레딧
- 일반적인 소요 시간: 1.5–4 min
- 처리 방식: 비동기, 폴링
이 페이지의 모든 수치는 다음 출처를 기반으로 합니다: Photta API 문서 · 확인 완료
프로덕션 레벨의 레시피: 고객이 제품 사진을 업로드하고, 백엔드가 Photta try-on 작업을 큐에 추가하며, 워커가 완료될 때까지 polling하고, 프론트엔드가 착용 모델 결과를 표시합니다. 블로킹 요청, API 키 유출, 불필요한 SDK는 없습니다.
RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/tryon/apparel \
-H "Authorization: Bearer $PHOTTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": true,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
}')
ID=$(echo "$RESPONSE" | jq -r '.data.id')const res = await fetch("https://api.photta.app/api/v1/tryon/apparel", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": true,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
}),
});
const { data } = await res.json(); // data.id, data.statusimport os, requests
res = requests.post(
"https://api.photta.app/api/v1/tryon/apparel",
headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
json={
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": True,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
},
)
data = res.json()["data"] # data["id"], data["status"]동작 원리
업로드부터 착용 모델 이미지까지
6단계의 구체적인 과정입니다. 첫 연동 시 10분에서 15분 정도의 작업 시간을 예상하십시오.
단계 1
데이터 흐름 설계하기
고객 디바이스 → 백엔드(업로드) → Photta API(제출) → Photta API(polling) → 데이터베이스(상태 미러링) → 고객 디바이스(렌더링). 코드를 한 줄이라도 작성하기 전에 이를 종이에 그려보십시오. 백엔드와 Photta 사이의 경계가 유일하게 중요한 부분입니다.
단계 2
API 키 생성 및 저장
라이브 키는 photta_live_로 시작합니다. 환경 변수에 저장하십시오. 절대로 브라우저로 전달해서는 안 됩니다. 하나의 키로 모든 Photta 기능을 이용할 수 있으므로 try-on, 주얼리, 고스트 마네킹용 키를 따로 둘 필요가 없습니다.
단계 3
업로드 endpoint 연결
먼저 고객의 제품 사진을 오브젝트 스토리지에 수신합니다. Photta에 공개 URL을 전달하는 것이 base64보다 간단하며, 모든 요청에 대한 감사 추적(audit trail)을 남길 수 있습니다.
단계 4
서버에서 작업 제출
제품 URL, mannequin_id, resolution, aspect_ratio를 포함하여 POST /api/v1/tryon/apparel을 호출합니다. API는 202 Accepted와 생성 ID를 반환합니다. 클라이언트가 Photta가 아닌 자체 endpoint를 polling할 수 있도록 이 ID를 데이터베이스에 저장하십시오.
단계 5
백그라운드 워커에서 polling
HTTP 요청 내에서 polling하지 마십시오. 작업에 1.5~4분이 소요되므로 대부분의 서버리스 타임아웃을 초과합니다. Trigger.dev, Inngest, BullMQ, Vercel Cron 또는 자체 큐를 사용하여 작업이 완료될 때까지 3초마다 polling을 재스케줄링하십시오.
단계 6
결과 렌더링
status가 completed로 바뀌면 data.output_url을 데이터베이스에 복사하고 고객 디바이스로 실시간 이벤트(websocket, SSE 또는 long-poll)를 전송합니다. UI에서 로딩 스켈레톤이 이미지로 교체됩니다. 완료되었습니다.
세 가지 코드 샘플, 엔드투엔드
업로드 핸들러, 작업 디스패처, 워커 측 poller — 기능을 배포하는 데 필요한 모든 구성 요소입니다.
// POST /app/try-on → your backend upload handler
import { put } from "@vercel/blob";
export async function uploadProduct(file: File) {
const { url } = await put(`products/${crypto.randomUUID()}.jpg`, file, {
access: "public",
});
return url;
}const res = await fetch("https://api.photta.app/api/v1/tryon/apparel", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": true,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
}),
});
const { data } = await res.json(); // data.id, data.status// Trigger.dev / Inngest / BullMQ task
const BASE = "https://api.photta.app/api/v1";
export const pollTryOnTask = task({
id: "poll-try-on",
run: async ({ generationId, attempt = 0 }) => {
const res = await fetch(`${BASE}/tryon/apparel/${generationId}`, {
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}` },
});
const { data } = await res.json();
if (data.status === "completed") return data.output_url;
if (data.status === "failed") throw new Error(data.error_message);
if (attempt >= 120) throw new Error("try-on timed out");
// Re-schedule this same task after 3 seconds.
return ctx.retry.after(3000, { generationId, attempt: attempt + 1 });
},
});개발자가 자주 묻는 질문
배포 전 확인 사항
백엔드 없이 구현할 수 있나요?
아니요. Photta API 키는 반드시 서버 측에 보관해야 합니다. 이 용도로는 Next.js 라우트 핸들러나 Cloudflare Worker 같은 가벼운 환경도 백엔드로 간주됩니다. 키는 user-agent가 절대 읽을 수 없는 곳에 두기만 하면 됩니다.
HTTP 요청이 멈춘 채 대기하는(hanging) 현상을 방지하려면 어떻게 해야 하나요?
요청 핸들러 내부에서 절대 polling하지 마세요. 작업을 동기적으로 제출하고, 클라이언트에 생성 ID를 반환한 뒤, 백그라운드 워커에서 polling하세요. 클라이언트는 Photta의 엔드포인트가 아니라 여러분의 상태 엔드포인트를 polling해야 합니다. 그래야 rate limit 문제를 각 사용자가 아닌 여러분의 시스템에서 직접 제어할 수 있습니다.
작업 실행 도중에 실패하면 어떻게 되나요?
Photta는 실패 시 credits를 자동으로 환불하며, 응답에는 data.error_code 및 data.error_message가 포함됩니다. 사용자에게 친절한 오류 메시지를 표시하고, 새 작업을 시작할 수 있는 재시도 버튼을 제공하세요.
실시간 진행률을 표시할 수 있나요?
Photta는 중간 진행률을 전달하지 않습니다. 상태는 processing, completed, failed 중 하나입니다. 1.5–4분 대기 시간에 맞춘 타이머 기반 진행률 표시줄이나 단순한 스켈레톤 로더를 사용하는 것이 좋습니다. 퍼센티지를 허위로 표시하지 마세요.
결과 URL은 영구적인가요?
CDN URL은 안정적이지만, 프로덕션 서비스가 장기 렌더링을 이 URL에 의존해서는 안 됩니다. 바이트 데이터를 한 번 다운로드하여 자체 S3/GCS/R2 버킷에 저장하세요. 그러면 Photta의 CDN이 이전되더라도 PDP(상품 상세 페이지)가 정상 작동합니다.
관련 페이지
레퍼런스
기능
가상 착용(Virtual try-on) API 의류용
의류를 AI 마네킹에 착용시키는 REST API입니다. 비동기식 처리, 1.5~4분의 생성 시간, 2K 기준 5 credits, 4K 기준 6 credits 소요. cURL, Node.js, Python 예제가 제공됩니다.
읽기빠른 시작
5분 안에 첫 번째 Photta 이미지를 생성하세요.
회원가입, API 키 생성, 첫 cURL 호출 실행, 완료될 때까지 폴링, 결과 저장까지. 엔드투엔드로 문서화된 5단계 가이드입니다.
읽기언어
Node.js walkthrough
읽기언어
Python walkthrough
읽기
이 페이지의 모든 수치는 다음 출처를 기반으로 합니다: Photta API 문서 · 확인 완료
반나절 만에 가상 try-on 기능 배포하기
이 페이지의 모든 코드 샘플은 공식 Photta 문서와 일치합니다. API 키를 발급받으면 커피가 다 내려지기도 전에 작동하는 프로토타입을 구축할 수 있습니다.