Nodaro 문서
문서노드 레퍼런스모델템플릿AI 에이전트(MCP)개발자셀프 호스팅리서치비교
TypeScript SDK

장소

TypeScript로 장소를 만들고 설정 샷을 생성해 하나를 승인한 뒤, 시간대, 날씨, 계절, 앵글, 모션 베리언트를 추가하는 방법을 안내합니다.

client.locations는 장소 스튜디오가 하는 모든 작업을 스크립트로 처리합니다. 장소를 만들고 수정하며, 설정 샷 후보를 생성하고, 그중 하나를 대표 이미지로 승인하고, 시간대, 날씨, 계절, 앵글, 조명 베리언트와 분위기 클립을 추가합니다. 장소는 대표 이미지, 베리언트 컬렉션, 레퍼런스 사진, 설명을 보관하므로, 이후의 모든 샷에서 같은 장소를 보여 줄 수 있습니다. 이 메서드들은 장소 REST API를 호출합니다. 에디터에서 보는 화면은 장소를 참고하세요.

메서드

메서드기능
list(params?)내 장소 목록을 반환합니다
listArchived(params?)보관된 내 장소 목록을 반환합니다
get(id)장소 하나를 진행 중인 작업과 함께 읽어 옵니다
create(input)장소를 만듭니다
update(id, input)장소를 변경합니다
delete(id) 및 restore(id)장소를 보관하거나 다시 복원합니다
generate(input)설정 샷 후보를 생성합니다
generateAsset(input)시간대, 날씨, 계절, 앵글 또는 조명 베리언트를 생성합니다
generateSurroundContinuation(input)360도 링의 다음 뷰를 생성합니다
generateMotion(input)대표 이미지를 분위기 클립으로 애니메이션화합니다
removeAsset(id, data)베리언트 컬렉션에서 테이크 하나를 제거합니다
approveMainImage(id, candidateJobId)후보를 대표 이미지로 만듭니다
recaption(id)장소의 설명을 다시 작성합니다

client.locations

list(params?)

내 장소 목록을 반환합니다. 기본적으로 활성 장소만 반환합니다. 페이지 나누기는 선택 사항입니다. limit 없이 호출하면 전체 목록을 받고 커서는 없습니다. 최대 500인 limit을 지정하면 한 페이지와 nextCursor를 받습니다.

list(params?: { archived?: boolean; q?: string; limit?: number; cursor?: string }): Promise<{
  locations: Location[]
  nextCursor?: string | null
}>

속성

타입

const { locations } = await client.locations.list()
const { locations: cafes } = await client.locations.list({ q: "café" })

const page = await client.locations.list({ limit: 100 })
const next = await client.locations.list({ limit: 100, cursor: page.nextCursor ?? undefined })

nextCursor가 null이 될 때까지 다음 페이지를 계속 요청하세요. q의 검색 규칙은 캐릭터에 있습니다. 처음 5개 단어만 쓰이며, %, _, *는 와일드카드가 아닙니다. 각 cursor와 함께 같은 q를 보내세요.

listArchived(params?)

보관된 내 장소 목록을 반환합니다. list({ archived: true })의 단축 메서드입니다.

listArchived(params?: { limit?: number; cursor?: string }): Promise<{ locations: Location[]; nextCursor?: string | null }>

속성

타입

const { locations: archived } = await client.locations.listArchived()

get(id)

장소 하나를 읽어 옵니다. 아직 생성 중인 베리언트인 pendingJobs와, 이전 대표 이미지 후보를 최신순으로 최대 5개 담은 previousCandidates가 함께 반환됩니다. 그중 하나를 approveMainImage()로 승격하세요.

get(id: string): Promise<LocationDetail>

속성

타입

const location = await client.locations.get(locationId)
console.log(location.sourceImageUrl, location.weather, location.atmosphereMotions)

보관된 장소도 ID로는 계속 반환되므로, 그 장소를 가리키는 워크플로 노드는 계속 불러와집니다. Location에는 sourceImageUrl의 대표 이미지, 컬렉션 6개(timeOfDay, weather, seasons, angles, lighting, atmosphereMotions이며 각각 { name, url } 목록), boards, referencePhotos, canonicalDescription, styleLock, updatedAt이 있습니다.

