# 캐릭터 학습

> REST로 캐릭터의 고충실도 모델을 학습시키고, 학습 상태를 폴링하고, 모델을 제거하는 방법과 이미지 생성이 학습된 모델을 쓰는 경우를 설명합니다.

Source: https://nodaro.ai/ko/docs/developers/api/character-training

**캐릭터 학습**은 캐릭터의 이미지로 그 캐릭터 전용 모델을 만들어, [이미지 생성](https://nodaro.ai/docs/nodes/image/generate-image)에서 원본과 가장 닮은 결과를 얻게 해 줍니다. 한 번의 호출로 학습을 시작하고, 끝날 때까지 폴링하고, 더 이상 필요하지 않으면 모델을 제거합니다. 학습이 끝나면 프롬프트가 그 캐릭터를 언급할 때마다 이미지 생성이 학습된 모델을 자동으로 사용합니다.

캐릭터 학습은 Nodaro Cloud에서만 실행되며, 셀프 호스팅 설치에는 해당 라우트가 없어 `404`로 응답합니다. 셀프 호스팅 설치에서는 [캐릭터](https://nodaro.ai/docs/developers/api/characters)에 설명된 대로 승인된 포트레이트와 레퍼런스 이미지로 캐릭터의 일관성을 유지하세요. 이 라우트들은 Bearer 토큰을 받으며 내 캐릭터에만 작동합니다. [인증](https://nodaro.ai/docs/developers/api/authentication)을 참조하세요.

SDK는 이 라우트들을 `client.characters.train`, `trainingStatus`, `deleteLora`로 감쌉니다. CLI에서는 `nodaro characters train`, `training`, `delete-lora`를 사용하세요. MCP로 연결된 AI 어시스턴트는 `train_character`로 학습을 시작하고 `get_character`로 진행을 확인합니다. 학습된 모델 제거는 MCP 도구로 제공되지 않습니다.

OAuth 앱 토큰으로 학습을 시작하거나 학습된 모델을 제거하려면, 캐릭터를 만들거나 삭제할 때와 같은 범위인 `assets:write`가 필요합니다. 이 범위가 없으면 아무것도 예약되기 전에 `403 insufficient_scope`로 응답합니다. 상태 읽기에는 범위가 필요 없습니다. 세션이나 개인 API 토큰에는 범위가 필요 없습니다.

## 엔드포인트
| 메서드 | 경로 | 기능 |
| --- | --- | --- |
| `POST` | `/v1/characters/:id/train` | 학습을 시작합니다. 1,650크레딧을 예약합니다. |
| `GET` | `/v1/characters/:id/training` | 학습 상태를 가져옵니다. |
| `DELETE` | `/v1/characters/:id/lora` | 진행 중인 학습을 취소하거나 학습된 모델을 제거합니다. |

## 학습 전 준비
캐릭터에는 **서로 다른 이미지가 4장 이상** 있어야 합니다. 기본적으로 Nodaro는 다음 순서로 캐릭터에서 이미지를 모으고, 중복을 제거한 다음, 최대 20장으로 학습합니다.

1. 승인된 포트레이트.
2. 레퍼런스 사진.
3. 표정, 포즈, 머리 앵글, 전신 앵글.
4. 조명 변형.

일부 이미지만으로 학습하려면 본문에 `{ "selectedImageUrls": [...] }`를 보내세요. 캐릭터 자신의 이미지 URL을 최대 200개까지 넣을 수 있습니다. 그러면 학습은 그 이미지만 사용하며, 최소 4장이 남아야 합니다. 목록을 생략하거나 비워 두면 해당하는 모든 이미지를 사용합니다.

캐릭터 시트는 시트 속 뷰들이 앵글을 반복하므로 포함되지 않습니다. 이미지가 4장보다 적으면 먼저 `POST /v1/generate-character-asset`으로 앵글과 표정을 몇 가지 생성하세요. [캐릭터](https://nodaro.ai/docs/developers/api/characters#generate-an-expression-angle-pose-or-lighting-variant)를 참조하세요.

## 학습 시작
`POST /v1/characters/:id/train`은 1,650크레딧을 예약하고 학습을 시작합니다. 학습의 작업 ID, 학습 ID, 캐릭터의 트리거 단어와 함께 `202`로 응답합니다. 트리거 단어를 직접 입력할 필요는 없습니다. Nodaro가 프롬프트에 자동으로 추가합니다.

**curl**

```bash
curl -X POST https://app.nodaro.ai/v1/characters/3f6c2a9e-8d41-4b7a-9c35-1e2f7a6b0d94/train \
  -H "Authorization: Bearer $NODARO_API_KEY"

# Or train on a subset of the character's images
curl -X POST https://app.nodaro.ai/v1/characters/3f6c2a9e-8d41-4b7a-9c35-1e2f7a6b0d94/train \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"selectedImageUrls": ["https://cdn.nodaro.ai/characters/kira-portrait.png", "https://cdn.nodaro.ai/characters/kira-smile.png", "https://cdn.nodaro.ai/characters/kira-left.png", "https://cdn.nodaro.ai/characters/kira-right.png"]}'
```

**TypeScript SDK**

```ts

const client = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
})

const training = await client.characters.train(characterId)
// Or a subset: client.characters.train(characterId, { selectedImageUrls: [...] })
```

```json
{
"jobId": "7a9c1e3b-5d2f-4b8a-9c6e-1f3d5b7a9c2e",
"trainingId": "q4m8x2k6p1",
"triggerWord": "TOK_kira_a1b2c3"
}
```

학습의 작업인 `jobId`는 학습과 함께 `completed`, `failed`, `cancelled` 중 하나로 끝나므로, `GET /v1/jobs/:id/status`로도 진행을 확인할 수 있습니다.

두 번 클릭해도 안전합니다. 학습이 대기 중이거나 실행 중일 때 다시 시작하면 `409 already_training_or_not_found`로 응답하며 아무것도 예약하지 않습니다. 토큰마다 분당 3회까지 학습을 시작할 수 있습니다.

## 학습 진행 확인
`GET /v1/characters/:id/training`은 학습 상태를 반환합니다. 학습에는 약 15분이 걸립니다. 10초 정도마다 폴링하세요. SDK에는 대기 도우미가 없습니다.

**curl**

```bash
curl https://app.nodaro.ai/v1/characters/3f6c2a9e-8d41-4b7a-9c35-1e2f7a6b0d94/training \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

**TypeScript SDK**

```ts
let training = await client.characters.trainingStatus(characterId)
while (training.status === 'queued' || training.status === 'training') {
await new Promise((resolve) => setTimeout(resolve, 10_000))
training = await client.characters.trainingStatus(characterId)
}
if (training.status !== 'succeeded') console.warn(training.status, training.error)
```

```json
{
"status": "succeeded",
"trainingId": "q4m8x2k6p1",
"error": null,
"trainedAt": "2026-09-20T11:42:08Z",
"version": "b81f4c0e9d27",
"triggerWord": "TOK_kira_a1b2c3",
"imageCount": 12
}
```

| 필드 | 담긴 내용 |
| --- | --- |
| `status` | `untrained`, `queued`, `training`, `succeeded`, `failed`, `cancelled` 중 하나. `untrained`는 학습한 적이 없거나, 모델이 제거되었거나, 학습을 시작할 수 없었다는 뜻입니다. |
| `trainingId` | 현재 또는 마지막 학습, 또는 `null`. |
| `error` | 학습이 실패한 이유, 또는 `null`. |
| `trainedAt` | 마지막으로 성공한 학습이 끝난 시각, 또는 `null`. |
| `version` | 마지막으로 성공한 학습의 모델 버전, 또는 `null`. |
| `triggerWord` | 학습된 모델에서 캐릭터를 불러내는 단어, 또는 `null`. |
| `imageCount` | 가장 최근 학습에 사용된 이미지 수, 또는 `null`. |

생성은 **`status`가 `succeeded`인 동안에만** 학습된 모델을 사용합니다. `trainedAt`과 `version`은 다시 학습하는 중이거나, 학습이 실패 또는 취소되었거나, 시작할 수 없었던 경우에도 값이 남아 있지만, 이런 상태에서는 모델이 사용되지 않습니다. 내 캐릭터가 아니거나 보관된 캐릭터는 `404 not_found`로 응답합니다.

## 학습된 모델로 이미지 생성
상태가 `succeeded`가 되면, `@`로 학습된 캐릭터를 정확히 하나만 언급하는 프롬프트를 쓰고 그 캐릭터를 노드에 연결한 상태로 [이미지 생성](https://nodaro.ai/docs/nodes/image/generate-image)을 실행하세요. 그러면 Nodaro는 선택한 모델과 레퍼런스 이미지 대신 학습된 모델을 사용합니다.

- **자동.** 트리거 단어가 프롬프트 맨 앞에 자동으로 들어가고, `@` 언급은 텍스트에서 제거됩니다.
- **이미지당 22크레딧.** 학습된 모델의 가격이 선택한 모델의 가격을 대신합니다.
- **한 번에 학습된 캐릭터 하나.** 프롬프트가 학습된 캐릭터를 둘 이상 언급하면, 이미지 생성은 레퍼런스 이미지와 함께 선택한 모델을 사용하는 방식으로 돌아갑니다.
- **이미지 생성 전용.** [이미지 수정](https://nodaro.ai/docs/nodes/image/modify-image)이나 동영상 노드 같은 다른 노드는 계속 캐릭터의 레퍼런스 이미지를 사용합니다.

이미지 생성 요청은 [단일 노드 실행](https://nodaro.ai/docs/developers/api/nodes)을, 편집기에서 쓰는 같은 기능은 [캐릭터 학습](https://nodaro.ai/docs/guides/character-training)을 참조하세요.

## 다시 학습 또는 모델 제거
- **다시 학습.** 학습이 성공, 실패 또는 취소된 뒤에는 언제든 다시 학습을 시작할 수 있습니다. 매번 1,650크레딧이 들며, 새 모델이 이전 모델을 대체합니다.
- **제거.** `DELETE /v1/characters/:id/lora`는 진행 중인 학습을 취소하고 크레딧을 환불하며, 학습된 모델을 삭제하고, 캐릭터의 학습 필드를 지웁니다. 그러면 상태는 `untrained`가 됩니다. `{ ok: true }`를 반환하며, 되돌릴 수 없습니다. 이후 생성은 다시 레퍼런스 이미지를 사용합니다.
- **캐릭터 보관.** 캐릭터를 보관해도 진행 중인 학습이 취소되고, 크레딧이 환불되며, 학습된 모델이 삭제됩니다.

```bash
curl -X DELETE https://app.nodaro.ai/v1/characters/3f6c2a9e-8d41-4b7a-9c35-1e2f7a6b0d94/lora \
  -H "Authorization: Bearer $NODARO_API_KEY"
```

## 크레딧
| 항목 | 크레딧 |
| --- | --- |
| 학습 | 1,650크레딧. 학습이 실패하거나 취소되면 환불됩니다. |
| 다시 학습 | 매번 1,650크레딧. |
| 학습된 모델로 만든 이미지 | 선택한 모델의 가격 대신 이미지당 22크레딧. |

잔액과 거래 내역은 [크레딧](https://nodaro.ai/docs/developers/api/credits)을 참조하세요.

## 오류
| 상태 | 코드 | 의미 |
| --- | --- | --- |
| `400` | `insufficient_training_images` | 캐릭터의 서로 다른 이미지가 4장보다 적습니다. 응답의 `count`와 `message`가 현재 이미지 수를 알려 줍니다. 앵글이나 표정을 추가한 다음 다시 시도하세요. |
| `400` | — | 형식이 잘못된 id 또는 `selectedImageUrls`. 본문은 `{ "message", "issues": [...] }`입니다. |
| `401` | `unauthorized` | 토큰이 없거나, 유효하지 않거나, 해지되었습니다. |
| `402` | `insufficient_credits` | 계정 잔액으로 1,650크레딧을 감당할 수 없습니다. 응답에 `required`와 `balance`가 담깁니다. |
| `403` | `insufficient_scope` | `assets:write`가 없는 OAuth 앱 토큰입니다. |
| `404` | `not_found` | 내 캐릭터 중에 해당 캐릭터가 없거나, 캐릭터가 보관되었거나, 인스턴스가 Nodaro Cloud가 아닙니다. |
| `409` | `already_training_or_not_found` | 이 캐릭터의 학습이 이미 대기 중이거나 실행 중이거나, 내 캐릭터가 아니거나 보관된 캐릭터입니다. |
| `422` | `job_blocked` | 배포의 작업 정책이 학습을 거부했습니다. |
| `429` | — | 이 토큰으로 1분 안에 학습을 3회 넘게 시작했습니다. |
| `500` | `claim_failed` | 학습을 점유할 수 없었습니다. 아무것도 시작되지 않았으니 다시 시도하세요. |
| `502` | `training_dispatch_failed` | 학습을 시작할 수 없었습니다. 크레딧은 환불되며, 상태는 `untrained`가 됩니다. |
| `503` | `public_url_not_configured`, `price_not_configured` | 이 인스턴스에는 학습이 구성되어 있지 않습니다. |
| `503` | `dedup_race_winner_unresolvable` | 같은 요청이 이미 처리 중이었고 그 결과를 읽을 수 없었습니다. 이 요청은 아무것도 예약하지 않았으니, 다시 시도하기 전에 상태를 읽으세요. |

이 라우트들이 직접 쓰는 거부 응답은 이전 본문 형식을 사용합니다. 코드는 그냥 문자열인 `{ "error": "<code>" }`이며, 최상위 `message`가 함께 올 때도 있습니다. 범위, 크레딧, 요청 한도, 작업 정책 거부는 일반적인 `{ "error": { "code", "message" } }` 형식을 사용합니다. SDK는 두 형식을 모두 `NodaroError.code`와 `.message`로 읽습니다.

## 자주 묻는 질문

### 캐릭터 학습에는 사진이 몇 장 필요한가요?

승인된 포트레이트, 레퍼런스 사진, 표정, 포즈, 앵글, 조명 변형을 통틀어 캐릭터의 서로 다른 이미지가 4장 이상 필요합니다. Nodaro는 그중 최대 20장으로 학습합니다. 캐릭터 시트는 포함되지 않습니다.

### 캐릭터 학습에는 크레딧이 얼마나 드나요?

학습 한 번에 1,650크레딧이 들며, 다시 학습할 때마다 같은 크레딧이 듭니다. 학습이 실패하거나 취소되면 크레딧이 환불됩니다. 이후 학습된 모델로 생성하는 이미지는 장당 22크레딧입니다.

### 학습은 얼마나 걸리나요?

약 15분입니다. 상태가 succeeded, failed 또는 cancelled가 될 때까지 몇 초마다 GET /v1/characters/:id/training을 폴링하세요.

### 이미지 생성은 언제 학습된 모델을 사용하나요?

프롬프트가 @로 학습된 캐릭터를 정확히 하나만 언급하고, 그 캐릭터가 이미지 생성 노드에 연결되어 있을 때입니다. 학습된 캐릭터가 둘 이상이면 노드는 그 대신 선택한 모델과 레퍼런스 이미지를 사용합니다.

### 셀프 호스팅 설치에서도 캐릭터를 학습시킬 수 있나요?

아니요. 캐릭터 학습은 Nodaro Cloud에서만 실행되며, 셀프 호스팅 설치에는 해당 라우트가 없습니다. 셀프 호스팅 설치에서는 승인된 포트레이트와 레퍼런스 이미지로 캐릭터의 일관성을 유지하세요.
