HiAPI
블로그

튜토리얼

recraft-v4-styles/text-to-image API 튜토리얼: curl, Python 및 비동기 폴링

HiAPI에서 스타일 참조 이미지 생성을 사용하는 전체 요청 실습

Oct 31, 2026
深蓝底色上铜色细分平行线构成几何带状图案,呼应异步轮询主题的博客封面图

recraft-v4-styles/text-to-image가 무엇을 해줄 수 있는가

recraft-v4-styles/text-to-image는 스타일 참조 이미지 생성 엔드포인트입니다. 보고 싶은 내용을 설명하는 텍스트 프롬프트와, 외관 스타일을 설명하는 하나 이상의 참조 이미지를 제공합니다. 모델은 둘을 융합합니다. 프롬프트는 내용과 구도 의도를 담고, 참조 이미지는 시각적 스타일을 담습니다.

엔드포인트를 선택할 때 이 구분은 중요합니다. 외관을 전적으로 텍스트로 표현할 수 있을 때—예를 들어 '커피 컵의 플랫 벡터 일러스트, 2색, 굵은 외곽선'—에는 프롬프트 기반 이미지 생성이 올바른 도구입니다. 그러나 실제 제품 업무에서 대부분의 시나리오는 설명하기 어렵지만 보여주기 쉬운 브랜드 외관을 다룹니다. 기존 키 비주얼 한 장, 이전에 승인된 렌더링 한 장, 또는 디자인 시스템 샘플 하나가 형용사 문단보다 훨씬 정확하게 스타일 규격을 정의합니다. 이것이 바로 이 엔드포인트가 채우는 공백입니다.

개발자 관점에서 보면 이중 입력 변환으로 볼 수 있습니다: 프롬프트 + 스타일 참조 -> 이미지. 이를 통합하는 모든 것은 이 형태에서 비롯됩니다. 문자열 하나를 직렬화하는 것이 아니라, 참조 이미지가 일급 입력인 여러 부분으로 구성된 작은 페이로드를 조립하는 것입니다. 요청 구성 코드는 참조 이미지를 그렇게 대해야 하며, 검증 계층은 사용 가능한 스타일 참조 없이 프롬프트만 있는 작업을 서비스를 떠나기 전에 거부해야 합니다.

이 엔드포인트는 비동기입니다. 요청을 제출하면 작업이 반환되고, 그 작업을 폴링한 뒤 최종 이미지가 인라인 바이트가 아닌 URL로 도착합니다. 이는 이미지 워크로드에 대한 의도적 설계입니다. 생성은 충분히 오래 걸리므로 HTTP 연결을 열린 채로 유지하는 것은 낭비이자 취약합니다. '제출-폴링-다운로드' 수명 주기를 받아들이면, 통합의 나머지는 평범한 API 파이프라인 작업입니다.

작동하는 요청의 해부

코드를 작성하기 전에 필드 그룹을 정리하세요. recraft-v4-styles/text-to-image 요청은 세 가지 논리적 부분으로 구성됩니다.

프롬프트

프롬프트는 내용 지시입니다. 화면에 무엇이 포함되어야 하는지를 중심으로 작성하세요—피사체, 구도, 프레이밍, 객체 수, 배경 처리. 참조 이미지가 이미 전달하는 스타일 형용사에 프롬프트 예산을 쓰지 마세요. 프롬프트와 참조 이미지 사이의 신호 충돌은 종종 모호한 결과를 낳습니다. 프롬프트는 짧고, 구체적이며, 테스트 가능하게 유지하세요.

스타일 참조 입력

이것이 이 엔드포인트를 일반적인 텍스트-이미지 호출과 구별하는 부분입니다. 스타일 참조는 프롬프트와 함께 제공되어 시각 언어를 정의하는 이미지입니다. 조립할 때는 잡다한 이미지 더미가 아니라 큐레이션된 세트로 취급하세요:

  • 일관성이 수량보다 낫습니다. 같은 일관된 외관을 공유하는 몇 장의 참조 이미지가 다양한 스타일의 많은 참조 이미지보다 모델을 더 잘 가르칩니다.
  • 해상도와 프레이밍이 중요합니다. 참조 이미지는 썸네일이나 심하게 크롭된 조각이 아니라 원하는 출력을 대표해야 합니다.
  • 접근 가능해야 합니다. 참조 이미지를 URL로 전달하는 경우, 해당 URL은 요청 시점에 공개적으로 접근 가능해야 합니다. 60초 후 만료되는 서명 링크는 느린 작업을 실패시킵니다.

