# 객체 태그 API (베타)

객체 태그는 사용자에 대한 구조화된 데이터를 하나의 태그에 저장합니다. `Object` 유형의 태그는 이름이 지정된 필드를 가진 객체 하나를 저장합니다. 예를 들어 `amount`와 `status`가 있는 마지막 `order`가 이에 해당합니다. `ObjectList` 유형의 태그는 이러한 객체의 배열을 저장합니다. 예를 들어 모든 `orders`가 이에 해당합니다. 필드는 한 번만 정의하면 되며, 태그 API를 통해 값을 쓰고 읽고, 필드 값으로 세그먼트를 만들 수 있습니다.

<Aside type="caution" title="베타">
객체 태그는 베타 상태이며 요청 시 사용할 수 있습니다. 계정에서 활성화하려면 [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new)에 문의하세요. 베타 기간 동안 객체 태그는 API를 통해서만 작동합니다.

- Control Panel에는 객체 태그 화면이 없습니다.
- 객체 태그의 필드는 메시지 콘텐츠에 삽입할 수 없습니다.
- Customer Journey는 객체 태그를 지원하지 않습니다.
</Aside>

## 객체 태그의 작동 방식

객체 태그에는 스키마와 값이 있습니다. 스키마는 태그 이름과 필드 목록이며, 각 필드는 고유한 스칼라 유형을 가집니다. 값은 필드에 사용자별로 따로 저장됩니다.

- **필드.** 각 필드는 `<object>.<field>`라는 이름의 태그입니다(예: `order.amount`). 필드로 세그먼트를 만들 수 있으며, 이 이름으로 `Object` 태그의 필드를 쓸 수 있습니다.
- **`Object` 값.** 사용자당 객체 하나입니다(예: `"order": {"amount": 120, "status": "paid"}`).
- **`ObjectList` 값.** 동일한 필드를 가진 객체의 배열입니다(예: `"orders": [{"amount": 120, "status": "paid"}, {"amount": 50}]`).

객체 태그는 항상 User-specific 태그입니다. 즉, 값은 단일 디바이스가 아닌 사용자에게 속하며 해당 사용자의 모든 디바이스에 복사됩니다. 일반 태그에 대해서는 [태그 API](/ko/developer/api-reference/tags/)를 참조하세요.

## 스키마 규칙

객체 태그를 생성하거나 필드를 추가할 때 Pushwoosh는 다음 규칙을 확인합니다.

- 객체 이름과 필드 이름에는 라틴 문자, 숫자, `_`만 사용할 수 있습니다.
- 전체 필드 이름 `<object>.<field>`는 64자 이하입니다.
- 객체 태그에는 1~20개의 필드가 있습니다. 필드 이름은 대소문자를 구분하지 않으므로 `Amount`와 `amount`는 같은 필드입니다.
- 필드 유형은 `INTEGER`, `STRING`, `LIST`, `DATE`, `BOOLEAN`, `PRICE`, `VERSION` 중 하나입니다. 시간대 필드와 중첩 객체는 지원되지 않습니다.
- 필드에는 다른 태그가 이미 사용 중인 이름을 지정할 수 없습니다. `order.note` 태그가 있으면 `order`에 `note` 필드를 추가할 때 `tag order.note already exists`로 실패합니다.
- 객체 태그의 마지막 필드는 삭제할 수 없습니다. 대신 객체 태그를 삭제하세요.
- 사용 중인 필드나 객체 태그는 삭제할 수 없습니다. 오류에는 사용 위치가 나열됩니다(예: `field is in use: segment Big buyers, in-app Spring sale`). 태그를 참조하는 세그먼트, 인앱, 실행 중인 Customer Journey 캠페인, 프리셋, 이메일 템플릿 및 기타 항목이 사용으로 간주됩니다.

## 스키마 관리

아래 REST 메서드로 객체 태그를 생성하고 변경합니다. `/json/1.3/addTag`는 필드를 정의할 수 없으므로 일반 태그에만 사용하세요.

### 기본 URL

```
https://rpc-api.svc-nue.pushwoosh.com
```

### 인증

