Node.js · 가상 피팅 API

Node.js 가상 피팅 API

@photta/node 패키지는 아직 없지만 필요하지도 않습니다. Node 18 이상은 `fetch`를 기본 지원하며, 20줄 정도의 헬퍼 코드로 약 5분 만에 첫 번째 모델 이미지를 얻을 수 있습니다.

요약

환경 변수에 `PHOTTA_API_KEY`를 설정하고, `Authorization: Bearer photta_live_xxx`를 포함하여 `https://ai.photta.app/api/v1`을 호출하는 간단한 `phottaFetch()` 헬퍼를 작성하세요. 제품 이미지 URL, mannequin_id, pose_id와 함께 `/tryon/apparel`로 POST한 뒤, `status === 'completed'`가 될 때까지 3초마다 `/tryon/apparel/:id`를 폴링하세요. 보통 1.5~4분 정도 소요됩니다.

업데이트됨 · 2026-04-19

첫 번째 요청

Node.jsPythoncURLcURL
import { phottaFetch } from "./photta.js";

// 1. Submit the job — returns a generation ID immediately.
const created = await phottaFetch("/tryon/apparel", {
  method: "POST",
  body: JSON.stringify({
    product_type: "dress",
    product_images: ["https://example.com/dress.jpg"],
    mannequin_id: "mnq_athena_ts",
    pose_id: "pose_standing_front",
    resolution: "2K",
    aspect_ratio: "3:4",
  }),
});
const generationId = created.data.id;

// 2. Poll every 3 seconds until processing completes. Typical
// completion is 1.5–4 minutes; put an upper bound so a stuck
// job can't hang your request forever.
const pollInterval = 3000;
const maxAttempts = 120;     // 120 × 3s = 6 minutes
let result;

for (let i = 0; i < maxAttempts; i++) {
  result = await phottaFetch(`/tryon/apparel/${generationId}`);
  if (result.data.status === "completed") break;
  if (result.data.status === "failed") {
    throw new Error(result.data.error_message);
  }
  await new Promise((r) => setTimeout(r, pollInterval));
}

console.log("Result:", result.data.output_url);

기대 효과

Typical completion

1–3min

2K / 4K credits

4 / 6

Styles

2

Batch-ready

yes

작동 원리

Node.js 가상 피팅 API

5단계, 가입부터 첫 이미지까지 약 5분.

  1. 01

    단계 1

    가입 및 키 생성

    ai.photta.app에서 계정을 만드세요. 대시보드의 개발자(Developers) 섹션에서 'API 키 생성'을 클릭하고 라이브 키를 복사하세요. `photta_live_`로 시작하는 32자리의 16진수 키입니다.

  2. 02

    단계 2

    소스 코드가 아닌 환경 변수에 키 저장

    `.env`(또는 플랫폼의 시크릿 저장소)에 `PHOTTA_API_KEY=...`를 추가하고 `process.env`를 통해 로드하세요. 키를 절대 커밋하지 마세요. 특히 `'use client'`가 선언된 파일에서 가져오면 번들러를 통해 브라우저로 유출될 수 있으니 주의하세요.

  3. 03

    단계 3

    간단한 fetch 헬퍼 작성

    Base URL, Authorization 헤더, JSON 본문 처리 및 에러 정규화를 수행하는 20줄 정도의 `fetch()` 래퍼를 만듭니다. 공식 SDK가 출시되기 전까지는 이 추상화만으로 충분합니다.

  4. 04

    단계 4

    가상 피팅 제출 및 폴링

    `product_type`, `product_images`, `mannequin_id`, `pose_id`, `resolution`, `aspect_ratio`를 포함해 `/tryon/apparel`로 POST 요청을 보냅니다. 즉시 생성 ID가 반환됩니다. `status === 'completed'`가 될 때까지 3초마다 `/tryon/apparel/:id`를 폴링하세요.

  5. 05

    단계 5

    결과 저장

    완료된 페이로드에는 `output_url`과 `thumbnail_url`이 들어 있습니다. 바이트 데이터를 한 번 가져와 자체 오브젝트 스토리지에 저장하세요. URL은 고정적이지만 렌더링 시 Photta의 CDN에 의존하지 않는 것이 안전합니다.