create(input)

장소를 만듭니다. name과 nodeId는 필수입니다. 캔버스 노드가 없는 스크립트는 nodeId에 "mcp-managed"를 전달할 수 있습니다.

create(input: CreateLocationInput): Promise<{ id: string }>

속성

타입

const { id: locationId } = await client.locations.create({
  nodeId: "mcp-managed",
  name: "Rainy Tokyo Alley",
  description: "Neon-soaked alley with vending machines",
  category: "urban",
  style: "realistic",
})

update(id, input)

장소를 변경합니다. 보낸 필드만 기록됩니다. 작업하는 동안 생성 작업이 베리언트 컬렉션에 항목을 추가하므로, 베리언트 컬렉션은 이 호출에 포함되지 않습니다. 컬렉션에는 generateAsset()과 removeAsset()을 사용하세요.

update(id: string, input: UpdateLocationInput): Promise<{ id: string; updatedAt: string }>

속성

타입

await client.locations.update(locationId, {
  canonicalDescription: "A narrow, rain-soaked alley lit by neon signs",
  styleLock: false,
  piiConsentAt: new Date().toISOString(),
  expectedUpdatedAt: location.updatedAt,
})

409 concurrent_modification은 해당 code를 가진 일반 NodaroError로 전달됩니다. 장소를 다시 읽고 변경 사항을 병합한 뒤 다시 시도하세요.

delete(id) 및 restore(id)

delete()는 장소를 보관하고, restore()는 다시 복원합니다. 영구 삭제는 Nodaro 앱에서만 할 수 있습니다. 복원한 이름이 대소문자 구분 없이 활성 장소의 이름과 같으면, 서버가 (restored)를 붙이고 실제로 사용한 이름을 반환합니다.

delete(id: string): Promise<{ success: true; archived: true }>
restore(id: string): Promise<{ id: string; name: string }>

속성

타입

await client.locations.delete(locationId)
const { name } = await client.locations.restore(locationId)

generate(input)

설정 샷 후보를 생성합니다(POST /v1/generate-location). count가 1보다 크면 어떤 작업도 시작하기 전에 모든 작업을 먼저 예약하므로, 도중에 실패하면 일괄 처리 전체가 되돌려집니다.

generate(input: GenerateLocationInput): Promise<{ jobIds: string[]; jobId?: string }>

속성

타입

// One candidate, written to the location when it completes
const { jobIds: [jobId] } = await client.locations.generate({
  name: "Rainy Tokyo Alley",
  description: "Neon-soaked alley with vending machines",
  attachToLocationId: locationId,
})

// Four candidates to choose from
const { jobIds } = await client.locations.generate({ name: "Rainy Tokyo Alley", count: 4 })

attachToLocationId를 지정하고 count가 1이면, 작업이 완료될 때 결과가 대표 이미지가 됩니다. 그렇지 않으면 approveMainImage()로 후보를 고르세요. quality와 resolution의 가격은 이미지 생성과 같습니다. 모델이 지원하지 않는 값은 무시됩니다.

generateAsset(input)

장소의 베리언트를 하나 생성합니다(POST /v1/generate-location-asset). attachToLocationId, attachToColumn, attachName을 지정하면, 작업이 완료될 때 결과가 해당 컬렉션에 추가됩니다.

generateAsset(input: GenerateLocationAssetInput): Promise<{ jobId: string }>

속성

타입

const { jobId } = await client.locations.generateAsset({
  name: "Rainy Tokyo Alley",
  assetType: "weather",
  variant: "storm",
  attachToLocationId: locationId,
  attachToColumn: "weather",
  attachName: "storm",
})

generateSurroundContinuation(input)

이전 뷰에서 이어지는 360도 링의 다음 뷰를 생성합니다(POST /v1/generate-surround-continuation). 서버는 이전 뷰의 절반을 그대로 유지하고 나머지 절반을 그린 뒤 색을 맞추므로, 이웃한 뷰가 이음새 없이 이어집니다. 이 메서드는 Nodaro Cloud에서 작동합니다. 다른 에디션은 처리를 시작하기 전에 403 edition_required로 응답하며, SDK는 이를 ForbiddenError로 발생시킵니다.