모든 요청에는 [서버 API 토큰](/ko/developer/api-reference/api-access-token/#server-api-token)이 포함된 `Authorization` 헤더가 있어야 합니다. 디바이스 API 토큰을 사용하면 HTTP 403 `a device API token cannot call this method, use an account API token`이 반환됩니다.

```
Authorization: Api YOUR_API_TOKEN
```

### 엔드포인트

다음 메서드로 객체 태그의 스키마를 관리합니다.

| 메서드 | 경로 | 설명 |
| :---- | :---- | :---- |
| `POST` | `/api/tags` | 필드와 함께 객체 태그 생성 |
| `GET` | `/api/tags/{name}` | 필드와 함께 객체 태그 가져오기 |
| `GET` | `/api/tags` | 태그 목록 조회. 객체 태그는 `fields`와 함께 반환되며, 필드는 별도의 태그로 나열되지 않음 |
| `POST` | `/api/tags/{name}/fields` | 필드 추가 |
| `DELETE` | `/api/tags/{name}/fields/{field}` | 필드 삭제 |
| `DELETE` | `/api/tags/{name}` | 모든 필드와 함께 객체 태그 삭제 |

`POST /api/tags`와 두 필드 메서드는 변경 후의 객체 태그를 `{"tag": {...}}`로 감싸서 반환합니다. `DELETE /api/tags/{name}`은 `{}`를 반환합니다.

계정에서 객체 태그가 활성화되지 않은 경우, 객체 유형으로 호출한 `POST /api/tags`와 두 필드 메서드는 HTTP 403 `object tags are not enabled for the account`로 실패합니다. `GET /api/tags/{name}`과 `DELETE /api/tags/{name}`은 HTTP 404 `tag not found`를 반환하며, `GET /api/tags`는 객체 태그를 나열하지 않습니다. 객체 태그가 활성화되지 않으면 객체 태그를 생성할 수 없으므로 값을 쓸 대상도 없습니다.

<span id="create-an-object-tag" />

### 객체 태그 생성

`POST` `/api/tags`

##### 요청 본문

| 이름 | 유형 | 설명 |
| :---- | :---- | :---- |
| name\* | string | 객체 태그의 이름입니다. |
| type\* | string | 객체 하나는 `OBJECT`, 객체 목록은 `OBJECT_LIST`입니다. |
| fields\* | array | 객체의 필드이며, 1~20개입니다. |
| fields[].name\* | string | 필드 이름입니다. 필드는 `<name>.<fields[].name>` 태그가 됩니다. |
| fields[].type\* | string | 필드 유형: `INTEGER`, `STRING`, `LIST`, `DATE`, `BOOLEAN`, `PRICE` 또는 `VERSION`. |

```json title="예시"
{
  "name": "orders",
  "type": "OBJECT_LIST",
  "fields": [
    { "name": "amount", "type": "INTEGER" },
    { "name": "status", "type": "STRING" }
  ]
}
```

```json title="응답"
{
  "tag": {
    "name": "orders",
    "type": "OBJECT_LIST",
    "system": false,
    "user_specific": true,
    "has_right_ace_modify": true,
    "fields": [
      { "name": "amount", "type": "INTEGER" },
      { "name": "status", "type": "STRING" }
    ]
  }
}
```

태그는 `user_specific` 값과 관계없이 User-specific 태그로 생성됩니다. `GET /api/tags/{name}`은 `tag` 래퍼 없이 같은 객체를 반환합니다. 필드는 이름순으로 정렬됩니다.

### 필드 추가

`POST` `/api/tags/{name}/fields`

```json title="예시: POST /api/tags/orders/fields"
{
  "field": { "name": "note", "type": "STRING" }
}
```

새 필드는 전체 태그 값의 일부로 바로 쓸 수 있습니다. 객체 태그에 이미 필드가 20개 있거나 전체 필드 이름과 같은 태그가 있으면 필드 추가가 실패합니다. [오류](#errors)를 참조하세요.

### 필드 삭제

`DELETE` `/api/tags/{name}/fields/{field}`

예를 들어 `DELETE /api/tags/orders/fields/note`는 `orders.note` 필드를 삭제합니다. 마지막 필드나 사용 중인 필드는 삭제할 수 없습니다. [오류](#errors)를 참조하세요.

<span id="errors" />

### 오류

오류는 `{"code": <n>, "message": "...", "details": [...]}` 형식으로 반환됩니다. 객체 태그를 생성할 때 400 메시지는 `validate: `로 시작합니다.

| HTTP 상태 | 메시지 | 원인 |
| :---- | :---- | :---- |
| 400 | `object tag and field names must contain only letters, digits and underscores` | 이름에 다른 문자가 포함되어 있습니다. |
| 400 | `an object tag needs 1 to 20 fields` | 생성 시 필드가 없거나 20개를 초과합니다. |
| 400 | `an object tag has at most 20 fields` | 객체 태그에 이미 필드가 20개 있습니다. |
| 400 | `field <name> is given twice` | 대소문자를 무시하면 두 필드의 이름이 같습니다. |
| 400 | `<object>.<field>: object and field names together cannot be longer than 64 characters` | 전체 필드 이름이 너무 깁니다. |
| 400 | `<object>.<field>: field type must be INTEGER, STRING, LIST, DATE, BOOLEAN, PRICE or VERSION` | 지원되지 않는 필드 유형입니다. |
| 400 | `an object tag needs at least one field; delete the object tag instead` | 마지막 필드를 삭제하려고 했습니다. |
| 400 | `field is in use: ...`, `tag is in use: ...` | 필드나 객체 태그가 세그먼트, 인앱, Customer Journey 캠페인 또는 기타 항목에서 사용 중입니다. |
| 403 | `object tags are not enabled for the account` | 계정에서 객체 태그가 활성화되지 않았습니다. |
| 403 | `a device API token cannot call this method, use an account API token` | 요청에 디바이스 API 토큰을 사용했습니다. |
| 404 | `tag not found` | `GET` 또는 `DELETE /api/tags/{name}`: 해당 태그가 없거나, 이름이 필드이거나, 객체 태그가 활성화되지 않았습니다. |
| 404 | `object tag not found`, `field not found` | 필드 메서드: 해당 객체 태그나 필드가 없습니다. |
| 409 | `tag already exists` | 객체 태그 이름과 같은 태그가 있습니다. |
| 409 | `tag <object>.<field> already exists` | 필드의 전체 이름과 같은 태그가 있습니다. |

## 값 쓰기

[`/setTags`](/ko/developer/api-reference/device-api/#settags)와 [`/bulkSetTags`](/ko/developer/api-reference/audience-api/#bulksettags)로 객체 태그 값을 씁니다. 이 메서드들은 자체 인증 방식을 유지합니다. `/setTags`는 `Authorization: Token` 헤더에 디바이스 API 토큰을 받고, `/bulkSetTags`는 `auth`에 API 액세스 토큰을 받습니다. `Object` 값은 JSON 객체로, `ObjectList` 값은 객체 배열로 보냅니다.

```json title="예시: /json/1.3/setTags"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "userId": "user_42",
    "tags": {
      "order": { "amount": 120, "status": "paid" },
      "orders": [
        { "amount": 120, "status": "paid" },
        { "amount": 50 }
      ]
    }
  }
}
```

HWID로 값을 설정하면 해당 디바이스의 사용자에게 저장됩니다.

각 필드는 해당 유형 형식의 값을 받습니다.

| 필드 유형 | 값 예시 |
| :---- | :---- |
| `INTEGER` | `120` |
| `STRING` | `"paid"` |
| `LIST` | `["gift", "express"]` |
| `DATE` | `"2026-10-02 22:11"`(UTC), `"2026-10-02"` 또는 초 단위 Unix 타임스탬프 |
| `BOOLEAN` | `true` 또는 `false` |
| `PRICE` | `19.95` |
| `VERSION` | `"1.0.0"` |

### 쓰기는 전체 값을 대체합니다

쓸 때마다 객체 태그에 저장된 값이 대체됩니다.

- 새 값에 없는 필드는 삭제됩니다. `null`로 보낸 필드도 삭제됩니다.
- `null`, `[]` 또는 `""`는 전체 값을 삭제합니다.
- `{}`는 `Object` 태그의 모든 필드를 삭제합니다.
- 객체의 필드가 아닌 키는 건너뜁니다.
- 필드 값이 필드 유형과 일치하지 않으면 태그의 전체 값이 거부되고 저장된 값은 그대로 유지됩니다.

`/setTags`는 거부된 값을 응답에서 알려주지 않습니다. [`/getTags`](#read-values)로 결과를 확인하거나, 태그별 결과를 알려주는 `/bulkSetTags`를 사용하세요.

### Object의 필드 하나 쓰기

`Object` 태그의 필드 하나를 전체 이름으로 씁니다. `increment`와 [`/setTags`](/ko/developer/api-reference/device-api/#settags)의 다른 연산은 필드에서도 작동합니다.

```json
"tags": {
  "order.status": "shipped",
  "order.amount": { "operation": "increment", "value": 5 }
}
```

`ObjectList` 태그의 필드는 어떤 요소를 변경할지 명확하지 않으므로 단독으로 쓸 수 없습니다. `/setTags`는 이러한 값을 무시합니다. 대신 전체 목록을 보내세요.

### 객체 태그는 자동으로 생성되지 않습니다

존재하지 않는 태그에 대한 객체 또는 객체 배열 값은 건너뜁니다. 태그 자동 생성은 객체 태그를 생성하지 않으므로 먼저 [`POST /api/tags`](#create-an-object-tag)로 스키마를 생성하세요.

### bulkSetTags 결과

`detailed=true`를 사용하면 [`/bulkSetTags` 상태](/ko/developer/api-reference/audience-api/#bulksettags-status)는 `ok`, `tag not found`, `wrong value. expect :<type>` 외에 객체 태그에 대해 다음 결과 중 하나를 반환합니다.

| 결과 | 원인 |
| :---- | :---- |
| `object tags are stored on the user only` | 객체 태그가 User-specific 태그가 아닙니다. |
| `cannot load the fields of the object` | Pushwoosh가 객체 태그의 필드를 읽을 수 없습니다. |
| `wrong value. expect an object (a map) or, for a list of objects, an array of maps` | 값이 객체 태그에 맞지 않거나 필드 값이 해당 유형과 일치하지 않습니다. |
| `a field of a list of objects cannot be set on its own, set the whole list` | `ObjectList` 태그의 필드에 값을 설정했습니다. |

<span id="read-values" />

## 값 읽기

[`/getTags`](/ko/developer/api-reference/device-api/#gettags)는 `Object` 값을 JSON 객체로, `ObjectList` 값을 객체 배열로 반환합니다.

```json title="응답"
{
  "status_code": 200,
  "status_message": "OK",
  "response": {
    "result": {
      "order": { "amount": 120, "status": "paid" },
      "orders": [
        { "amount": 120, "status": "paid" },
        { "amount": 50 }
      ]
    }
  }
}
```

필드 값은 `/getTags`의 일반 태그 값과 같은 방식으로 반환됩니다. Date는 초 단위 Unix 타임스탬프로, Price는 `"120.00"`과 같은 문자열로, Boolean은 `1` 또는 `0`으로 반환됩니다.

<span id="segment-by-fields" />

## 필드로 세그먼트 만들기

세그먼트 조건은 객체 태그 자체가 아니라 필드에 설정합니다. [세분화 언어](/ko/developer/api-reference/segmentation-filters-api/segmentation-language/)에서 필드는 이름에 점이 포함된 태그이며, 자체 유형의 연산자를 사용합니다.

```
AT("XXXXX-XXXXX", "order.amount", gte, 100)
```

객체 태그 자체에 대한 조건은 `object tag has no value of its own, use one of its fields`로 실패합니다.

### ObjectList에 대한 조건

`ObjectList` 태그의 필드에 대한 조건은 목록의 요소 중 하나 이상이 조건에 맞으면 사용자와 일치합니다. AND로 연결된 같은 목록의 필드 조건은 같은 요소와 일치해야 합니다.

```
AT("XXXXX-XXXXX", "orders.amount", gte, 100) * AT("XXXXX-XXXXX", "orders.status", eq, "paid")
```

이 세그먼트에는 100 이상이면서 결제된 주문이 하나 이상 있는 사용자가 포함됩니다. 결제되지 않은 큰 주문 하나와 결제된 작은 주문 하나가 있는 사용자는 일치하지 않습니다. OR로 연결된 조건은 같은 요소와 일치할 필요가 없습니다. 서로 다른 두 목록에 대한 조건은 각 목록에서 따로 확인됩니다.

일부 연산자는 `ObjectList` 태그의 필드에서 다르게 작동합니다.

- **`notset`.** 목록의 어떤 요소도 이 필드에 값이 없습니다. 목록이 없는 사용자도 일치합니다.
- **`noteq`.** 하나 이상의 요소가 이 필드에 값이 있고, 그 값이 지정된 값과 다릅니다. 필드가 없는 요소는 고려되지 않습니다. 상태가 `paid`와 `new`인 주문이 있는 사용자는 `noteq "paid"`와 일치합니다.
- **List 필드의 `notin`.** 지정된 값 중 어느 것도 포함하는 요소가 없습니다.
- **다른 유형 필드의 `notin`.** 하나 이상의 요소가 이 필드에 지정된 값에 속하지 않는 값을 가집니다.

`notset`과 List 필드의 `notin`은 AND로 연결된 다른 조건과 같은 요소에 묶이지 않습니다.

## 객체 태그가 포함된 세그먼트 내보내기

[`/exportSegment`](/ko/developer/api-reference/segmentation-filters-api/#exportsegment)는 객체 태그의 필드를 내보냅니다.

- **CSV.** 각 필드는 `<object>.<field>`라는 이름의 별도 열이며, 객체 태그 자체에는 열이 없습니다. ObjectList의 필드는 List 태그와 같은 방식으로 모든 요소의 고유 값을 JSON 배열로 담습니다: `["120","50"]`.
- **`json` 및 `json_each_line`.** `Object` 태그의 필드는 `<object>.<field>`라는 이름의 별도 태그로 제공됩니다. `ObjectList` 태그는 `tags`의 항목 하나로 제공되며, 요소는 `object_list.items`에 있습니다. 각 요소는 짧은 이름으로 필드를 나열하고, 각 필드는 유형 이름을 딴 키 아래에 값을 담습니다: `integer`, `string`, `list`, `date`, `boolean`, `price` 또는 `version`. 정수 값은 문자열로 제공됩니다.

```json
{
  "name": "orders",
  "object_list": {
    "items": [
      { "fields": [{ "name": "amount", "integer": "120" }, { "name": "status", "string": "paid" }] },
      { "fields": [{ "name": "amount", "integer": "50" }] }
    ]
  }
}
```

`tagsList`에 객체 태그를 지정하면 해당 태그의 모든 필드를 내보냅니다.