Novita AI의 Qwen Image Edit API는 20B Qwen-Image 모델을 기반으로 자연어 명령어를 사용하여 기존 이미지를 수정합니다. 이 빠른 시작 가이드는 전체 비동기 워크플로를 다룹니다: 소스 이미지와 프롬프트로 편집 요청을 제출하고, 작업 ID를 받은 후, 편집이 완료될 때까지 폴링하여 편집된 이미지 URL을 검색합니다. 엔드포인트는 POST https://api.novita.ai/v3/async/qwen-image-edit입니다.
목차
이 빠른 시작 가이드를 사용해야 하는 경우
다음과 같은 상황에서 이 가이드를 사용하세요:
- 텍스트 명령어를 통해 기존 이미지를 수정해야 하는 경우 — 배경 변경, 스타일 변환, 요소 제거, 객체 회전, 또는 처음부터 이미지를 다시 만들지 않고 의류 조정 등.
- 설명하지 않은 영역에 편집이 번지는 것을 방지하면서 대상 편집을 적용해야 하는 경우. 모델의 이중 경로 아키텍처(의미 이해를 위한 Qwen2.5-VL + 외형 보존을 위한 VAE 인코더)는 설명하지 않은 영역으로 편집이 번지는 것을 방지하도록 특별히 설계되었습니다.
- 이미지 속 텍스트를 정밀하게 편집해야 하는 경우. Qwen Image Edit은 간판, 포스터, 브랜드 자산에서 영어와 중국어 텍스트 교체를 처리합니다 — 대부분의 이미지 생성기가 실패하는 글꼴, 크기, 주변 시각적 스타일을 보존합니다.
프롬프트만으로 새 이미지를 생성하는 경우(입력 이미지 불필요), 대신 Qwen Image txt2img 빠른 시작을 사용하세요. 모델의 기능, 벤치마크 및 아키텍처에 대한 전체 개요는 Novita AI의 Qwen-Image 출시 게시물을 참조하세요.
1단계: Novita API 키 가져오기
Novita AI 계정을 생성하고 API 키 관리로 이동합니다. 키를 생성하고 아래 코드 예제를 실행하기 전에 환경 변수로 저장하세요:
export NOVITA_API_KEY="your_api_key_here"
키를 클라이언트 측 코드, 프론트엔드 번들, 버전 관리 시스템에 포함하지 마세요. 키가 유출된 경우 동일한 설정 페이지에서 키를 교체하세요.
2단계: 엔드포인트 및 모델 확인
| 항목 | 값 |
|---|---|
| 편집 엔드포인트 | POST https://api.novita.ai/v3/async/qwen-image-edit |
| 결과 폴링 엔드포인트 | GET https://api.novita.ai/v3/async/task-result?task_id=<id> |
| 모델 | Qwen Image Edit (20B MMDiT, 이중 경로 아키텍처) |
| API 문서 | Novita AI Qwen Image Edit 참조 |
API는 비동기 방식입니다. 편집 호출은 즉시 task_id를 반환하고, 상태가 TASK_STATUS_SUCCEED가 될 때까지 결과 엔드포인트를 폴링합니다. 이는 txt2img 엔드포인트를 포함한 모든 Novita AI 이미지 생성 엔드포인트에서 사용되는 동일한 2단계 패턴입니다.
가격은 이미지당 $0.02입니다. 비용 모델을 구축하기 전에 Novita AI 가격 페이지에서 현재 요금을 확인하세요.
3단계: 첫 번째 편집 요청 보내기
소스 이미지와 편집 명령어를 사용하여 편집 엔드포인트에 POST 요청을 보냅니다:
curl -s -X POST https://api.novita.ai/v3/async/qwen-image-edit \
-H "Authorization: Bearer $NOVITA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "배경을 눈 덮인 산 풍경으로 변경해줘",
"image": "https://example.com/my-portrait.jpg"
}'
성공적인 200 응답은 다음을 반환합니다:
{
"task_id": "abc123..."
}
task_id를 저장하세요 — 편집된 이미지를 검색하는 데 필요합니다.
4단계: 결과 폴링하기
task_id를 쿼리 파라미터로 사용하여 작업 결과 엔드포인트에 GET 요청을 보냅니다:
curl -s "https://api.novita.ai/v3/async/task-result?task_id=abc123..." \
-H "Authorization: Bearer $NOVITA_API_KEY"
task_status 필드가 TASK_STATUS_SUCCEED가 될 때까지 폴링을 계속합니다:
{
"task_status": "TASK_STATUS_SUCCEED",
"images": [
{
"image_url": "https://...",
"image_url_ttl": "86400",
"image_type": "JPEG"
}
]
}
image_url은 TTL 기간(일반적으로 24시간) 후에 만료됩니다. 장기간 액세스가 필요한 경우 결과를 다운로드하여 자체 객체 스토리지에 저장하세요.
처리해야 할 상태 값:
| 상태 | 의미 |
|---|---|
TASK_STATUS_QUEUED |
요청이 대기열에서 대기 중입니다. 계속 폴링하세요. |
TASK_STATUS_PROCESSING |
편집이 진행 중입니다. 계속 폴링하세요. |
TASK_STATUS_SUCCEED |
완료됨. images[0].image_url에서 편집된 이미지를 읽으세요. |
TASK_STATUS_FAILED |
생성 실패. 오류 필드에서 이유를 확인하세요. |
5단계: 가격, 제한 및 일반 오류 확인
가격: 이미지당 $0.02 (가격 페이지에서 확인).
속도 제한: 현재 속도 제한은 API 참조를 확인하세요. 429 오류가 발생하면 요청 속도를 줄이고 재시도에 지수 백오프를 구현하세요.
일반적인 HTTP 오류:
| 상태 코드 | 예상 원인 |
|---|---|
| 400 | 잘못된 요청 본문 — prompt 또는 image 누락, 잘못된 이미지 형식, 또는 지원되지 않는 입력 크기 |
| 401 | API 키 누락 또는 유효하지 않음 — Authorization: Bearer <key> 형식 확인 |
| 429 | 속도 제한 초과 — 백오프 및 재시도 로직 추가 |
| 500 | 서버 측 오류 — 잠시 후 재시도 |
작업 상태가 TASK_STATUS_FAILED인 경우 응답에 일반적으로 오류 메시지가 포함됩니다. 일반적인 원인으로는 지원되지 않는 이미지 형식, 크기 제한을 초과하는 이미지, 또는 콘텐츠 안전 필터를 트리거하는 프롬프트가 있습니다.
Python 예제
이 스크립트는 편집 요청을 제출하고, 완료될 때까지 폴링한 후 출력 이미지 URL을 출력합니다:
import os
import time
import requests
API_KEY = os.environ["NOVITA_API_KEY"]
BASE_URL = "https://api.novita.ai/v3/async"
def submit_edit(image: str, prompt: str, seed: int | None = None) -> str:
payload: dict = {"prompt": prompt, "image": image}
if seed is not None:
payload["seed"] = seed
response = requests.post(
f"{BASE_URL}/qwen-image-edit",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
json=payload,
)
response.raise_for_status()
return response.json()["task_id"]
def poll_result(task_id: str, interval: float = 2.0, timeout: float = 120.0) -> dict:
deadline = time.time() + timeout
while time.time() < deadline:
response = requests.get(
f"{BASE_URL}/task-result",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"task_id": task_id},
)
response.raise_for_status()
data = response.json()
status = data.get("task_status", "")
if status == "TASK_STATUS_SUCCEED":
return data
if status == "TASK_STATUS_FAILED":
raise RuntimeError(f"편집 작업 실패: {data}")
time.sleep(interval)
raise TimeoutError(f"작업 {task_id}이(가) {timeout}초 내에 완료되지 않았습니다")
if __name__ == "__main__":
task_id = submit_edit(
image="https://example.com/product-photo.jpg",
prompt="자켓을 빨간색 가죽 자켓으로 변경하고, 배경은 유지해줘",
)
print(f"작업 ID: {task_id}")
result = poll_result(task_id)
for img in result["images"]:
print(img["image_url"])
cURL 예제
JSON 파싱을 위해 jq를 사용하는 2단계 명령어 워크플로:
#!/bin/bash
# 1단계: 편집 요청 제출
TASK_ID=$(curl -s -X POST https://api.novita.ai/v3/async/qwen-image-edit \
-H "Authorization: Bearer $NOVITA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "수채화 스타일로 변환하고, 피사체와 구도는 유지해줘",
"image": "https://example.com/landscape.jpg"
}' | jq -r '.task_id')
echo "작업 ID: $TASK_ID"
# 2단계: 완료될 때까지 폴링
while true; do
RESULT=$(curl -s "https://api.novita.ai/v3/async/task-result?task_id=$TASK_ID" \
-H "Authorization: Bearer $NOVITA_API_KEY")
STATUS=$(echo "$RESULT" | jq -r '.task_status')
echo "상태: $STATUS"
if [ "$STATUS" = "TASK_STATUS_SUCCEED" ]; then
echo "$RESULT" | jq -r '.images[].image_url'
break
elif [ "$STATUS" = "TASK_STATUS_FAILED" ]; then
echo "작업 실패:"
echo "$RESULT" | jq .
exit 1
fi
sleep 2
done
jq가 설치되어 있지 않은 경우 apt install jq(Debian/Ubuntu) 또는 brew install jq(macOS)를 사용하여 설치하세요.
주요 파라미터
| 파라미터 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
prompt |
string | 예 | — | 원하는 편집을 설명하는 자연어 명령어 |
image |
string | 예 | — | URL 또는 base64로 인코딩된 문자열 형태의 소스 이미지 |
seed |
integer | 아니요 | 랜덤 | 여러 실행에서 재현 가능한 출력을 위한 고정 시드 |
output_format |
string | 아니요 | jpeg |
출력 형식 — 허용되는 값은 API 문서 참조 |
효과적인 프롬프트 작성법: 구체성이 중요합니다. "광고판의 텍스트를 'Summer Sale’로 변경해줘"는 모델에게 명확하고 제한된 작업을 제공합니다. "이미지를 개선해줘"는 그렇지 않습니다. 하나의 요소를 변경하고 다른 모든 요소는 그대로 두려면 보존하려는 것을 명시하세요: “자켓 색상을 빨간색으로 변경하고, 배경과 얼굴은 변경하지 마세요.” 이는 VAE 인코더 경로가 변경되지 않은 영역을 고정하는 작업을 수행하는 데 도움이 됩니다.
반복 작업을 위한 seed 사용: 프롬프트를 조정할 때 시드를 고정하면 편집 결과를 직접 비교할 수 있습니다 — 출력 변동은 랜덤 샘플링이 아닌 프롬프트 변경에서 비롯됩니다. 효과적인 프롬프트를 찾으면 여러 편집에서 출력 다양성을 원하는 경우 시드 제약을 제거하세요.
일반적인 사용 사례
배경 교체: “배경을 미니멀한 화이트 스튜디오로 교체해줘” — Qwen Image Edit은 환경을 전환하면서 피사체의 가장자리와 조명을 보존합니다. 피사체가 원본 배경과 명확한 경계를 가질 때 안정적으로 작동합니다.
이미지 속 텍스트: 상점 간판, 포스터 제목, 제품 라벨을 새 텍스트로 교체 — 중국어 및 영어 포함. 모델은 전체 영역을 재생성하는 경향이 있는 생성적 인페인팅 방식보다 글꼴 스타일과 주변 시각적 맥락을 더 잘 보존합니다.
스타일 변환: “유화 스타일로 변환해줘” 또는 "연필 스케치처럼 보이게 해줘"와 같은 명령을 소스 이미지에 적용합니다. VAE 인코더 경로는 원본 구도와 객체 배치를 보존하고 프롬프트가 스타일 변환을 주도합니다.
객체 수준 변경: 의류 교체, 특정 항목 색상 변경, 액세서리 추가 또는 제거. 명령어는 편집이 인접 영역에 영향을 미치는 것을 방지하기 위해 변경할 특정 요소를 명명해야 합니다.
IP 변환 및 객체 회전: 모델은 뷰 합성 변경(예: 객체를 회전하여 다른 각도로 표시)과 캐릭터 또는 제품 이미지의 시각적 스타일 간 변환을 지원합니다 — Novita AI의 Qwen-Image-Edit 개요에 문서화된 사용 사례입니다.
문제 해결
출력이 변경되지 않음: 명령어가 너무 모호할 가능성이 높습니다. 대상 요소를 더 명확하게 지정하세요: “빨간색으로 만들어줘” 대신 “자동차 색상을 파란색에서 빨간색으로 변경해줘.”
원치 않는 편집이 의도하지 않은 영역으로 퍼짐: 매우 특정한 로컬 편집의 경우 프롬프트에 명시적인 보존 언어를 포함하세요: “X만 변경하고 Y는 그대로 두세요.” 지나치게 개방적인 프롬프트는 모델이 전체 구성을 재해석할 여지를 줍니다.
작업이 완료되었지만 이미지 URL이 403 또는 404 반환: image_url_ttl 필드(초 단위)는 URL의 유효 기간을 알려줍니다. TTL 이후에 URL을 폴링하면 URL이 만료된 것입니다. 성공적인 폴링 직후 이미지를 다운로드하세요.
401 Unauthorized: Authorization 헤더 형식이 Bearer 뒤에 공백이 있는 정확히 Bearer <key>인지 확인하세요. 키가 활성 상태이고 Novita AI 콘솔에서 해지되지 않았는지 확인하세요.
작업이 계속해서 시간 초과됨: 이미지 편집은 텍스트 생성보다 시간이 더 걸립니다 — 일반적으로 정상 부하에서 15~60초. 작업이 일관되게 시간 초과를 초과하는 경우 실패로 처리하기 전에 폴링 시간을 늘리세요.
자주 묻는 질문
소스 이미지가 Novita의 서버에 남아 있나요?
이미지는 HTTPS를 통해 Novita AI의 API에 업로드되어 해당 인프라에서 처리됩니다. 민감한 개인 정보, 개인 건강 데이터 또는 기밀 콘텐츠가 포함된 이미지를 보내기 전에 Novita AI의 데이터 처리 정책을 검토하세요.
URL 대신 base64로 인코딩된 이미지를 사용할 수 있나요?
네. image 필드에 원시 base64 문자열을 전달하세요. base64 페이로드의 크기 제한은 API 참조를 확인하세요.
일관성을 위한 시드(seed) 파라미터가 있나요?
네 — 선택적 seed 파라미터는 랜덤 상태를 고정하여 동일한 프롬프트와 이미지로 일관된 결과를 생성합니다. 재샘플링으로 인한 출력 노이즈 없이 프롬프트 문구를 반복하는 데 유용합니다.
이 엔드포인트를 배치 작업에 사용할 수 있나요?
네. 여러 편집 요청을 병렬로 제출하고 task_id 값을 수집한 다음 동시에 폴링하세요. 각 요청은 독립적입니다.
이 엔드포인트와 Qwen Image txt2img 엔드포인트의 차이점은 무엇인가요?
편집 엔드포인트는 소스 이미지가 필요하고 수정된 버전을 생성합니다. txt2img 엔드포인트는 입력 이미지 없이 텍스트 프롬프트만으로 이미지를 생성합니다. 수정하려는 기존 콘텐츠가 있을 때는 편집 엔드포인트를 사용하고, 처음부터 생성할 때는 txt2img를 사용하세요.