generateSurroundContinuation(input: GenerateSurroundContinuationInput): Promise<{ jobId: string }>

속성

타입

const { jobId } = await client.locations.generateSurroundContinuation({
  referenceImageUrl: previousRingView,
  direction: "right",
  degrees: 45,
  provider: "nano-banana-pro",
  aspectRatio: "16:9",
  attachToLocationId: locationId,
  attachToColumn: "angles",
  attachName: "Surround 45°",
})

generateMotion(input)

동영상 생성의 이미지로 동영상 만들기 모드로 장소의 이미지를 분위기 클립으로 애니메이션화합니다(POST /v1/generate-location-motion). 장소에는 모션 컬렉션이 하나뿐이므로, 클립은 항상 atmosphereMotions에 들어가며 attachToColumn은 없습니다.

generateMotion(input: GenerateLocationMotionInput): Promise<{ jobId: string }>

속성

타입

const { jobId } = await client.locations.generateMotion({
  name: "Rainy Tokyo Alley",
  motionPrompt: "Slow dolly-in, neon signs flicker, light rain falling",
  sourceImageUrl: location.sourceImageUrl!,
  provider: "kling",
  attachToLocationId: locationId,
  attachName: "neon dolly-in",
})

removeAsset(id, data)

베리언트 컬렉션에서 테이크 하나를 제거합니다(POST /v1/locations/:id/remove-asset). 그 url을 가진 항목은 모두 한 번에 제거됩니다. 예를 들어 360도 뷰를 다시 생성하기 전에 사용하세요.

removeAsset(id: string, data: { column: LocationAttachColumn; url: string }): Promise<{ removed: true }>

속성

타입

await client.locations.removeAsset(locationId, { column: "angles", url: oldViewUrl })

URL이 그 컬렉션에 없거나 내 장소가 아니면 NotFoundError를 발생시킵니다.

approveMainImage(id, candidateJobId)

generate()로 만든 완료된 후보를 장소의 대표 이미지로 만듭니다. 그다음 비전 모델이 장소의 설명을 작성하며, 메서드는 둘 다 반환합니다.

approveMainImage(id: string, candidateJobId: string): Promise<{ sourceImageUrl: string; canonicalDescription: string | null }>

속성

타입

const { sourceImageUrl, canonicalDescription } = await client.locations.approveMainImage(locationId, jobIds[0])

설명을 작성하지 못하면 canonicalDescription은 null입니다. 그래도 대표 이미지는 설정되므로, recaption()을 호출해 다시 시도하세요.

recaption(id)

현재 대표 이미지를 바탕으로 장소의 설명을 다시 작성합니다.

recaption(id: string): Promise<{ canonicalDescription: string }>

속성

타입

const { canonicalDescription } = await client.locations.recaption(locationId)

장소에 대표 이미지가 없으면 400 no_source_image로 실패하고, 비전 모델이 실패하면 502로 실패합니다.

자주 묻는 질문

Nodaro SDK로 장소를 만들려면 어떻게 하나요?

nodeId와 name을 넣어 client.locations.create를 호출하고, client.locations.generate로 설정 샷을 생성한 다음, client.locations.approveMainImage로 하나를 승인하세요.

장소에는 어떤 베리언트를 생성할 수 있나요?

generateAsset으로 시간대, 날씨, 계절, 앵글, 조명 베리언트와 사용자 지정 베리언트를 생성하고, generateMotion으로 분위기 클립을 생성할 수 있습니다. Nodaro Cloud에서는 generateSurroundContinuation으로 360도 뷰를 추가할 수 있습니다.

다른 사람이 바꾼 장소를 덮어쓰지 않으려면 어떻게 하나요?

읽어 온 updatedAt 값을 expectedUpdatedAt으로 client.locations.update에 전달하세요. 그사이 장소가 바뀌었다면 호출은 409 concurrent_modification으로 실패합니다. 장소를 다시 읽고 병합한 뒤 다시 시도하세요.

SDK로 장소를 영구 삭제할 수 있나요?

아니요. delete는 장소를 보관하고, restore는 다시 복원합니다. 영구 삭제는 Nodaro 앱에서만 할 수 있습니다.

최종 업데이트

목차