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

# 타겟팅 옵션

> 문맥 타겟팅 API로 타겟팅 옵션을 조회, 추가, 수정, 삭제하고 컨텍스트 ID를 최신 상태로 유지하는 방법을 안내합니다.

타겟팅 옵션(옵션)은 문맥 타겟팅 안에서 컨텍스트 ID를 묶어 이름을 붙인 것입니다(예: `sport`, `baseball`, `soccer`가 들어 있는 "Sports"). API 경로와 필드에서는 옵션을 `category`, 옵션의 컨텍스트 ID를 `contexts`라고 합니다.

옵션의 유형은 두 가지입니다.

* `atomic`: 컨텍스트 ID 목록으로 이루어진 일반 옵션입니다.
* `collection`: 자체 컨텍스트 ID와 하위 옵션의 컨텍스트 ID에 모두 매칭되는 옵션입니다. [옵션 그룹](/ko/integrations/context-targeting/option-groups#옵션-그룹이란)을 참고하세요.

이 페이지의 경로는 `https://lake.adrop.io` 기준입니다. 인증과 요청 형식은 [기본 정보](/ko/integrations/context-targeting/overview#기본-정보)를 참고하세요.

***

## 옵션 객체

이 페이지의 엔드포인트는 옵션을 다음 형태로 반환합니다. 옵션 객체에는 컨텍스트 ID 목록이 아니라 컨텍스트 ID 개수만 들어 있습니다. 컨텍스트 ID를 읽으려면 [옵션 조회](#옵션-조회)를 사용하세요.

| 필드 | 타입 | 설명 |
| - | - | - |
| `_id` | string | 옵션 ID. 서버가 발급하는 26자 ULID |
| `name` | string | 옵션 이름 |
| `type` | string | `atomic` 또는 `collection` |
| `contextCount` | number | 옵션에 매칭되는 컨텍스트 ID 수. 옵션 그룹은 자체 컨텍스트 ID와 하위 옵션의 컨텍스트 ID를 합쳐 중복을 뺀 개수 |
| `ownContextCount` | number | 옵션 그룹 전용. 옵션 그룹 자체 컨텍스트 ID 수 |
| `exposeChildren` | boolean | 옵션 그룹 전용. 하위 옵션을 광고주에게 노출하는지 여부. 설정한 적이 없으면 `false` |
| `children` | string\[] | 옵션 그룹 전용. 옵션 그룹 안의 순서대로 처음 50개 하위 옵션의 ID |
| `childCount` | number | 옵션 그룹 전용. 전체 하위 옵션 수. `children` 길이보다 크면 나머지는 [옵션 목록 조회](#옵션-목록-조회)의 `parent` 파라미터로 읽습니다 |
| `parent` | string | 하위 옵션 전용. 옵션이 속한 옵션 그룹의 ID |
| `createdAt` | string | 옵션 생성 시각(ISO 8601, UTC) |
| `updatedAt` | string | 옵션 최종 수정 시각(ISO 8601, UTC) |

옵션에 해당하지 않는 필드는 응답에서 빠집니다.

<CodeGroup>
  ```json 일반 옵션 theme={null}
  {
    "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8",
    "name": "Sports",
    "type": "atomic",
    "contextCount": 3,
    "createdAt": "2026-10-13T01:23:45.000Z",
    "updatedAt": "2026-10-13T01:23:45.000Z"
  }
  ```

  ```json 옵션 그룹 theme={null}
  {
    "_id": "01JAB3XK5SB1C2D3E4F5G6H7J8",
    "name": "Sports",
    "type": "collection",
    "contextCount": 3,
    "ownContextCount": 1,
    "exposeChildren": false,
    "children": ["01JAB3XK5TC1D2E3F4G5H6J7K8", "01JAB3XK5TC1D2E3F4G5H6J7K9"],
    "childCount": 2,
    "createdAt": "2026-10-13T01:23:45.000Z",
    "updatedAt": "2026-10-13T01:23:45.000Z"
  }
  ```

  ```json 하위 옵션 theme={null}
  {
    "_id": "01JAB3XK5TC1D2E3F4G5H6J7K8",
    "name": "Baseball",
    "type": "atomic",
    "contextCount": 1,
    "parent": "01JAB3XK5SB1C2D3E4F5G6H7J8",
    "createdAt": "2026-10-13T01:23:45.000Z",
    "updatedAt": "2026-10-13T01:23:45.000Z"
  }
  ```
</CodeGroup>

***

## 옵션 목록 조회

타겟팅의 옵션을 `_id` 오름차순(대략 생성 순서)으로 한 페이지씩 반환합니다. 옵션 그룹과 하위 옵션도 포함됩니다. 각 항목은 [옵션 객체](#옵션-객체)입니다.

### 요청

```http theme={null}
GET /context-targeting/{targetingId}/categories
```

`context-targeting:read` 권한이 필요합니다.

### 경로 파라미터

| 파라미터 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `targetingId` | string | O | 타겟팅 ID |

### 쿼리 파라미터

| 파라미터 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `next` | string | - | 다음 페이지 커서. 이전 응답의 `next` 값을 넣습니다. 첫 페이지를 요청할 때는 생략합니다 |
| `limit` | number | - | 페이지당 최대 옵션 수(1\~1,000). 기본값 1,000 |
| `parent` | string | - | 옵션 그룹 ID. 해당 옵션 그룹의 하위 옵션만 반환합니다 |

### 요청 예시

```bash theme={null}
curl "https://lake.adrop.io/context-targeting/01JAB3XK5Q8V7M2N4P6R9T0W1Y/categories?limit=1" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json"
```

### 응답 예시

```json theme={null}
{
  "items": [
    {
      "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8",
      "name": "Sports",
      "type": "atomic",
      "contextCount": 3,
      "createdAt": "2026-10-13T01:23:45.000Z",
      "updatedAt": "2026-10-13T01:23:45.000Z"
    }
  ],
  "next": "01JAB3XK5RA1B2C3D4E5F6G7H8"
}
```

다음 페이지를 받으려면 같은 요청에 `next=01JAB3XK5RA1B2C3D4E5F6G7H8`을 붙여 보냅니다.

* 마지막 페이지에는 `next`가 없습니다. `next`가 없을 때까지 따라가면 모든 옵션을 빠짐없이 한 번씩 받습니다.
* 다음 페이지가 남아 있어도 한 페이지의 항목 수가 `limit`보다 적을 수 있습니다. `next`가 없을 때만 멈추세요.
* 이름으로 검색하는 기능은 없습니다. 받은 항목을 `name`, `parent`, `children`으로 직접 걸러 쓰세요.

**오류**

| 상태 | `errorCode` | 발생 조건 |
| - | - | - |
| 404 | `TARGETING_NOT_FOUND` | 타겟팅이 없거나, 다른 프로젝트의 타겟팅이거나, 문맥 타겟팅이 아닌 경우 |
| 404 | `TARGETING_CATEGORY_NOT_FOUND` | `parent`가 이 타겟팅의 옵션이 아닌 경우 |
| 400 | `TARGETING_CATEGORY_NOT_SUPPORTED` | `parent`가 옵션 그룹이 아닌 경우 |

***

## 옵션 조회

옵션과 그 옵션의 컨텍스트 ID 한 페이지를 반환합니다.

### 요청

```http theme={null}
GET /context-targeting/category/{categoryId}
```

`context-targeting:read` 권한이 필요합니다.

### 경로 파라미터

| 파라미터 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `categoryId` | string | O | 옵션 ID |

### 쿼리 파라미터

| 파라미터 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `offset` | number | - | 반환을 시작할 컨텍스트 ID의 위치(0\~50,000). 기본값 0 |
| `limit` | number | - | 반환할 최대 컨텍스트 ID 수(1\~10,000). 기본값 10,000 |

### 요청 예시

```bash theme={null}
curl "https://lake.adrop.io/context-targeting/category/01JAB3XK5RA1B2C3D4E5F6G7H8?offset=0&limit=10000" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json"
```

### 응답 예시

```json theme={null}
{
  "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8",
  "name": "Sports",
  "type": "atomic",
  "contextCount": 3,
  "createdAt": "2026-10-13T01:23:45.000Z",
  "updatedAt": "2026-10-13T01:23:45.000Z",
  "targeting": "01JAB3XK5Q8V7M2N4P6R9T0W1Y",
  "contexts": ["sport", "baseball", "soccer"],
  "offset": 0,
  "limit": 10000
}
```

응답은 [옵션 객체](#옵션-객체)에 다음 필드가 더해진 형태입니다.

| 필드 | 타입 | 설명 |
| - | - | - |
| `targeting` | string | 옵션이 속한 타겟팅의 ID |
| `contexts` | string\[] | 이 페이지의 컨텍스트 ID |
| `offset` | number | 이 페이지의 `offset` |
| `limit` | number | 이 페이지의 `limit` |

* 옵션의 컨텍스트 ID를 모두 읽으려면 `offset`이 `contextCount`에 이를 때까지 `offset`을 `limit`만큼 늘려 가며 요청합니다.
* 옵션 그룹의 `contexts`에는 옵션 그룹 자체 컨텍스트 ID만 들어 있으므로 `ownContextCount`까지 페이지를 넘기며 읽습니다. 하위 옵션의 컨텍스트 ID는 하위 옵션을 조회해서 읽습니다.

**오류**

| 상태 | `errorCode` | 발생 조건 |
| - | - | - |
| 404 | `TARGETING_CATEGORY_NOT_FOUND` | 옵션이 없거나, 다른 프로젝트의 옵션이거나, 문맥 타겟팅의 옵션이 아닌 경우 |

***

## 옵션 추가

기존 타겟팅에 옵션을 추가합니다. 일반 옵션, 옵션 그룹, 기존 옵션 그룹의 하위 옵션을 추가할 수 있습니다.

### 요청

```http theme={null}
POST /context-targeting/{targetingId}/category
```

`context-targeting:write` 권한이 필요합니다. 응답은 `201`이며, 새로 만든 [옵션 객체](#옵션-객체)를 반환합니다.

### 경로 파라미터

| 파라미터 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `targetingId` | string | O | 옵션을 추가할 타겟팅의 ID |

### 요청 본문

| 필드 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `name` | string | O | 옵션 이름. 옵션 그룹과 하위 옵션을 포함해 타겟팅 안에서 고유해야 합니다. [이름 규칙](/ko/integrations/context-targeting/errors#이름-규칙)을 참고하세요 |
| `contexts` | string\[] | - | 컨텍스트 ID. 옵션 그룹이면 옵션 그룹 자체 컨텍스트 ID입니다. [컨텍스트 ID 규칙](#컨텍스트-id-규칙)을 참고하세요 |
| `type` | string | - | `atomic` 또는 `collection`. 생략하면 `children`이 있을 때(`[]` 포함)는 옵션 그룹, 없을 때는 일반 옵션이 됩니다 |
| `children` | object\[] | - | 옵션 그룹 전용. 옵션 그룹과 함께 만들 하위 옵션입니다. 각 항목에 `name`(필수)과 `contexts`를 넣습니다. 하위 옵션은 항상 일반 옵션입니다 |
| `exposeChildren` | boolean | - | 옵션 그룹 전용. 하위 옵션을 광고주에게 노출합니다. 기본값 `false`. [하위 옵션을 광고주에게 노출](/ko/integrations/context-targeting/option-groups#하위-옵션을-광고주에게-노출)을 참고하세요 |
| `parent` | string | - | 같은 타겟팅에 있는 최상위 옵션 그룹의 ID. 새 옵션을 그 옵션 그룹의 하위 옵션으로 추가합니다 |

ID는 모두 서버가 발급합니다. 본문에 `_id`나 위 표에 없는 필드가 있으면 `400`으로 실패합니다.

다음 조합은 `400 TARGETING_CATEGORY_NOT_SUPPORTED`로 실패하며, 아무것도 생성되지 않습니다.

* 하위 옵션도 자체 컨텍스트 ID도 없는 옵션 그룹을 만드는 경우
* `type: "atomic"`과 `children`을 함께 보낸 경우
* 일반 옵션에 `exposeChildren`을 보낸 경우
* `parent`와 `type: "collection"`, `children`, `exposeChildren` 중 하나를 함께 보낸 경우. 옵션 그룹에 추가하는 옵션은 항상 일반 옵션입니다
* `parent`가 최상위 옵션 그룹이 아닌 옵션을 가리키는 경우

요청 하나로 만들 수 있는 옵션은 옵션 그룹과 그 하위 옵션을 합쳐 최대 5,000개이고, 요청 하나에 담을 수 있는 컨텍스트 ID는 모두 합쳐 최대 10,000개입니다. 여러 옵션을 요청 하나로 추가하려면 [옵션 일괄 추가](#옵션-일괄-추가)를 사용하세요.

### 요청 예시

```bash theme={null}
curl -X POST https://lake.adrop.io/context-targeting/01JAB3XK5Q8V7M2N4P6R9T0W1Y/category \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Tech",
    "contexts": ["tech", "ai", "mobile"]
  }'
```

### 응답 예시

```json theme={null}
{
  "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9",
  "name": "Tech",
  "type": "atomic",
  "contextCount": 3,
  "createdAt": "2026-10-13T01:23:45.000Z",
  "updatedAt": "2026-10-13T01:23:45.000Z"
}
```

<Note>
  새 최상위 옵션은 이 타겟팅을 사용하는 광고 상품과 그 캠페인에도 추가될 수 있습니다. [광고 상품과 캠페인에 미치는 영향](#광고-상품과-캠페인에-미치는-영향)을 참고하세요.
</Note>

**오류**

| 상태 | `errorCode` | 발생 조건 |
| - | - | - |
| 404 | `TARGETING_NOT_FOUND` | 타겟팅이 없거나, 다른 프로젝트의 타겟팅이거나, 문맥 타겟팅이 아닌 경우 |
| 404 | `TARGETING_CATEGORY_NOT_FOUND` | `parent`가 이 타겟팅의 옵션이 아닌 경우 |
| 400 | `TARGETING_CATEGORY_NOT_SUPPORTED` | 위 조합 중 하나에 해당하는 경우 |
| 400 | `TARGETING_CONTEXT_LIMIT_EXCEEDED` | 옵션이나 프로젝트의 컨텍스트 ID 한도를 넘게 되는 경우. [컨텍스트 ID 규칙](#컨텍스트-id-규칙)을 참고하세요 |
| 400 | `TARGETING_CATEGORY_LIMIT_EXCEEDED` | 타겟팅의 옵션이 하위 옵션을 포함해 100,000개를 넘게 되는 경우 |
| 400 | `TARGETING_COLLECTION_CONTEXT_DUPLICATED` | 옵션 그룹 자체 컨텍스트 ID 중 하나가 이미 프로젝트 안 다른 옵션 그룹의 자체 컨텍스트 ID인 경우. [제약 사항](/ko/integrations/context-targeting/option-groups#제약-사항)을 참고하세요 |
| 400 | `AD_PRODUCT_TARGETING_POLICY_LIMIT_EXCEEDED` | 광고 상품의 필수 옵션이 500개를 넘게 되는 경우. [광고 상품과 캠페인에 미치는 영향](#광고-상품과-캠페인에-미치는-영향)을 참고하세요 |
| 409 | `TARGETING_CATEGORY_NAME_DUPLICATE` | 타겟팅 안에서 이미 쓰는 이름이거나, 요청 안에서 이름이 겹치는 경우. `detail.existingIds`에 그 이름을 쓰는 기존 옵션의 ID가 들어 있습니다. 요청 안에서 이름이 겹친 경우에는 대신 `detail.duplicateOfIndex`가 반환됩니다 |

전체 오류 코드와 오류 형식은 [오류 코드](/ko/integrations/context-targeting/errors#오류-코드)를 참고하세요.

***

## 옵션 수정

옵션의 이름이나 컨텍스트 ID를 바꾸거나, 옵션을 옵션 그룹 안팎이나 옵션 그룹 사이로 옮기거나, 옵션 그룹이 하위 옵션을 광고주에게 노출할지 바꿉니다.

### 요청

```http theme={null}
PATCH /context-targeting/category/{categoryId}
```

`context-targeting:write` 권한이 필요합니다. 응답은 `200`이며, 수정된 [옵션 객체](#옵션-객체)를 반환합니다.

### 경로 파라미터

| 파라미터 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `categoryId` | string | O | 옵션 ID |

### 요청 본문

모든 필드는 선택입니다. 바꿀 필드만 보내세요. 보내지 않은 필드는 그대로 유지됩니다.

| 필드 | 타입 | 설명 |
| - | - | - |
| `name` | string | 새 이름. [이름 규칙](/ko/integrations/context-targeting/errors#이름-규칙)을 참고하세요 |
| `contexts` | object | 컨텍스트 ID 변경 내용. `add`, `remove` 중 하나 또는 둘 다 쓰거나, `set`만 씁니다 |
| `contexts.add` | string\[] | 추가할 컨텍스트 ID. 옵션에 이미 있는 ID는 무시합니다 |
| `contexts.remove` | string\[] | 삭제할 컨텍스트 ID. 옵션에 없는 ID는 무시합니다 |
| `contexts.set` | string\[] | 옵션의 컨텍스트 ID 전체를 이 목록으로 바꿉니다 |
| `parent` | string 또는 null | 일반 옵션 전용. 옵션 그룹 ID를 넣으면 옵션을 그 옵션 그룹 아래로 옮깁니다. `null`을 넣으면 옵션을 옵션 그룹에서 분리해 최상위로 옮깁니다. [옵션 이동과 분리](/ko/integrations/context-targeting/option-groups#옵션-이동과-분리)를 참고하세요 |
| `exposeChildren` | boolean | 옵션 그룹 전용. 하위 옵션을 광고주에게 노출합니다 |

본문에는 다음 규칙이 적용됩니다.

* `contexts`에는 `add`, `remove`, `set` 중 하나 이상이 있어야 하고, `set`은 `add`나 `remove`와 함께 쓸 수 없습니다. 이를 어기면 요청이 `400`으로 실패합니다.
* `parent`는 `contexts`와 함께 쓸 수 없습니다. 옵션 이동·분리와 컨텍스트 ID 변경은 요청을 나눠 보내세요. `parent`와 `name`은 함께 쓸 수 있습니다. 이동이 실패하면 이름도 바뀌지 않습니다.
* `null`은 `parent`에만 쓸 수 있습니다. `name`, `contexts`, `exposeChildren`, `add`, `remove`, `set`에 `null`을 넣으면 `400`으로 실패합니다. 필드를 바꾸지 않으려면 생략하세요.
* 옵션 그룹은 옮길 수 없습니다. 옵션 그룹에 `parent`를 보내면 `400 TARGETING_CATEGORY_NOT_SUPPORTED`로 실패하며, 일반 옵션에 `exposeChildren`을 보낼 때도 마찬가지입니다.
* 옵션 그룹에 보낸 `contexts`는 옵션 그룹 자체 컨텍스트 ID를 바꿉니다. 하위 옵션이 없는 옵션 그룹의 자체 컨텍스트 ID를 모두 삭제하면 `400 TARGETING_CATEGORY_NOT_SUPPORTED`로 실패합니다.
* `name`, `contexts`, `exposeChildren`은 함께 저장됩니다. 컨텍스트 ID 변경이 실패하면 이름과 `exposeChildren`도 바뀌지 않습니다. 단, `add`와 `remove`를 함께 보내면 `add`가 먼저 적용되므로 `remove`가 실패해도 추가한 컨텍스트 ID는 남습니다.

하위 옵션도 다른 일반 옵션과 같은 방법으로 이름과 컨텍스트 ID를 바꿀 수 있습니다. `add`/`remove`와 `set` 중 무엇을 쓸지는 [컨텍스트 ID 갱신 방법](#컨텍스트-id-갱신-방법)을 참고하세요.

### 요청 예시

```bash theme={null}
curl -X PATCH https://lake.adrop.io/context-targeting/category/01JAB3XK5RA1B2C3D4E5F6G7H9 \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contexts": {
      "add": ["science"],
      "remove": ["mobile"]
    }
  }'
```

### 응답 예시

```json theme={null}
{
  "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9",
  "name": "Tech",
  "type": "atomic",
  "contextCount": 3,
  "createdAt": "2026-10-13T01:23:45.000Z",
  "updatedAt": "2026-10-13T02:10:00.000Z"
}
```

**오류**

| 상태 | `errorCode` | 발생 조건 |
| - | - | - |
| 404 | `TARGETING_CATEGORY_NOT_FOUND` | 이 프로젝트의 문맥 타겟팅에 옵션이 없거나, `parent`가 같은 타겟팅의 옵션이 아닌 경우 |
| 400 | `TARGETING_CATEGORY_NOT_SUPPORTED` | 위 규칙에 해당하거나, `parent`가 최상위 옵션 그룹이 아니거나, 옮기고 나면 하위 옵션도 자체 컨텍스트 ID도 없는 옵션 그룹이 남는 경우 |
| 400 | `TARGETING_CATEGORY_LOCKED` | 광고 상품에서 필수로 지정했거나 종료되지 않은 캠페인이 포함 또는 제외한 최상위 옵션을 옵션 그룹 아래로 옮기는 경우 |
| 400 | `TARGETING_CONTEXT_LIMIT_EXCEEDED` | 옵션이나 프로젝트의 컨텍스트 ID 한도를 넘게 되는 경우 |
| 400 | `TARGETING_COLLECTION_CONTEXT_DUPLICATED` | 옵션 그룹에 추가한 컨텍스트 ID가 이미 프로젝트 안 다른 옵션 그룹의 자체 컨텍스트 ID인 경우 |
| 400 | `AD_PRODUCT_TARGETING_POLICY_LIMIT_EXCEEDED` | 옵션을 분리하면 광고 상품의 필수 옵션이 500개를 넘게 되는 경우 |
| 409 | `TARGETING_CATEGORY_NAME_DUPLICATE` | 새 이름을 타겟팅 안에서 이미 쓰는 경우. `detail.existingIds`에 그 이름을 쓰는 옵션의 ID가 들어 있습니다 |

***

## 옵션 삭제

옵션과 그 옵션의 컨텍스트 ID를 삭제합니다.

* 옵션 그룹을 삭제하면 하위 옵션이 최상위 옵션이 되고, 광고 상품과 캠페인에 있던 옵션 그룹의 설정을 이어받습니다. 옵션 그룹에 자체 컨텍스트 ID가 있었다면 그 컨텍스트 ID는 더 이상 매칭되지 않습니다. [옵션 그룹 삭제](/ko/integrations/context-targeting/option-groups#옵션-그룹-삭제)를 참고하세요.
* 자체 컨텍스트 ID가 없는 옵션 그룹의 마지막 하위 옵션은 삭제할 수 없습니다. 이때는 옵션 그룹을 삭제하세요.

<Warning>
  캠페인이 사용 중인 옵션이라도 삭제가 막히지 않습니다. 옵션을 삭제하기 전에 [광고 상품과 캠페인에 미치는 영향](#광고-상품과-캠페인에-미치는-영향)을 확인하세요.
</Warning>

### 요청

```http theme={null}
DELETE /context-targeting/category/{categoryId}
```

`context-targeting:write` 권한이 필요합니다. 응답은 `200`이며, 삭제한 옵션의 ID를 반환합니다.

### 경로 파라미터

| 파라미터 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `categoryId` | string | O | 옵션 ID |

### 요청 예시

```bash theme={null}
curl -X DELETE https://lake.adrop.io/context-targeting/category/01JAB3XK5RA1B2C3D4E5F6G7H9 \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json"
```

### 응답 예시

```json theme={null}
{
  "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9"
}
```

**오류**

| 상태 | `errorCode` | 발생 조건 |
| - | - | - |
| 404 | `TARGETING_CATEGORY_NOT_FOUND` | 이 프로젝트의 문맥 타겟팅에 옵션이 없는 경우. 삭제 요청을 다시 보냈다면 첫 요청에서 이미 삭제된 것입니다 |
| 400 | `TARGETING_CATEGORY_NOT_SUPPORTED` | 자체 컨텍스트 ID가 없는 옵션 그룹의 마지막 하위 옵션인 경우 |
| 400 | `AD_PRODUCT_TARGETING_POLICY_LIMIT_EXCEEDED` | 옵션 그룹을 삭제하면 광고 상품의 필수 옵션이 500개를 넘게 되는 경우 |

***

## 일괄 작업

일괄 작업 엔드포인트는 요청 하나로 옵션을 최대 5,000개까지 추가, 수정, 삭제합니다. `context-targeting:write` 권한이 필요합니다.

* 요청 전체가 실패하지 않는 한, 일괄 작업 엔드포인트는 `200`과 함께 항목별 결과를 요청 순서대로 반환합니다. 각 결과의 `success`를 확인하세요. 실패한 항목에는 `error`가 있습니다. [일괄 작업 응답](/ko/integrations/context-targeting/errors#일괄-작업-응답)을 참고하세요.
* 본문이 잘못된 요청은 잘못된 항목이 하나뿐이어도 요청 전체가 `400`으로 실패합니다. [컨텍스트 ID 규칙](#컨텍스트-id-규칙)을 어긴 컨텍스트 ID, `add`와 함께 쓴 `set`, 허용되지 않는 필드 등이 여기에 해당합니다.
* 한 프로젝트의 API 쓰기 요청은 한 번에 하나씩 처리됩니다. 일괄 요청은 병렬로 보내지 말고 하나씩 차례로 보내세요. [동시 쓰기](/ko/integrations/context-targeting/errors#동시-쓰기)를 참고하세요.

요청마다 다음 한도를 지켜야 합니다.

| 한도 | 추가 | 수정 | 삭제 |
| - | - | - | - |
| 요청당 항목 수 | 1\~5,000 | 1\~5,000 | 1\~5,000 |
| 요청당 생성 옵션 수(하위 옵션 포함) | 5,000 | – | – |
| 요청당 컨텍스트 ID 수(모든 항목 합계) | 10,000 | 10,000 | – |
| 요청당 옵션 그룹 관련 항목 수 | 150 | 150 | 150 |

옵션 그룹 관련 항목은 다음과 같습니다.

* **추가**: 옵션 그룹을 만드는 항목(`type: "collection"` 또는 `children`)과 `parent`가 있는 항목
* **수정**: `parent`가 있는 항목(`null` 포함)
* **삭제**: 옵션 그룹과 하위 옵션의 ID

한도를 넘는 요청은 전체가 `400`으로 실패하며, 요청 안의 어떤 항목도 적용되지 않습니다. 삭제 요청에 옵션 그룹과 하위 옵션이 150개를 넘게 들어 있으면 오류는 `TARGETING_CATEGORY_TREE_ITEMS_LIMIT_EXCEEDED`이며, `detail.limit`에는 한도가, `detail.count`에는 요청에 든 옵션 그룹과 하위 옵션 수가 들어 있습니다. 옵션 수만 개처럼 많은 데이터를 올리려면 5,000개 이하로 나눈 요청을 차례로 보내세요.

### 옵션 일괄 추가

기존 타겟팅에 옵션을 추가합니다. 항목마다 다른 타겟팅에 추가할 수 있습니다.

#### 요청

```http theme={null}
POST /context-targeting/category/bulk/create
```

#### 요청 본문

| 필드 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `items` | object\[] | O | 추가할 옵션. 각 항목에는 [옵션 추가](#옵션-추가) 요청 본문의 필드를 넣습니다 |
| `items[].targeting` | string | O | 옵션을 추가할 타겟팅의 ID |

성공한 항목의 `data`에는 새로 만든 [옵션 객체](#옵션-객체)가 들어 있습니다.

#### 요청 예시

```bash theme={null}
curl -X POST https://lake.adrop.io/context-targeting/category/bulk/create \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "targeting": "01JAB3XK5Q8V7M2N4P6R9T0W1Y", "name": "Entertainment", "contexts": ["entertainment", "movie"] },
      { "targeting": "01JAB3XK5Q8V7M2N4P6R9T0W1Y", "name": "Business", "contexts": ["business", "economy"] },
      { "targeting": "01JAB3XK5Q8V7M2N4P6R9T0W1Y", "name": "Sports", "contexts": ["sport"] }
    ]
  }'
```

#### 응답 예시

타겟팅에 "Sports"라는 이름의 옵션이 이미 있어서 세 번째 항목만 실패합니다.

```json theme={null}
{
  "results": [
    {
      "index": 0,
      "success": true,
      "data": {
        "_id": "01JAB3XK5VD1E2F3G4H5J6K7M8",
        "name": "Entertainment",
        "type": "atomic",
        "contextCount": 2,
        "createdAt": "2026-10-13T02:20:00.000Z",
        "updatedAt": "2026-10-13T02:20:00.000Z"
      }
    },
    {
      "index": 1,
      "success": true,
      "data": {
        "_id": "01JAB3XK5VD1E2F3G4H5J6K7M9",
        "name": "Business",
        "type": "atomic",
        "contextCount": 2,
        "createdAt": "2026-10-13T02:20:00.000Z",
        "updatedAt": "2026-10-13T02:20:00.000Z"
      }
    },
    {
      "index": 2,
      "success": false,
      "error": {
        "statusCode": 409,
        "errorCode": "TARGETING_CATEGORY_NAME_DUPLICATE",
        "detail": {
          "existingIds": ["01JAB3XK5RA1B2C3D4E5F6G7H8"]
        }
      }
    }
  ],
  "succeeded": 2,
  "failed": 1
}
```

### 옵션 일괄 수정

옵션을 수정합니다. 항목마다 다른 옵션을 수정할 수 있습니다.

#### 요청

```http theme={null}
POST /context-targeting/category/bulk/update
```

#### 요청 본문

| 필드 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `items` | object\[] | O | 수정 내용. 각 항목에는 [옵션 수정](#옵션-수정) 요청 본문의 필드를 넣습니다 |
| `items[]._id` | string | O | 수정할 옵션의 ID |

* 요청 하나에 같은 `_id`는 한 번만 넣을 수 있습니다. `_id`가 겹치면 요청 전체가 `400`으로 실패하므로, 한 옵션의 변경 내용은 모두 한 항목에 담으세요.
* 항목은 각각 따로 적용되며 동시에 처리될 수 있습니다. 그래도 결과는 요청 순서대로 반환됩니다.
* 앞 항목이 이름을 바꾸면서 비운 이름은 같은 요청의 뒤 항목에서 쓸 수 있습니다.

성공한 항목의 `data`에는 수정된 [옵션 객체](#옵션-객체)가 들어 있습니다.

#### 요청 예시

```bash theme={null}
curl -X POST https://lake.adrop.io/context-targeting/category/bulk/update \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8", "contexts": { "add": ["basketball"] } },
      { "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9", "name": "Technology" }
    ]
  }'
```

#### 응답 예시

```json theme={null}
{
  "results": [
    {
      "index": 0,
      "success": true,
      "data": {
        "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8",
        "name": "Sports",
        "type": "atomic",
        "contextCount": 4,
        "createdAt": "2026-10-13T01:23:45.000Z",
        "updatedAt": "2026-10-13T02:10:00.000Z"
      }
    },
    {
      "index": 1,
      "success": true,
      "data": {
        "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9",
        "name": "Technology",
        "type": "atomic",
        "contextCount": 3,
        "createdAt": "2026-10-13T01:23:45.000Z",
        "updatedAt": "2026-10-13T02:10:00.000Z"
      }
    }
  ],
  "succeeded": 2,
  "failed": 0
}
```

### 옵션 일괄 삭제

옵션을 삭제합니다. ID마다 [옵션 삭제](#옵션-삭제)와 같은 규칙이 적용됩니다.

<Warning>
  옵션 그룹과 그 하위 옵션을 같은 요청에서 삭제하지 마세요. 옵션 그룹과 하위 옵션은 요청 순서대로 처리됩니다. 옵션 그룹이 먼저 오면 하위 옵션이 최상위 옵션이 된 뒤 최상위 옵션으로 삭제됩니다. 하위 옵션이 먼저 오고 옵션 그룹에 자체 컨텍스트 ID가 없으면, 마지막 하위 옵션은 삭제에 실패해 그대로 남습니다.
</Warning>

#### 요청

```http theme={null}
POST /context-targeting/category/bulk/delete
```

#### 요청 본문

| 필드 | 타입 | 필수 | 설명 |
| - | - | :-: | - |
| `ids` | string\[] | O | 삭제할 옵션의 ID |

성공한 항목의 `data`에는 삭제한 옵션의 ID가 들어 있습니다.

#### 요청 예시

```bash theme={null}
curl -X POST https://lake.adrop.io/context-targeting/category/bulk/delete \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": ["01JAB3XK5RA1B2C3D4E5F6G7H8", "01JAB3XK5RA1B2C3D4E5F6G7H9"]
  }'
```

#### 응답 예시

```json theme={null}
{
  "results": [
    { "index": 0, "success": true, "data": { "_id": "01JAB3XK5RA1B2C3D4E5F6G7H8" } },
    { "index": 1, "success": true, "data": { "_id": "01JAB3XK5RA1B2C3D4E5F6G7H9" } }
  ],
  "succeeded": 2,
  "failed": 0
}
```

***

## 컨텍스트 ID 규칙

`contexts`, `children`, `add`, `remove`, `set`으로 보내는 모든 컨텍스트 ID에 다음 규칙이 적용됩니다.

| 규칙 | 설명 |
| - | - |
| 공백 | 앞뒤 공백은 제거되므로 `" sport "`와 `sport`는 같은 컨텍스트 ID입니다. 컨텍스트 ID 중간의 공백은 유지됩니다 |
| 길이 | 앞뒤 공백을 제거한 뒤 UTF-8 기준 1\~256바이트. 빈 값이나 공백만 있는 값은 허용되지 않습니다 |
| 쉼표 | 허용되지 않습니다 |
| 문자 | 그 밖의 문자는 모두 허용되지만, 올바른 형식의 유니코드 텍스트여야 합니다. 짝이 없는 서로게이트(unpaired surrogate)는 허용되지 않습니다 |
| 대소문자 | 대소문자를 구분합니다. `Sport`와 `sport`는 서로 다른 컨텍스트 ID입니다. 페이지가 광고 요청에 보내는 값과 정확히 같게 등록하세요 |
| 중복 | 한 번만 저장됩니다. 요청 안의 중복은 하나로 합쳐지고, 옵션에 이미 있는 컨텍스트 ID를 추가해도 아무것도 바뀌지 않습니다 |

요청 안의 컨텍스트 ID 중 하나라도 이 규칙을 어기면 요청 전체가 `400`으로 실패합니다.

같은 컨텍스트 ID를 여러 옵션에 넣을 수 있습니다. 그 컨텍스트 ID가 담긴 광고 요청은 해당 옵션 모두에 매칭됩니다. 단, 옵션 그룹 자체 컨텍스트 ID는 프로젝트 안 다른 옵션 그룹의 자체 컨텍스트 ID와 겹칠 수 없습니다. [제약 사항](/ko/integrations/context-targeting/option-groups#제약-사항)을 참고하세요.

컨텍스트 ID는 개수와 크기에도 한도가 있습니다.

| 한도 | 값 |
| - | - |
| 옵션당 컨텍스트 ID 수 | 50,000개. 옵션 그룹은 자체 컨텍스트 ID를 셉니다 |
| 옵션당 전체 크기 | 8 MB(옵션에 있는 컨텍스트 ID의 UTF-8 크기 합계) |
| 프로젝트당 컨텍스트 ID 수 | 200,000개(프로젝트의 모든 문맥 타겟팅에 있는 모든 옵션 합산). 두 옵션에 들어 있는 컨텍스트 ID는 두 번 셉니다 |
| 요청당 컨텍스트 ID 수 | 10,000개(모든 항목과 `add`, `remove`, `set` 합산) |

변경 후 옵션이나 프로젝트가 한도를 넘게 되면 `400 TARGETING_CONTEXT_LIMIT_EXCEEDED`로 실패합니다. 이 한도는 변경으로 옵션이나 프로젝트가 커질 때만 확인하므로, 컨텍스트 ID를 삭제하기만 하는 변경은 막히지 않습니다. 컨텍스트 ID가 10,000개를 넘는 요청은 전체가 `400`으로 실패합니다. 전체 한도는 [한도](/ko/integrations/context-targeting/errors#한도)를 참고하세요.

***

## 컨텍스트 ID 갱신 방법

컨텍스트 ID는 [옵션 수정](#옵션-수정)이나 [옵션 일괄 수정](#옵션-일괄-수정)에서 `add`/`remove` 또는 `set`으로 바꿉니다.

| 방법 | 동작 | 사용하는 경우 |
| - | - | - |
| `add` / `remove` | 지정한 컨텍스트 ID만 바꿉니다 | 섹션이 추가되거나 삭제되는 등 변경 사항을 그때그때 받는 경우 |
| `set` | 옵션의 컨텍스트 ID 전체를 보낸 목록으로 바꿉니다 | 옵션의 전체 목록을 갖고 있고, 그 목록이 10,000개 이하인 경우 |

`add`, `remove`, `set`은 같은 요청을 다시 보내도 결과가 같으므로, 타임아웃이 난 뒤 다시 보내도 됩니다. [재시도와 중복 요청](/ko/integrations/context-targeting/errors#재시도와-중복-요청)을 참고하세요.

**컨텍스트 ID가 10,000개를 넘는 경우**

요청 하나에는 컨텍스트 ID를 10,000개까지만 담을 수 있으므로, 그보다 긴 목록은 `set` 요청 하나로 바꿀 수 없습니다. 대신 다음 방법으로 옵션을 동기화하세요.

1. [옵션 조회](#옵션-조회)에서 `offset`과 `limit`으로 페이지를 넘기며 옵션의 현재 컨텍스트 ID를 읽습니다.
2. 가지고 있는 목록과 비교합니다.
3. 차이만 `add`와 `remove`로, 요청당 컨텍스트 ID 10,000개까지 담아 하나씩 차례로 보냅니다.

전체 목록을 여러 `set` 요청으로 나눠 보내지 마세요. `set`은 매번 컨텍스트 ID 전체를 바꾸므로, 뒤의 `set`이 앞선 요청에서 넣은 컨텍스트 ID를 지웁니다. `set` 한 번 뒤에 `add` 요청을 이어 보내더라도 요청과 요청 사이에는 옵션에 목록의 일부만 남아 있고, 광고 송출에 그 일부 목록이 반영될 수 있습니다.

변경 사항은 잠시 후 광고 송출에 반영됩니다. 변경 사항이 로드됐는지 확인하는 방법은 [변경 반영 시점](/ko/integrations/context-targeting/overview#변경-반영-시점)을 참고하세요.

***

## 광고 상품과 캠페인에 미치는 영향

광고 상품과 캠페인은 옵션을 선택해 사용하므로, 옵션을 바꾸면 캠페인이 노출되는 곳도 바뀔 수 있습니다. 컨텍스트 ID를 추가하거나 삭제하면 그 옵션을 포함하거나 제외한 캠페인이 노출될 수 있는 페이지가 바뀝니다.

**새 최상위 옵션**

최상위 옵션(일반 옵션 또는 옵션 그룹)을 추가하면, 그 타겟팅을 설정해 둔 광고 상품에도 새 옵션이 추가됩니다.

* 광고 상품이 타겟팅의 현재 최상위 옵션을 모두 **필수 포함**으로 설정했다면 새 옵션도 **필수 포함**으로 설정되고, 그 상품의 종료되지 않은 캠페인에도 포함됩니다. **필수 제외**도 같은 방식으로 동작합니다.
* 그 밖의 경우에는 새 옵션이 광고주가 선택할 수 있는 옵션으로 광고 상품에 추가됩니다. 필수가 아닌 최상위 옵션이 있거나, **필수 포함**과 **필수 제외**가 섞여 있는 경우가 여기에 해당합니다. 캠페인은 바뀌지 않습니다.

따라서 모든 옵션을 필수로 지정한 광고 상품에서는 새 옵션이 캠페인의 노출 범위를 넓히거나(**필수 포함**) 좁힙니다(**필수 제외**). 광고 상품에서 타겟팅의 최상위 옵션이 하나라도 필수가 아니면, 이후 새로 추가하는 옵션도 선택할 수 있는 옵션으로 추가됩니다. 새 옵션을 필수로 지정하려면 콘솔에서 광고 상품의 타겟팅 설정을 바꾸세요. [타겟팅 판매하기](/ko/targeting/sell)를 참고하세요.

옵션 그룹에 추가한 하위 옵션은 광고 상품에 따로 추가되지 않습니다. 대신 옵션 그룹이 하위 옵션의 컨텍스트 ID에도 매칭됩니다. [옵션 그룹에 옵션 추가](/ko/integrations/context-targeting/option-groups#옵션-그룹에-옵션-추가)를 참고하세요.

**필수 옵션 한도**

광고 상품 하나에는 모든 타겟팅을 합쳐 **필수 포함** 또는 **필수 제외**로 설정한 옵션을 최대 500개까지 둘 수 있습니다. 위 규칙에 따라 광고 상품이 이 한도를 넘게 되면 새 최상위 옵션은 생성되지 않고 `400 AD_PRODUCT_TARGETING_POLICY_LIMIT_EXCEEDED`로 실패합니다.

| 필드 | 설명 |
| - | - |
| `detail.limit` | 광고 상품당 최대 필수 옵션 수 |
| `detail.count` | 변경 후 광고 상품이 갖게 될 필수 옵션 수. 여러 광고 상품이 한도를 넘으면 그중 가장 큰 값 |
| `detail.products` | 한도를 넘게 되는 광고 상품의 ID |

일괄 요청에서는 그 타겟팅에 추가하는 새 최상위 옵션이 모두 실패하고, 나머지 항목은 적용됩니다. 콘솔에서 광고 상품의 필수 옵션을 줄인 뒤 요청을 다시 보내세요. 옵션 그룹을 삭제하거나 하위 옵션을 분리할 때도 옵션 그룹의 설정이 하위 옵션으로 복사되므로 같은 한도가 적용됩니다.

**옵션 삭제**

[타겟팅 삭제](/ko/integrations/context-targeting/targetings#타겟팅-삭제)와 달리, 옵션은 캠페인이 사용 중이어도 삭제가 막히지 않습니다. 삭제한 옵션은 그 옵션을 선택한 캠페인에서 빠집니다. 캠페인이 그 타겟팅에서 포함한 옵션이 이 옵션 하나뿐이었다면, 캠페인은 더 이상 그 타겟팅의 컨텍스트 ID를 요구하지 않습니다. 캠페인은 멈추지 않고, 다른 컨텍스트 ID가 담긴 광고 요청이나 컨텍스트 ID가 없는 광고 요청에도 노출될 수 있습니다.

옵션을 삭제하기 전에 캠페인이 그 옵션을 사용하는지 확인하세요. 콘솔에서 캠페인이 예약된 옵션은 **운영중** 상태로 표시됩니다. [타겟팅 관리하기](/ko/targeting/manage)를 참고하세요. 옵션을 삭제하기 전에 해당 캠페인을 변경하거나 종료하세요.

**컨텍스트 ID 제외**

옵션에는 매칭할 컨텍스트 ID만 나열합니다. "이 옵션에 없는 컨텍스트 ID"라는 조건은 없습니다. 특정 페이지에 광고가 노출되지 않게 하려면, 그 페이지의 컨텍스트 ID를 옵션에 넣고 광고 상품이나 캠페인에서 그 옵션을 제외하세요(예: 광고 상품에서 **필수 제외**로 설정). 나머지 컨텍스트 ID를 모두 나열한 옵션을 만들지 마세요.


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