전체 코드

Copy, paste, done.

Four snippets — install prerequisites, wrap the REST call, submit + poll, then handle the errors that actually happen in production.

01No SDK required — use native fetch in Node 18+
bash
# Node 18+ ships fetch natively. No install step needed.
node --version   # ensure v18 or later

# Optional: keep your API key out of source control
echo "PHOTTA_API_KEY=photta_live_xxxxx" >> .env
02Wrap the REST call in a tiny client helper
javascript
// photta.js — a 20-line wrapper you can reuse across your app.
const PHOTTA_BASE_URL = "https://ai.photta.app/api/v1";

export async function phottaFetch(path, init = {}) {
  const res = await fetch(`${PHOTTA_BASE_URL}${path}`, {
    ...init,
    headers: {
      "Authorization": `Bearer ${process.env.PHOTTA_API_KEY}`,
      "Content-Type": "application/json",
      ...init.headers,
    },
  });
  const body = await res.json();
  if (!res.ok) {
    const err = new Error(body?.error?.message ?? res.statusText);
    err.status = res.status;
    err.code = body?.error?.code;
    throw err;
  }
  return body;
}
03Submit a try-on job and poll until it lands
javascript
import { phottaFetch } from "./photta.js";

// 1. Submit the job — returns a generation ID immediately.
const created = await phottaFetch("/tryon/apparel", {
  method: "POST",
  body: JSON.stringify({
    product_type: "dress",
    product_images: ["https://example.com/dress.jpg"],
    mannequin_id: "mnq_athena_ts",
    pose_id: "pose_standing_front",
    resolution: "2K",
    aspect_ratio: "3:4",
  }),
});
const generationId = created.data.id;

// 2. Poll every 3 seconds until processing completes. Typical
// completion is 1.5–4 minutes; put an upper bound so a stuck
// job can't hang your request forever.
const pollInterval = 3000;
const maxAttempts = 120;     // 120 × 3s = 6 minutes
let result;

for (let i = 0; i < maxAttempts; i++) {
  result = await phottaFetch(`/tryon/apparel/${generationId}`);
  if (result.data.status === "completed") break;
  if (result.data.status === "failed") {
    throw new Error(result.data.error_message);
  }
  await new Promise((r) => setTimeout(r, pollInterval));
}

console.log("Result:", result.data.output_url);
04Handle 402 credit exhaustion and 429 rate limits
javascript
try {
  await phottaFetch("/tryon/apparel", {
    method: "POST",
    body: JSON.stringify({ /* … */ }),
  });
} catch (err) {
  if (err.status === 402) {
    // Out of credits — surface a top-up CTA to the user.
    // err.code === "insufficient_credits"
  } else if (err.status === 429) {
    // Rate-limited. Honour the Retry-After header.
    const retryAfter = err.retryAfter ?? 30;
    await new Promise((r) => setTimeout(r, retryAfter * 1000));
    // Then retry…
  } else if (err.status >= 500) {
    // Server-side issue — backoff + retry.
  }
  throw err;
}

구조 설계 이유

현재 클라이언트 라이브러리보다 헬퍼 코드가 나은 이유

  • Node 18+ ships native fetch — no runtime deps required
  • Same helper works inside Next.js route handlers, API routes, server actions and edge functions
  • Keep API keys in env; never import the helper from a 'use client' module or the bundler will leak them
  • Works with any queue/cron: BullMQ, Inngest, Trigger.dev, Vercel Cron, Cloudflare Queues

제외된 기능

Honest caveats

  • No official @photta/node SDK yet — REST is the only path today
  • No built-in webhook delivery; polling is the documented pattern (3–5s interval)
  • Bearer token lives in env; the API doesn't support OAuth client credentials yet