참조 이미지 세트를 코드 곳곳에 흩어 놓지 말고 구성에 보관하여 브랜드 자산과 함께 버전 관리할 수 있게 하세요.

출력 옵션

나머지 필드는 반환할 아티팩트를 설명합니다—반환되는 이미지의 내용이 아니라 이미지에 영향을 주는 노브입니다. 정확한 파라미터 이름과 허용 값은 해당 엔드포인트의 참조 페이지에 나열되어 있으니 추측하지 말고 그곳에서 읽고, 코드의 한 위치에 집중시키세요. 그래야 사양 변경 시 파일 하나만 수정하면 됩니다.

유용한 습관: 선택한 언어에서 JSON 본문을 사전/객체로 구성하고, 첫 실행 시 디버그 레벨로 한 번 로깅한 뒤, 나쁜 결과를 모델 탓으로 돌리기 전에 그 구조를 눈으로 확인하세요.

curl로 호출하기

curl로 시작하세요. 여러분과 와이어 포맷 사이의 모든 라이브러리 추상화를 제거하므로 요청이 잘못되면 즉시 알 수 있습니다.

제출은 JSON 본문과 HiAPI API 키를 담은 인증 헤더를 가진 POST 요청입니다:

curl -sS -X POST "https://api.hiapi.ai/v1/recraft-v4-styles/text-to-image" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "a ceramic coffee cup on a linen tablecloth, soft daylight",
    "style_references": [
      "https://your-cdn.example.com/style/ref-01.png"
    ]
  }'

보이는 내용에 대해 몇 가지 참고 사항:

  • 키는 환경 변수($HIAPI_API_KEY)에 보관하고, 셸 기록이나 커밋된 스크립트에 인라인으로 두지 마세요.
  • -d 페이로드는 순수 JSON입니다. 셸의 인용 부호 규칙은 흔한 문제의 원인입니다—파싱 오류가 발생하면 본문을 파일로 에코하고 -d @body.json을 사용하세요.
  • 여기 표시된 필드 이름은 이전 섹션의 요청 해부를 따릅니다. 게시하기 전에 해당 엔드포인트 참조 페이지에서 정확한 이름과 허용되는 출력 옵션을 확인하세요.

응답은 이미지가 아닙니다. 방금 대기열에 넣은 생성을 식별하는 식별자와 아직 완료되지 않았음을 알려주는 상태 필드가 담긴 작업 확인입니다. 첫 실행 시 응답 본문을 주의 깊게 읽고 작업 ID를 저장하는 필드 이름을 기록하세요—그 문자열이 이후 모든 작업의 핸들입니다. 저장하세요. 첫 응답에 오류 객체가 포함되어 있다면 멈추고 요청을 수정하세요. 받지도 못한 ID를 폴링하는 것이 가장 흔한 막다른 길입니다.

Python으로 동일한 요청 구현하기

curl 호출을 Python으로 한 번 이식하면 재사용 가능한 기본 요소를 얻게 됩니다. 아래 예제는 requests를 사용하며 요청 본문을 그대로 미러링합니다.

import os
import requests

API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = "https://api.hiapi.ai/v1"

HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

STYLE_REFERENCES = [
    "https://your-cdn.example.com/style/ref-01.png",
]

