오브젝트 및 크리처
Nodaro SDK로 TypeScript에서 오브젝트와 크리처를 만들고, 대표 이미지와 베리언트를 생성해 애니메이션화하며, 크리처가 말하게 하는 방법을 안내합니다.
client.objects는 오브젝트/소품 스튜디오가 소품, 제품, 탈것에 대해 하는 모든 작업을 스크립트로 처리하고, client.creatures는 동물과 크리처에 대해 같은 일을 합니다. 두 리소스 모두 항목을 만들고 수정하며, 대표 이미지 후보를 생성해 그중 하나를 승인하고, 베리언트와 모션 클립을 추가하며, 이후 프롬프트에서 항목을 일관되게 유지하는 설명을 작성합니다. 이 메서드들은 오브젝트 REST API와 크리처 REST API를 호출합니다. 에디터에서 보는 화면은 오브젝트와 크리처를 참고하세요.
메서드
두 리소스의 메서드는 같습니다.
| 메서드 | 기능 |
|---|---|
objects.list(params?), creatures.list(params?) | 내 항목 목록을 반환합니다 |
objects.listArchived(params?) | 보관된 내 항목 목록을 반환합니다 |
objects.get(id) | 항목 하나를 진행 중인 작업과 함께 읽어 옵니다 |
objects.create(input), creatures.create(input) | 항목을 만듭니다 |
objects.update(id, input), creatures.update(id, input) | 항목을 변경합니다 |
objects.delete(id) 및 restore(id) | 항목을 보관하거나 다시 복원합니다 |
objects.permanentDelete(id) | 보관된 항목과 그 파일을 완전히 삭제합니다 |
objects.generate(input), creatures.generate(input) | 대표 이미지 후보를 생성합니다 |
objects.generateAsset(input), creatures.generateAsset(input) | 베리언트를 생성합니다 |
objects.generateMotion(input), creatures.generateMotion(input) | 대표 이미지를 클립으로 애니메이션화합니다 |
objects.approveMainImage(id, candidateJobId, expectedUpdatedAt?) | 후보를 대표 이미지로 만듭니다 |
objects.recaption(id) | 설명을 다시 작성합니다 |
client.objects
오브젝트에는 sourceImageUrl의 대표 이미지, 컬렉션 4개(angles, materials, variations, motionClips이며 각각 { name, url } 목록), boards, referencePhotos, canonicalDescription, styleLock이 있습니다. category는 furniture, vehicle, weapon, food, clothing, electronics, nature, tool, animal, other 중 하나입니다.
Object는 JavaScript 전역 객체와 이름이 같습니다. 둘 다 필요하면 다른 이름으로 가져오세요. import type { Object as NodaroObject } from "@nodaro/sdk"
objects.list(params?)
내 오브젝트 목록을 반환합니다. 기본적으로 활성 오브젝트만 반환합니다. 페이지 나누기는 선택 사항입니다. limit 없이 호출하면 전체 목록을 받고, 최대 500인 limit을 지정하면 한 페이지와 nextCursor를 받습니다.
list(params?: { archived?: boolean; projectId?: string; q?: string; limit?: number; cursor?: string }): Promise<{
objects: Object[]
nextCursor?: string | null
}>속성
타입
const { objects } = await client.objects.list()
const { objects: cars } = await client.objects.list({ q: "red car" })
const page = await client.objects.list({ limit: 100 })objects.listArchived(params?)
보관된 내 오브젝트 목록을 반환하며, list({ archived: true })의 단축 메서드입니다. creatures.listArchived()도 같은 방식으로 작동합니다.
listArchived(params?: { projectId?: string; limit?: number; cursor?: string }): Promise<{ objects: Object[]; nextCursor?: string | null }>속성
타입
const { objects: archived } = await client.objects.listArchived()objects.get(id)
오브젝트 하나를 아직 생성 중인 베리언트인 pendingJobs와 함께 읽어 옵니다. 보관된 오브젝트는 반환되지 않으며, NotFoundError를 발생시킵니다. creatures.get()도 같은 방식으로 작동합니다.
get(id: string): Promise<ObjectDetail>속성
타입
const object = await client.objects.get(objectId)
console.log(object.sourceImageUrl, object.materials)objects.create(input)
오브젝트를 만듭니다. name과 nodeId는 필수입니다. 캔버스 노드가 없는 스크립트는 nodeId에 "mcp-managed"를 전달할 수 있습니다.
create(input: CreateObjectInput): Promise<{ id: string }>속성
타입
const { id: objectId } = await client.objects.create({
nodeId: "mcp-managed",
name: "Antique Lantern",
description: "Weathered brass lantern with hand-engraved filigree",
category: "tool",
style: "realistic",
})objects.update(id, input)
오브젝트를 변경합니다. 보낸 필드만 기록됩니다. 작업하는 동안 생성 작업이 베리언트 컬렉션에 항목을 추가하므로, 베리언트 컬렉션은 이 호출에 포함되지 않습니다.
update(id: string, input: UpdateObjectInput): Promise<{ id: string; updatedAt: string }>속성
타입
await client.objects.update(objectId, {
canonicalDescription: "A weathered brass lantern with engraved filigree and a glass chimney",
expectedUpdatedAt: object.updatedAt,
})409 concurrent_modification은 일반 NodaroError로 전달됩니다. 오브젝트를 다시 읽고 병합한 뒤 다시 시도하세요.
objects.delete(id) 및 restore(id)
delete()는 오브젝트를 보관합니다. 보관된 오브젝트에 다시 호출해도 아무것도 바뀌지 않습니다. restore()는 오브젝트를 다시 복원합니다. 이때 이름이 대소문자 구분 없이 활성 오브젝트의 이름과 같으면, 서버가 (restored)를 붙이고 실제로 사용한 이름을 반환합니다.
delete(id: string): Promise<{ success: true; archived: true }>
restore(id: string): Promise<{ id: string; name: string }>속성
타입
await client.objects.delete(objectId)
const { name } = await client.objects.restore(objectId)objects.permanentDelete(id)
보관된 오브젝트와 오브젝트가 참조하는 저장된 파일을 모두 완전히 삭제합니다. 보관된 오브젝트에서만 작동하며, 활성 오브젝트는 400 not_archived로 실패합니다. 먼저 delete()로 보관하세요. creatures.permanentDelete()도 같은 방식으로 작동합니다.
permanentDelete(id: string): Promise<{ success: true; permanent: true }>속성
타입
await client.objects.delete(objectId)
await client.objects.permanentDelete(objectId)Nodaro의 MCP 도구는 이 작업을 제공하지 않으므로, AI 어시스턴트가 내 오브젝트를 완전히 삭제할 수 없습니다.
objects.generate(input)
대표 이미지 후보를 생성합니다(POST /v1/generate-object). count가 1보다 크면 어떤 작업도 시작하기 전에 모든 작업을 먼저 예약하므로, 도중에 실패하면 일괄 처리 전체가 되돌려집니다.
generate(input: GenerateObjectInput): Promise<{ jobIds: string[]; jobId?: string }>속성
타입
const { jobIds } = await client.objects.generate({ name: "Antique Lantern", count: 4 })
for (const jobId of jobIds) {
// poll each candidate with client.jobs.getStatus(jobId)
}jobIds는 항상 있으며, 후보마다 ID가 하나씩 담깁니다. jobId는 후보 하나에 대한 예전 별칭이므로 jobIds를 사용하세요. attachToObjectId를 지정하고 후보가 하나이면, 작업이 완료될 때 결과가 대표 이미지가 됩니다. 그렇지 않으면 approveMainImage()로 하나를 고르세요.
objects.generateAsset(input)
베리언트 하나를 생성합니다(POST /v1/generate-object-asset). attachToObjectId, attachToColumn, attachName을 지정하면, 작업이 완료될 때 결과가 해당 컬렉션에 추가됩니다.
generateAsset(input: GenerateObjectAssetInput): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.objects.generateAsset({
name: "Antique Lantern",
assetType: "materials",
variant: "gold",
attachToObjectId: objectId,
attachToColumn: "materials",
attachName: "gold",
})angles, materials, variations, motion에서는 에셋 타입에 따라 컬렉션이 정해집니다. custom 베리언트에는 attachToColumn이 필요합니다.
objects.generateMotion(input)
동영상 생성의 이미지로 동영상 만들기 모드로 오브젝트의 이미지를 클립으로 애니메이션화합니다(POST /v1/generate-object-motion). 클립은 항상 motionClips에 들어갑니다. 기본값은 제품 샷에 맞춰져 있어, 모델은 kling-turbo, 화면 비율은 1:1입니다.
generateMotion(input: GenerateObjectMotionInput): Promise<{ jobId: string }>속성
타입
const { jobId } = await client.objects.generateMotion({
name: "Antique Lantern",
motionPrompt: "Slow 360-degree rotation, soft golden rim light",
sourceImageUrl: object.sourceImageUrl!,
attachToObjectId: objectId,
attachName: "rotate-360",
})objects.approveMainImage(id, candidateJobId, expectedUpdatedAt?)
generate()로 만든 완료된 후보를 오브젝트의 대표 이미지로 만듭니다. 그다음 비전 모델이 오브젝트의 설명을 작성하며, 메서드는 둘 다 반환합니다.
approveMainImage(id: string, candidateJobId: string, expectedUpdatedAt?: string): Promise<{
sourceImageUrl: string
canonicalDescription: string | null
}>속성
타입
const { sourceImageUrl, canonicalDescription } = await client.objects.approveMainImage(objectId, jobIds[0])설명을 작성하지 못하면 canonicalDescription은 null입니다. 그래도 대표 이미지는 설정되므로, recaption()을 호출해 다시 시도하세요.
objects.recaption(id)
현재 대표 이미지를 바탕으로 오브젝트의 설명을 다시 작성합니다. 이 호출은 반복해도 안전하며 동시성 토큰을 받지 않습니다.
recaption(id: string): Promise<{ canonicalDescription: string }>속성
타입
const { canonicalDescription } = await client.objects.recaption(objectId)오브젝트에 대표 이미지가 없으면 400 main_image_required로 실패하고, 비전 모델이 실패하면 502로 실패합니다.
client.creatures
크리처는 동물이나 판타지 존재입니다. 오브젝트처럼 작동하지만, 네 가지가 다릅니다.
species는dragon이나wolf같은 자유 텍스트 종류이며, 대표 이미지 프롬프트의 피사체가 됩니다.category도 자유 텍스트입니다.poses가materials를 대체하므로, 컬렉션은angles,poses,variations,motionClips입니다. 베리언트 타입은angles,poses,variations,custom입니다.boards는 이름이 붙은 크리처 보드를 최대 24개 담습니다. 크리처 보드는 이미지 생성의 크리처 보드 프리셋으로 만든 촘촘한 레퍼런스 시트입니다. 이 목록은 내가 관리합니다.create()와update()는 목록 전체를 대체합니다.voice는 크리처를 말하는 크리처로 만듭니다. 캐릭터 보이스와 형태가 같으며,{ voiceId, voiceName, traits, voiceType?, previewUrl?, ttsProvider? }입니다. 보이스를 제거하려면voice: null을 전달하세요.
creatures.listArchived(), get(), delete(), restore(), permanentDelete(), approveMainImage(), recaption()은 같은 인수를 받으며, 위의 오브젝트 메서드처럼 동작합니다.
creatures.list(params?)
list(params?: { archived?: boolean; projectId?: string; q?: string; limit?: number; cursor?: string }): Promise<{
creatures: Creature[]
nextCursor?: string | null
}>속성
타입
const { creatures } = await client.creatures.list({ q: "dragon" })creatures.list()는 limit과 cursor를 쓰는 선택적 페이지 나누기를 포함해 objects.list()와 같은 매개변수를 받습니다. q의 검색 규칙은 캐릭터에 있습니다.
creatures.create(input)
create(input: CreateCreatureInput): Promise<{ id: string }>속성
타입
const { id: creatureId } = await client.creatures.create({
nodeId: "mcp-managed",
name: "Biscuit",
species: "ginger cat",
style: "realistic",
})creatures.update(id, input)
nodeId를 제외한 create()의 필드에 더해 boards, selectedAssetByVariant, expectedUpdatedAt을 받습니다. 보낸 필드만 기록됩니다.
update(id: string, input: UpdateCreatureInput): Promise<{ id: string; updatedAt: string }>속성
타입
await client.creatures.update(creatureId, {
voice: { voiceId: chosenVoiceId, voiceName: "Aria", traits: "smug, unhurried" },
})creatures.generate(input)
대표 이미지 후보를 생성합니다. objects.generate()의 필드에 더해 species를 받고, { jobIds }를 반환합니다.
generate(input: GenerateCreatureInput): Promise<{ jobIds: string[]; jobId?: string }>속성
타입
const { jobIds } = await client.creatures.generate({ name: "Biscuit", species: "ginger cat", count: 4 })creatures.generateAsset(input)
베리언트 하나를 생성합니다. objects.generateAsset()처럼 작동하며, attachToCreatureId를 씁니다.
generateAsset(input: GenerateCreatureAssetInput): Promise<{ jobId: string }>속성
타입
await client.creatures.generateAsset({
name: "Biscuit",
assetType: "poses",
variant: "sitting",
attachToCreatureId: creatureId,
attachToColumn: "poses",
attachName: "sitting",
})creatures.generateMotion(input)
크리처의 이미지를 클립으로 애니메이션화합니다. objects.generateMotion()처럼 작동하며, 기본값도 kling-turbo와 1:1로 같고, 클립을 motionClips에 추가합니다.
generateMotion(input: GenerateCreatureMotionInput): Promise<{ jobId: string }>속성
타입
await client.creatures.generateMotion({
name: "Biscuit",
motionPrompt: "The cat stretches, then yawns",
sourceImageUrl: creature.sourceImageUrl!,
attachToCreatureId: creatureId,
})말하는 크리처 만들기
음성에는 크리처 전용 메서드가 필요 없습니다. 크리처의 보이스로 대사를 렌더링한 다음, 크리처의 이미지에 립싱크하세요.
const creature = await client.creatures.get(creatureId)
// 1. Speak the line in the creature's voice
const speech = await client.nodes.runAndWait("text-to-speech", {
text: "I knocked the vase off the shelf. I regret nothing.",
voice: creature.voice!.voiceId,
provider: creature.voice!.ttsProvider,
voiceType: creature.voice!.voiceType,
})
// 2. Lip-sync the audio onto the creature's main image
const clip = await client.nodes.runAndWait("lip-sync", {
imageUrl: creature.sourceImageUrl!,
audioUrl: speech.audioUrl,
provider: "kling-avatar",
})
console.log(clip.videoUrl)립싱크 노드는 기존 동영상을 더빙할 수도 있습니다. videoUrl과 동영상을 받는 모델을 전달하세요. volcengine-lipsync는 더빙에서 가장 가격이 낮은 옵션이며, 여러 화자를 처리하는 유일한 옵션입니다.
const dub = await client.nodes.runAndWait("lip-sync", {
videoUrl: "https://example.com/scene.mp4",
audioUrl: "https://example.com/new-vocal.mp3",
provider: "volcengine-lipsync",
mode: "basic", // for complex scenes
openScenedet: true, // several speakers: scene and speaker detection
audioDurationSec: 42, // sets the per-second price; without it, you pay for 5 minutes
})크리처를 레퍼런스로 샷에 넣으려면 @nodaro/shared의 toConnectedReference({ kind: "creature", id, name, url, description })로 레퍼런스를 만드세요. 그러면 이미지 생성 노드가 크리처의 해부학적 구조, 무늬, 색을 유지하는 문장을 추가합니다. 레퍼런스를 참고하세요.
자주 묻는 질문
Nodaro에서 오브젝트와 크리처는 어떻게 다른가요?
오브젝트는 소품, 제품, 탈것으로, 앵글, 재질, 변형 베리언트를 가집니다. 크리처는 동물이나 판타지 존재로, 앵글, 포즈, 변형 베리언트와 함께 종, 레퍼런스 보드, 선택 사항인 보이스를 가집니다.
오브젝트의 대표 이미지 후보를 생성하려면 어떻게 하나요?
name과 count를 넣어 client.objects.generate를 호출하세요. 항상 후보마다 ID가 하나씩 담긴 jobIds를 반환합니다. 후보가 완료되면 client.objects.approveMainImage로 하나를 승인하세요.
SDK로 오브젝트를 영구 삭제할 수 있나요?
네, 두 단계로 할 수 있습니다. delete()로 보관한 다음 permanentDelete()를 호출하면 오브젝트와 오브젝트가 참조하는 모든 파일이 삭제됩니다. 활성 오브젝트는 400 not_archived로 거부됩니다.
크리처가 말하게 하려면 어떻게 하나요?
텍스트 음성 변환 노드와 크리처의 보이스로 음성을 렌더링한 다음, 크리처의 대표 이미지와 그 오디오로 립싱크 노드를 실행하세요.
관련 문서
오브젝트와 소품
동물과 크리처
오브젝트
크리처
캐릭터
최종 업데이트