> ## Documentation Index
> Fetch the complete documentation index at: https://make.edicus.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# VDP 주문

> 가변 데이터를 실어 잠정 주문하기

이름표나 명함처럼 같은 디자인에 이름만 바꿔 여러 벌을 만드는 주문은 가변 데이터(VDP)를 함께 보냅니다. 잠정 주문 대신 `POST /v2/projects/:project_id/order/tentative_with_vdp`를 쓰고, 그 밖의 흐름(확정, 취소)은 [주문](/docs/server/orders)과 같습니다.

## 배우는 것

* 가변 데이터를 JSON과 파일 두 방법으로 보내는 법
* 템플릿 방식에 맞는 dataset 형식
* VDP 주문을 취소할 때 주의할 점

## 데이터가 작을 때

JSON으로 보냅니다.

```ts theme={null}
const r = await callEdicus<TentativeOrderResult>(
  `/v2/projects/${project_id}/order/tentative_with_vdp`,
  {
    method: 'POST',
    uid,
    body: {
      order_count: 100,
      total_price: 150000,
      partner_order_id: 'ORD-2026-0002',
      vdp_dataset: dataset,
    },
  },
)
// r.order_id를 저장해 둡니다
```

`callEdicus`와 `TentativeOrderResult`는 [Server API 호출하기](/docs/server/client)에서 만든 래퍼와 타입입니다.

## 대량 데이터일 때

파일로 보냅니다. 이때는 `content-type`을 직접 지정하지 않고 `FormData`를 그대로 넘겨 런타임이 boundary를 붙이게 합니다. 위 래퍼는 JSON 전용이라 `fetch`를 직접 씁니다.

```ts theme={null}
const form = new FormData()
form.set('order_count', '100')
form.set('total_price', '150000')
form.set('partner_order_id', 'ORD-2026-0003')
form.set('vdp_dataset_file', new Blob([JSON.stringify(dataset)], { type: 'application/json' }))

const res = await fetch(`https://api.edicus.io/v2/projects/${project_id}/order/tentative_with_vdp`, {
  method: 'POST',
  headers: {
    'edicus-api-key': process.env.EDICUS_API_KEY!,
    'edicus-uid': uid,
  },
  body: form,
})
if (!res.ok) {
  const { error } = await res.json()
  throw new Error(`VDP 잠정 주문 실패: ${error.code} (${error.request_id})`)
}
const { order_id } = (await res.json()) as TentativeOrderResult
```

| 보내는 방법 | Content-Type | 언제 |
| - | - | - |
| `vdp_dataset` (객체) | `application/json` | 데이터가 작을 때 |
| `vdp_dataset_file` (File 또는 Blob) | `multipart/form-data` | 대량 데이터일 때 |

multipart로 보낼 때는 `vdp_dataset_file`을 제외한 모든 필드가 문자열로 전달되고, 서버가 숫자와 불리언으로 바꿔 검증합니다. `vdp_dataset_file`이 있으면 `vdp_dataset`은 무시됩니다. 요청 body는 50MB를 넘을 수 없고, 넘으면 `413 payload_too_large`입니다.

## dataset 형식

어노테이션 방식으로 만든 템플릿:

```json theme={null}
{
  "rows": [
    { "cols": [
        { "id": "name", "value": { "text": "홍길동" } },
        { "id": "title-role", "value": { "text": "대표" } }
    ] }
  ]
}
```

data merge 방식으로 만든 템플릿은 `segment: true`를 함께 줍니다.

```json theme={null}
{
  "rows": [
    { "cols": [
        { "id": "name", "segment": true, "value": { "text": "홍길동" }, "shrink": true }
    ] }
  ]
}
```

`vdp_dataset`에는 dataset 객체를 그대로 넣습니다. 서버가 직렬화해서 저장하므로 미리 `JSON.stringify`하면 문자열이 한 번 더 인코딩됩니다. 파일로 보낼 때만 `vdp_dataset_file`에 JSON 문자열을 담습니다.

`id`는 템플릿의 변수 이름입니다. 변수를 만드는 방법은 [변수와 데이터 병합](/docs/templates/variables)에 있습니다.

<Warning>
  VDP를 포함한 주문을 취소하면 `vdp_dataset`은 삭제되고 복구되지 않습니다.
</Warning>

## 됐는지 확인

<Check>
  응답으로 `order_id`와 `status: 'ordering'`을 받으면 잠정 주문이 된 것입니다. 이어서 [확정 주문](/docs/server/orders)을 하면 렌더링 대상이 됩니다. 결과 파일에 행마다 값이 들어갔는지는 [테스트 주문과 렌더링](/docs/templates/test-render)으로 확인합니다.
</Check>

## 다음 단계

<Columns cols={2}>
  <Card title="주문" icon="cart-shopping" href="/docs/server/orders">
    확정 주문과 취소, 주문 body의 다른 필드를 봅니다.
  </Card>

  <Card title="TnView와 VDP" icon="table" href="/docs/sdk/tnview">
    가변 데이터를 넣은 결과를 주문 전에 미리 봅니다.
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.