개발자 자주 묻는 질문

Questions other developers ask

공식 Photta Node.js SDK가 있나요?+

아직 없습니다. Photta 문서는 도입 단계에 따라 SDK를 계획하고 있습니다. Python은 API 사용자 10명, Node.js는 20명 달성 시 출시할 예정입니다. 그때까지는 cURL, Node 기본 fetch 또는 Python requests를 통한 REST 연동을 권장합니다. 이 페이지의 20줄 헬퍼 코드는 실제 클라이언트 라이브러리와 거의 동일한 역할을 합니다.

지원되는 Node.js 버전은 무엇인가요?+

기본 `fetch`를 지원하는 Node 18 이상 버전입니다. Node 16의 경우 `node-fetch`로 폴리필하거나 업그레이드해야 합니다. 그 외 코드는 일반적인 ECMAScript 2020(async/await, template literals, dynamic imports) 형식이므로 다른 런타임 제약은 없습니다.

Node에서 인증은 어떻게 하나요?+

모든 요청에 `Authorization: Bearer photta_live_xxx` 헤더를 포함하세요. 키는 운영 시 `photta_live_`, 샌드박스 모드(출시 예정) 시 `photta_test_`로 시작합니다. 환경 변수에 넣고 `process.env.PHOTTA_API_KEY`로 로드하세요. 현재 OAuth 클라이언트 자격 증명이나 세션 기반 인증은 지원하지 않습니다.

1.5~4분의 생성 시간을 어떻게 처리하나요?+

가상 피팅 엔드포인트는 완전히 비동기입니다. POST 요청은 즉시 `status: 'processing'`과 함께 생성 ID를 반환합니다. `status`가 `completed` 또는 `failed`로 바뀔 때까지 3~5초마다 `GET /tryon/apparel/:id`를 폴링하세요. 요청이 영원히 대기하지 않도록 최대 시도 횟수를 설정하세요. 3초 간격으로 120회 시도하면 문서화된 소요 시간을 넉넉히 커버합니다.

Next.js 내부에서 API를 호출할 수 있나요?+

네. 라우트 핸들러(`app/**/route.ts`), 서버 액션(`'use server'`), 또는 미들웨어에서 호출 가능합니다. API 키는 항상 서버에 두어야 하며, `'use client'` 파일에서 헬퍼를 가져오지 마세요. Vercel의 함수 실행 시간 초과를 넘는 장기 작업의 경우, 큐(Inngest, Trigger.dev, BullMQ)로 폴링을 위임하고 브라우저에는 생성 ID를 반환하여 웹훅이나 별도 상태 엔드포인트를 통해 확인하게 하세요.

에러 처리는 어떻게 하나요?+

2xx 이외의 응답은 `type`, `code`, `message` 및 고객 지원용 요청 ID를 포함한 JSON 에러 객체를 반환합니다. 일반적인 사례: 잘못된 필드에 대한 400 invalid_request_error, 크레딧 부족 시 필요한 수량과 현재 수량을 알리는 402 insufficient_credits, 초당 호출 제한 시 Retry-After 헤더를 포함한 429 rate_limit_exceeded 등이 있습니다. 5xx 에러는 지수 백오프를 사용하여 재시도하는 것이 안전합니다.

Node.js · 가상 피팅 API

계정 생성 및 API 키 발급

환경 변수에 `PHOTTA_API_KEY`를 설정하고, `Authorization: Bearer photta_live_xxx`를 포함하여 `https://ai.photta.app/api/v1`을 호출하는 간단한 `phottaFetch()` 헬퍼를 작성하세요. 제품 이미지 URL, mannequin_id, pose_id와 함께 `/tryon/apparel`로 POST한 뒤, `status === 'completed'`가 될 때까지 3초마다 `/tryon/apparel/:id`를 폴링하세요. 보통 1.5~4분 정도 소요됩니다.

Node.js용 가상 피팅 API — Photta | Photta