def submit_generation(prompt: str) -> dict:
    body = {
        "prompt": prompt,
        "style_references": STYLE_REFERENCES,
    }
    resp = requests.post(
        f"{BASE_URL}/recraft-v4-styles/text-to-image",
        headers=HEADERS,
        json=body,
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()

if __name__ == "__main__":
    job = submit_generation(
        "a ceramic coffee cup on a linen tablecloth, soft daylight"
    )
    print(job)

이 코드를 조정할 때 유지할 가치가 있는 두 가지 설계 선택:

  • 항상 타임아웃을 설정하세요. 제출은 빠르게 반환되어야 합니다. 멈춘다면 멈춘 프로세스가 아니라 예외를 원합니다.
  • 응답 객체가 아니라 파싱된 JSON을 반환하세요. 다운스트림 코드는 requests 내부 구현이 아니라 작업 페이로드에 의존해야 합니다. 이렇게 하면 나중에 전송 계층을 쉽게 교체할 수 있습니다.

위에서처럼 참조 이미지 세트를 모듈 또는 구성 수준에서 정의하세요. 브랜드 외관이 진화하면 호출 지점을 뒤지지 않고 목록 하나만 업데이트하면 됩니다. 그리고 요청 본문 구성을 단일 함수에 유지하여 엔드포인트에 필요한 필드를 한 곳에서만 추가하면 되게 하세요.

비동기 결과 폴링

생성은 비동기이므로 제출한 작업은 완료되기까지 여러 상태를 거칩니다. 클라이언트에는 상태를 확인하고, 확인 사이에 백오프하며, 결국 포기하는 대기 루프가 필요합니다.

import time

def wait_for_result(job_id: str, timeout_s: int = 300, interval_s: float = 3.0) -> dict:
    deadline = time.monotonic() + timeout_s
    delay = interval_s
    while time.monotonic() < deadline:
        resp = requests.get(
            f"{BASE_URL}/recraft-v4-styles/text-to-image/{job_id}",
            headers=HEADERS,
            timeout=30,
        )
        resp.raise_for_status()
        data = resp.json()
        status = data.get("status")
        if status in ("succeeded", "failed"):
            return data
        time.sleep(delay)
        delay = min(delay * 1.5, 15.0)
    raise TimeoutError(f"job {job_id} did not finish in {timeout_s}s")

이 루프에서 중요한 점:

  • 알 수 없는 상태를 대기 중으로 취급하세요. 진행 중 상태의 허용 목록이 아니라 명시적 종료 상태와 비교하세요. 플랫폼이 중간 상태를 추가하더라도 루프는 충돌하지 않고 계속 대기합니다.
  • 백오프하세요. 5분 동안 고정 1초 폴링은 5분간의 낭비된 요청입니다. 작은 간격으로 시작해 배수로 늘리고 상한을 두세요.
  • 대기 시간을 제한하세요. 모든 루프에는 데드라인이 필요하며, 데드라인은 호출자가 처리할 수 있는 예외로 나타나야 합니다.
  • 실패에 따라 분기하세요. 종료 실패 상태는 진단 정보를 담고 있습니다. 전체 응답 본문을 로깅하세요—'고장 났다'와 '참조 URL이 403을 반환했다'의 차이입니다.

이미지를 대량으로 처리한다면 웹 요청 핸들러에서 sleep하지 마세요. 제출하고, 작업 ID를 영속화하고, 백그라운드 워커나 예약 작업이 폴링을 구동하게 하세요. 엔드포인트 참조 페이지는 정확한 상태 값과 폴링 경로를 문서화하니, 클라이언트를 업그레이드할 때마다 확인하세요.

출력 다운로드 및 저장

작업이 종료 성공 상태에 도달하면 응답은 생성된 이미지의 URL을 포함합니다. 그 URL은 API와 스토리지 계층 사이의 인계점입니다.

바이트로 가져와 필요한 위치에 쓰세요:

def download_image(url: str, dest_path: str) -> None:
    resp = requests.get(url, timeout=60)
    resp.raise_for_status()
    with open(dest_path, "wb") as f:
        f.write(resp.content)

로컬 실험을 넘어서는 모든 경우, 아티팩트를 반환된 URL에 그대로 두지 마세요. 생성된 이미지를 임시 포인터로 취급하고 제어하는 스토리지—오브젝트 스토리지 버킷, CDN 원본, 미디어 파이프라인—로 복사하세요. 이유는 세 가지입니다:

  1. 수명 주기. 생성된 출력 URL은 무기한 지속됨을 보장하지 않습니다. 재시도 중 오래된 URL에서 재다운로드하는 것은 잠재적 무음 실패입니다.
  2. 안정적인 키. 애플리케이션은 재생성할 때마다 바뀌는 URL이 아니라 데이터베이스 행에 넣을 수 있는 결정적 식별자가 필요합니다.
  3. 출처 추적. 프롬프트, 스타일 참조 세트 버전, 작업 ID를 이미지와 함께 저장하면 나중에 어떤 자산이든 재현하거나 감사할 수 있습니다. 이 메타데이터를 동반 레코드나 데이터베이스 열에 보관하세요.

오브젝트 스토리지에 업로드할 때는 큰 이미지를 메모리에 완전히 버퍼링하지 말고 응답 본문을 스트리밍하고, 응답 헤더에서 콘텐츠 유형을 설정하세요. 자체 CDN을 통해 클라이언트에 이미지를 반환한다면 이 단계가 캐시 헤더를 적용할 위치이기도 합니다.

첫 실행 문제 해결

대부분의 첫 시도는 몇 가지 예측 가능한 방식으로 실패합니다. 다음을 순서대로 확인하세요.

요청 본문이 거부됨

400 계열 오류는 보통 JSON 형식 오류나 필수 필드 누락을 의미합니다. 먼저 curl 버전을 실행하고 보내는 정확한 요청 본문을 확인하세요. 흔한 문제: 셸 인용 부호가 JSON을 깨뜨림, API가 배열로 기대하는 필드를 문자열로 보냄, 또는 출력 옵션 값이 허용 범위를 벗어남. 엔드포인트 참조 페이지는 필드 이름과 값의 권위 있는 출처이므로 요청 본문을 문자 그대로 대조하세요.

프롬프트는 있지만 사용 가능한 스타일 참조가 없음

이 엔드포인트는 스타일 참조를 중심으로 만들어졌습니다. 요청에 프롬프트는 있으나 참조 이미지가 비어 있거나, 접근 불가하거나, 가져올 때 인증 오류를 반환하면 작업은 거부되거나 의도한 외관을 무시한 출력을 생성합니다. 플랫폼이 사용할 것과 동일한 무자격 증명 클라이언트로 각 참조 URL을 가져와 검증하세요. 세션 쿠키나 서명된 쿼리 문자열이 필요한 참조 이미지는 손상된 것으로 간주하세요.

작업 ID를 받지 못함

제출 응답은 확인이지 이미지가 아닙니다. 첫 응답에 이미지 바이트가 포함될 것이라 가정하는 코드를 작성했다면 매 실행마다 실패합니다. 응답을 파싱하고 작업 ID를 추출한 뒤에야 폴링을 시작하세요. 처음 몇 번 실행 시 전체 제출 응답을 로깅하여 그 형태를 볼 수 있게 하세요.

너무 자주 폴링함

1분에 수백 번의 상태 확인은 작업을 더 빨리 완료시키지 않으며 속도 제한을 유발할 가능성이 더 큽니다. 증가하는 간격에 상한을 두고 작업당 단일 워커를 사용하세요. 많은 작업을 디스패치한다면 작은 동시성 제한을 추가하여 제출 폭주가 폴링 폭주로 바뀌지 않게 하세요.

작업은 완료되었지만 다운로드 실패

출력 URL을 가져올 때 403 또는 404는 보통 URL이 만료되었거나, 잘못 읽은 상태 확인 이후 작업이 실패했음을 의미합니다. 종료 상태가 성공인지 확인한 뒤 응답을 다시 읽어 실제 출력 필드를 찾으세요. 아티팩트가 사용 가능해지는 즉시 저장하고 다운로드를 미루지 마세요.

파이프라인이 실행되는 순서—제출, 폴링, 다운로드—대로 문제를 하나씩 해결하면 첫 실행 실패는 거의 항상 명백한 수정으로 해소됩니다. 엔드포인트 참조와 그 요청 스키마는 모든 필드 수준 의문을 확인하는 가장 빠른 방법입니다.

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

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

API 문서 보기