# 피커 카탈로그

> @nodaro/prompts로 내 앱에 Nodaro 피커를 만들고, 선택을 프롬프트 구절로 바꾸고, 레이블을 번역하고, 그림을 보여 주거나 API로 카탈로그를 읽으세요.

Source: https://nodaro.ai/ko/docs/developers/picker-catalogs

**피커 카탈로그**는 [**분위기**](https://nodaro.ai/docs/nodes/creative-controls/mood), [**렌즈**](https://nodaro.ai/docs/nodes/creative-controls/lens), [**프레이밍**](https://nodaro.ai/docs/nodes/creative-controls/framing), [**인물**](https://nodaro.ai/docs/nodes/creative-controls/person) 같은 크리에이티브 컨트롤 노드, 즉 Nodaro 피커 하나를 뒷받침하는 데이터입니다. 카탈로그에는 피커의 선택지, 각 선택지가 추가하는 프롬프트 텍스트, 카테고리, 번역 키가 들어 있습니다. 카탈로그는 npm 패키지에 일반 데이터로 들어 있으므로, 내 앱에 내 스타일로 같은 피커를 만들고, Nodaro가 만드는 것과 정확히 같은 프롬프트를 만들 수 있습니다.

피커는 모델을 호출하지 않습니다. 피커는 연결된 노드의 프롬프트에 설명 구절을 추가합니다. [크리에이티브 컨트롤](https://nodaro.ai/docs/guides/creative-controls)을 참조하세요.

## 패키지 가져오기 또는 API 호출
| 내 앱 | 사용할 것 |
| --- | --- |
| npm 패키지를 번들링할 수 있음 | `@nodaro/prompts`에서 카탈로그를 가져오세요. 타입이 지정되어 있고, 오프라인에서 작동하며, API 호출이 필요 없습니다. |
| MCP를 쓰는 AI 에이전트처럼 패키지를 번들링할 수 없음 | 탐색 인터페이스에서 같은 데이터를 읽으세요. REST 엔드포인트 `GET /v1/picker-catalogs`, [SDK](https://nodaro.ai/docs/developers/sdk)의 `client.pickerCatalogs`, [CLI](https://nodaro.ai/docs/developers/cli/commands#pickers)의 `nodaro pickers`, 또는 MCP 도구 `get_picker_catalog`를 사용합니다. |

가능하면 패키지를 사용하세요. 릴리스할 때만 바뀌는 데이터를 위해 네트워크 왕복을 하지 않아도 됩니다.

```bash
npm install @nodaro/prompts @nodaro/shared @nodaro/sdk
```

`@nodaro/prompts`에는 카탈로그와 프롬프트 함수가, `@nodaro/shared`에는 번역이 들어 있고, `@nodaro/sdk`는 생성을 실행합니다.

## 레지스트리
```ts

```

| 내보내기 | 반환하는 것 |
| --- | --- |
| `PICKER_CATALOGS` | 모든 피커 카탈로그입니다. 단일 차원 피커 28개와 다중 차원 피커 11개입니다. |
| `getPickerCatalog(nodeTypeOrCatalogId)` | 노드 유형(예: `"mood"`)이나 카탈로그 ID로 찾은 카탈로그 하나입니다. |
| `listPickerCatalogs()` | 전체 카탈로그입니다. |

카탈로그 타입은 다음과 같습니다.

```ts
interface PickerOption {
id: string
label: string            // English display text; translate it with the catalogId (see below)
description?: string
category?: string        // group id, matching categoryOrder and categoryLabels
promptHint: string       // the clause this option adds ("" for a no-op option such as "auto")
term: string             // the short professional term Compact mode adds ("" for a no-op option)
icon?: string            // reserved; pictures are served separately (see "Pictures")
}

interface PickerDimension {  // multi-dimension pickers, and the secondary fields of a single-dimension picker
field: string              // for example "shotSize"
label: string
options: readonly PickerOption[]
}

interface PickerCatalog {
nodeType: string           // "mood", "framing", ...
label: string
catalogId: string          // the key of the translations
kind: "single" | "multi"
valueField?: string        // single: the field a selection writes
defaultValue?: string
categoryOrder?: readonly string[]
categoryLabels?: Readonly<Record<string, string>>
options?: readonly PickerOption[]        // single-dimension pickers
fields?: readonly string[]               // multi-dimension pickers: the dimension fields
dimensions?: readonly PickerDimension[]  // multi-dimension pickers, and secondary fields
}
```

`label`은 화면에 표시하고, 모델에는 `term`이나 `promptHint`를 보내세요. 하나에서 다른 하나를 만들어 내지 마세요.

## 단일 차원 피커 만들기
분위기 같은 단일 차원 피커는 평평한 목록에서 하나를 고르는 피커이며, 목록은 그룹으로 나뉠 수 있습니다. 그리드를 그리는 데 필요한 모든 것은 `options`에 들어 있습니다.

```tsx

const client = createClient({
baseUrl: "https://app.nodaro.ai",
auth: new StaticTokenAuth(process.env.NODARO_ACCESS_TOKEN!),
})
const mood = getPickerCatalog("mood")! // { nodeType: "mood", valueField: "mood", options, categoryOrder, categoryLabels }

// 1. Render your own tile grid, grouped by category
function MoodPicker({ value, onChange }: { value?: string; onChange: (id: string) => void }) {
return (mood.categoryOrder ?? [undefined]).map((cat) => (
<section key={cat ?? "all"}>
{cat && <h4>{mood.categoryLabels?.[cat]}</h4>}
{mood.options!
.filter((o) => !cat || o.category === cat)
.map((o) => (
<button key={o.id} aria-pressed={value === o.id} title={o.description} onClick={() => onChange(o.id)}>
{o.label}
</button>
))}
</section>
))
}

// 2. Selection, then prompt clause, then generation
const selected = "serene"
const clause = getParameterPromptHint({ type: "mood", data: { mood: selected } })
// or: mood.options!.find((o) => o.id === selected)!.promptHint

const prompt = ["a portrait of a woman", clause].filter(Boolean).join(", ")
const result = await client.nodes.runAndWait("generate-image", { prompt })
console.log(result.imageUrl)
```

### 전환, 캐릭터 효과, 캐릭터 모션의 보조 매개변수
단일 차원 피커 세 개는 주 선택 외에 추가 필드가 있습니다. [**전환**](https://nodaro.ai/docs/nodes/creative-controls/transition)과 [**캐릭터 효과**](https://nodaro.ai/docs/nodes/creative-controls/character-fx)에는 각각 `position`, `duration`, `intensity`가 있습니다. [**캐릭터 모션**](https://nodaro.ai/docs/nodes/creative-controls/character-motion)에는 `position`과 `pace`가 있습니다.

이 필드들도 카탈로그이며, 다른 모든 선택지와 같은 형태의 행을 가지고, 각 목록의 맨 앞에는 아무 동작도 하지 않는 `auto`가 있습니다. 카탈로그는 이 필드들을 `options`에 **더해** `dimensions`로 제공합니다. 주 피커는 `options`로, 드롭다운은 `dimensions`로 그리세요. ID만 보내는 클라이언트가 타이밍 구절을 직접 쓰는 일은 없습니다.

```ts
const fx = getPickerCatalog("character-fx")!
fx.options     // the effects of the main picker
fx.dimensions  // [{ field: "position", ... }, { field: "duration", ... }, { field: "intensity", ... }]

// Each dimension's rows are also exported directly:
//   TRANSITION_POSITIONS / TRANSITION_DURATIONS / TRANSITION_INTENSITIES
//   CHARACTER_FX_POSITIONS / CHARACTER_FX_DURATIONS / CHARACTER_FX_INTENSITIES
//   CHARACTER_MOTION_POSITIONS / CHARACTER_MOTION_PACES
```

전환과 캐릭터 효과는 같은 ID를 공유합니다. `start`, `middle`, `end`, `full`, 그리고 `instant`, `short`, `medium`, `long`, 그리고 `subtle`, `natural`, `dynamic`, `crazy`입니다. 하지만 문구는 공유하지 **않습니다**. 전환의 문구는 일어나서 클립 전체에 걸친다고 표현하고, 효과의 문구는 나타나서 지속된다고 표현합니다. 행은 항상 그 노드 자체의 카탈로그에서 읽으세요. 캐릭터 모션은 위치 ID를 공유하고, 자체 속도 ID인 `slow-motion`, `slow`, `natural`, `fast`, `explosive`를 자체 문구와 함께 추가합니다.

**전환의 컷 플래그.** `@nodaro/prompts` 카탈로그(`getPickerCatalog("transition")`)에서 컷인 전환에는 `instant: true`가 붙습니다. 컷에서는 `duration`과 `intensity`가 문구를 추가하지 않고, `position`의 `full`도 아무것도 추가하지 않습니다. 블렌드할 수 있는 두 컷인 `seamless-match`와 `jump-match`에는 `blendable: true`도 붙습니다. `duration` 차원의 `short` 행에는 `blendsCut: true`가 붙습니다. 이 행은 블렌드할 수 있는 컷을 [블렌드 컷](https://nodaro.ai/docs/nodes/creative-controls/transition#blended-cut)으로 바꾸는 단계입니다. 따라서 컷에는 `full`을 뺀 `position`을 보여 주고, `intensity`는 보여 주지 마세요. 블렌드할 수 있는 컷에는 `duration`을 블렌드로 보여 주세요. 하드 컷을 유지하는 `auto`와 `blendsCut`이 붙은 행입니다. 여러 개를 고른 경우, `@nodaro/prompts`가 내보내는 `isInstantTransition`과 `isBlendableTransition`은 고른 것이 모두 해당할 때만 true입니다. 이 플래그는 패키지의 카탈로그에만 있습니다. `GET /v1/picker-catalogs/transition`, `client.pickerCatalogs.get("transition")`, MCP `get_picker_catalog` 도구에는 포함되지 않습니다.

## 다중 차원 피커 만들기
다중 차원 피커는 서로 독립적인 여러 필드를 한 번에 설정합니다. 예를 들어 프레이밍은 샷 크기, 앵글, 커버리지, 구도, 시점을 설정합니다. 그 카탈로그의 `dimensions`에는 필드마다 `{ field, label, options }` 항목이 하나씩 있습니다.

```tsx

const framing = getPickerCatalog("framing")!
// framing.dimensions = [
//   { field: "shotSize",    label: "Shot Size",   options: [{ id: "close-up", ... }, ...] },
//   { field: "angle",       label: "Angle",       options: [{ id: "low-angle", ... }, ...] },
//   { field: "coverage",    label: "Coverage",    options: [...] },
//   { field: "composition", label: "Composition", options: [...] },
//   { field: "vantage",     label: "Vantage",     options: [...] },
// ]

function FramingPicker({ value, onChange }: {
value: Record<string, string>
onChange: (v: Record<string, string>) => void
}) {
return framing.dimensions!.map((dim) => (
<section key={dim.field}>
<h4>{dim.label}</h4>
{dim.options.map((o) => (
<button
key={o.id}
aria-pressed={value[dim.field] === o.id}
title={o.description}
onClick={() => onChange({ ...value, [dim.field]: o.id })}
>
{o.label}
</button>
))}
</section>
))
}

// Selection to clause: getParameterPromptHint composes every field that is set
const value = { shotSize: "close-up", angle: "low-angle", composition: "rule-of-thirds" }
const clause = getParameterPromptHint({ type: "framing", data: value })
```

## 선택을 프롬프트로 바꾸기
`getParameterPromptHint({ type, data })`는 단일 선택이든 다중 선택이든 모든 선택을 그 구절로 바꿉니다. Nodaro가 서버에서 실행하는 것과 같은 함수이므로, 내 프롬프트는 편집기의 프롬프트와 같습니다. 다중 차원 피커용 `buildFramingHints(value)`, 선택지 하나용 `getMoodPromptHint(id)`처럼 카탈로그별 빌더도 있습니다.

```ts
// Several pickers, one prompt, one generation
const clauses = [
getParameterPromptHint({ type: "mood",    data: { mood: "serene" } }),
getParameterPromptHint({ type: "lens",    data: { lens: "portrait-85mm" } }),
getParameterPromptHint({ type: "framing", data: { shotSize: "close-up", angle: "eye-level" } }),
]
const prompt = ["a portrait of a woman in a garden", ...clauses].filter(Boolean).join(", ")
const result = await client.nodes.runAndWait("generate-image", { prompt, provider: "nano-banana-2" })
```

피커의 `data`에 `hintMode: "compact"`를 추가하면 전체 구절 대신 짧은 `term`을 받습니다. 이것이 편집기의 **프롬프트 힌트** 토글에 있는 **간략** 모드입니다. [전체 또는 간략](https://nodaro.ai/docs/nodes/creative-controls/mood#full-or-compact)을 참조하세요.

| 함수 | 하는 일 |
| --- | --- |
| `getParameterPromptHint({ type, data })` | 모든 선택을 조합된 구절로 바꿉니다. |
| `build<Name>Hints(value)` | `buildFramingHints`처럼 카탈로그 하나의 구절을 만듭니다. |
| `get<Name>PromptHint(id)` | `getMoodPromptHint`처럼 선택지 하나의 구절을 반환합니다. |
| `PICKER_CATALOGS`, `getPickerCatalog`, `listPickerCatalogs` | 레지스트리입니다. |

## 레이블 번역
카탈로그 레이블은 영어입니다. 12개 로캘의 번역이 `@nodaro/shared`에 들어 있으며, 각 카탈로그의 `catalogId`를 키로 합니다. 로캘은 `en`, `es`, `fr`, `de`, `pt-BR`, `ru`, `hi`, `ja`, `ko`, `zh-CN`, `he`, `ar`입니다. `promptHint`와 `term` 값은 모델이 읽으므로 영어로 유지됩니다.

영어는 설정이 필요 없습니다. 다른 로캘은 시작할 때 번역 번들을 한 번 등록한 다음, 영어를 대체값으로 삼아 레이블을 동기적으로 확인하세요.

```ts

// 1. Once at startup, in a Vite app: wire the lazily loaded locale bundles
registerSidecarLoaders(import.meta.glob("/node_modules/@nodaro/shared/src/i18n/*.*.ts"))

// 2. Before rendering a locale, load that catalog's bundle
await ensureLocaleCatalogLoaded(mood.catalogId, "fr")

// 3. Resolve a label, synchronously; English when a translation is missing or not loaded yet
const label = resolveLabel(mood.catalogId, option.id, option.label, "fr")
```

번들은 필요할 때 불러오므로, `registerSidecarLoaders`는 Vite의 `import.meta.glob`이 반환하는 것 같은 로더 맵을 받습니다. 백엔드와 테스트는 번역을 건너뛰고 영어 `label`을 사용해도 됩니다.

## 그림 표시
내 앱에서도 편집기가 다음 피커에 보여 주는 것과 같은 그림을 보여 줄 수 있습니다.

- [**인물**](https://nodaro.ai/docs/nodes/creative-controls/person), [**스타일링**](https://nodaro.ai/docs/nodes/creative-controls/styling), [**손에 든 소품**](https://nodaro.ai/docs/nodes/creative-controls/held-prop), [**재질**](https://nodaro.ai/docs/nodes/creative-controls/material), [**동물**](https://nodaro.ai/docs/nodes/creative-controls/animal)의 **사진**.
- 음악 및 보이스 피커인 **음악 장르**, **음악 분위기**, **악기 구성**, **보이스 특성**, **말하기 스타일**의 **아트**.
- 룩 피커의 **미리보기 스틸**, Nodaro Cloud 전용. **스타일**, **색감 / 룩**, **시대 / 연대**, **렌즈**, **분위기**, **대기 효과**, **컴포지션 효과**, **카메라 / 필름**, **프레이밍**, **조명**, **카메라 모션**입니다.

**API에서는** 그림이 있는 모든 선택지에 요청한 설치 기준의 절대 URL인 `imageUrl`이 들어 있고, 그림이 없는 선택지에는 없습니다. 인물과 스타일링은 `sections`도 반환합니다. 주제를 순서대로 담은 것으로, 각 주제에 둥근 그림이 있습니다. URL 규칙은 아래 [API로 카탈로그 읽기](#read-the-catalogs-over-the-api)에 이어집니다.

**라이브러리에서는** `@nodaro/prompts`가 같은 데이터로 같은 URL을 만듭니다.

- 선택지에는 `pickerOptionImageUrl(catalog, field, id, { baseUrl })`
- 인물이나 스타일링 주제에는 `pickerSectionImageUrl(topicLabel, { baseUrl })`

`baseUrl`은 파일을 제공하는 Nodaro 설치입니다. 그림은 npm 패키지가 아니라 설치에 속합니다. `lookPreviews: true`는 Nodaro Cloud에 대해서만 전달하세요. 다른 모든 피커는 `label`, `description`, `category`로 직접 시각 요소를 그리세요.

## API로 카탈로그 읽기
두 엔드포인트 모두 공개되어 있고, 토큰이 필요 없으며, 5분 동안 캐시됩니다.

| 메서드 | 경로 | 반환하는 것 |
| --- | --- | --- |
| `GET` | `/v1/picker-catalogs` | 모든 피커의 목록입니다. `nodeType`, `label`, `catalogId`, `kind`, `valueField` 또는 `fields`, `optionCount`, 그리고 그림이 있는 선택지 수인 `imageCount`가 들어 있습니다. |
| `GET` | `/v1/picker-catalogs/:nodeType` | 피커 하나의 카탈로그입니다. 알 수 없는 유형이면 `404 not_found`로 응답합니다. |

`GET /v1/picker-catalogs/:nodeType`은 쿼리 매개변수 세 개를 받습니다. 잘못된 값이면 `400 validation_error`로 응답합니다.

| 매개변수 | 값 | 하는 일 |
| --- | --- | --- |
| `detail` | `compact`(기본값) 또는 `full` | `compact`는 `id`, `label`, `category`, `term`, `icon`, `imageUrl`을 반환합니다. `full`은 각 선택지의 `description`과 `promptHint`를 추가합니다. |
| `category` | 카테고리 ID | 단일 차원 피커를 카테고리 하나로 거릅니다. |
| `field` | 필드 이름 | 다중 차원 피커의 차원 하나, 또는 전환, 캐릭터 효과, 캐릭터 모션의 보조 필드 하나를 반환합니다. |

```bash
curl -s https://app.nodaro.ai/v1/picker-catalogs/mood | jq '.data.options[0]'
```

```json
{ "id": "happy", "label": "Happy", "category": "positive", "term": "happy expression",
"imageUrl": "https://cdn.nodaro.ai/cdn-cgi/image/width=480,format=auto,quality=80/images/710df65a-3c1e-485b-b8b7-29f7b3baf479.png" }
```

### 그림 URL
| 피커 | 그림 |
| --- | --- |
| `person`, `styling`, `held-prop`, `material`, `animal` | 사진, WebP, 너비 최대 480px. |
| `music-genre`, `music-mood`, `instrumentation`, `voice-character`, `voice-delivery` | 3D 이모지 그림(WebP, 128px) 또는 국기(WebP, 너비 120px). |
| `style`, `color-look`, `lens`, `framing`, `lighting`, `mood`, `camera-format`, `camera-motion` 같은 룩 피커 | Nodaro Cloud에서는 Nodaro CDN이 제공하는, 렌더링된 미리보기의 480px 스틸이며, `camera-motion`은 클립의 한 프레임입니다. 셀프 호스팅 설치는 반환하지 않습니다. |

- **호스트.** 설치는 자체 그림을 `/picker-art/` 아래에서 제공하므로, `imageUrl`은 그 공개 주소를 사용합니다. Nodaro Cloud에서는 `https://app.nodaro.ai`, 셀프 호스팅 설치에서는 `PUBLIC_URL`입니다. 각 URL은 받은 그대로 사용하고, 선택지 ID로 URL을 직접 만들지 마세요.
- **캐싱.** 파일 이름에 콘텐츠 해시가 들어 있으므로, 그림이 바뀌면 새 URL이 생깁니다. 파일은 `Cache-Control: public, max-age=31536000, immutable`과 `Access-Control-Allow-Origin: *`로 제공되므로, 어떤 출처에서든 `<img>`, `fetch` 또는 캔버스로 불러올 수 있습니다.
- **주제.** `person`과 `styling`은 `sections`도 반환하며, 각 섹션에는 `label`, 섹션이 묶는 `fields`, 선택 항목인 둥근 `imageUrl`이 있습니다.

```bash
curl -s https://app.nodaro.ai/v1/picker-catalogs/person | jq '.data.sections[0], .data.dimensions[0].options[0]'
```

```json
{ "label": "Identity", "fields": ["type", "age", "ethnicity", "regionalAesthetic"],
"imageUrl": "https://app.nodaro.ai/picker-art/character/sections/identity.2d5ec1a4.webp" }
{ "id": "man", "label": "Man", "term": "man",
"imageUrl": "https://app.nodaro.ai/picker-art/character/person/man.d6bed999.webp" }
```

### 배포 환경의 큐레이션된 카탈로그
`GET /v1/catalogs`는 한 번의 호출로 모든 카탈로그를 하나의 평평한 형태로 반환합니다. 배포 환경은 선택지를 대체하거나, 확장하거나, 금지하는 공급업체 팩으로 카탈로그를 큐레이션할 수 있으며, 이 엔드포인트는 그 큐레이션을 반영합니다. 배포 환경이 팩을 등록했으면 응답에 `curated: true`와 `data`의 카탈로그가 들어 있습니다. 등록한 팩이 없으면 `curated: false`가 들어 있으며, 번들된 카탈로그는 피커별로 `/v1/picker-catalogs/:nodeType`에서 읽습니다. SDK 메서드는 `client.catalogs.list()`입니다.

### 설명으로 피커 채우기
`POST /v1/text-to-picker`는 장면이나 샷의 자유 텍스트 설명으로 피커 값을 고릅니다. 이것이 AI 채우기 기능입니다. 토큰이 필요하며 LLM 호출로 청구됩니다. 피커 유형, 차원, 선택된 ID 순으로 키가 지정된 `pickerJson`과, 어떤 카탈로그 선택지로도 표현되지 않는 설명 속 세부 사항인 `gaps`를 반환합니다. SDK 메서드는 `client.pickerCatalogs.analyzeText()`이고, CLI 명령은 `nodaro pickers analyze`입니다.

## 캐릭터 모션 메타데이터
캐릭터 모션 카탈로그의 선택지는 두 상세 수준 모두에서 `motion` 메타데이터를 가질 수 있습니다. 메타데이터는 동작을 설명합니다.

- 동작의 요구 사항, 시작 포즈와 끝 포즈, 동작 후의 가시성과 손 상태.
- 손이 비어 있어야 하는지, 동작의 종류, 고정 속도, 짝이 되는 동작.
- 검색 별칭, 그리고 ID가 폐기되었는지와 어떤 ID가 대신하는지.

필드가 없으면 알 수 없다는 뜻입니다.

저장된 워크플로를 불러올 때는 폐기된 ID가 계속 작동하게 하고, 새로 고르는 선택지에서는 숨기세요. 타입은 `@nodaro/prompts`가 내보내는 `CharacterMotionMetadata`입니다.

## 자주 묻는 질문

### 피커 카탈로그를 사용하려면 Nodaro API가 필요한가요?

아니요. 카탈로그와, 선택을 프롬프트 구절로 바꾸는 함수는 @nodaro/prompts npm 패키지에 들어 있으므로, 내 앱이 오프라인에서 피커를 그리고 프롬프트를 만들 수 있습니다. API 엔드포인트는 패키지를 번들링할 수 없는 클라이언트를 위해 있습니다.

### 피커 선택을 프롬프트 텍스트로 바꾸려면 어떻게 하나요?

피커의 노드 유형과, 선택한 분위기 ID 같은 데이터를 담아 getParameterPromptHint를 호출하세요. Nodaro도 서버에서 같은 함수를 실행하므로, 내 프롬프트는 편집기가 만드는 프롬프트와 같습니다.

### 피커 레이블은 어떤 언어를 지원하나요?

12개 로캘을 지원합니다. 영어, 스페인어, 프랑스어, 독일어, 브라질 포르투갈어, 러시아어, 힌디어, 일본어, 한국어, 중국어 간체, 히브리어, 아랍어입니다. 프롬프트 텍스트와 용어는 모델이 읽으므로 영어로 유지됩니다.

### 내 셀프 호스팅 설치에서는 왜 룩 피커에 그림이 없나요?

분위기, 조명 같은 룩 피커의 미리보기는 Nodaro CDN에서 제공되며, Nodaro Cloud만 이를 반환합니다. 인물, 스타일링, 손에 든 소품, 재질, 동물의 사진과 음악 및 보이스 아트는 모든 설치에서 제공됩니다.

### 피커 카탈로그는 몇 개인가요?

39개입니다. 분위기, 렌즈 같은 단일 차원 피커가 28개, 프레이밍, 조명, 인물 같은 다중 차원 피커가 11개입니다.
