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

# 사용자 행동 로그

> 사용자의 상품 조회·검색·구매 행동을 A2에 전달하는 방법을 알아봅니다.

사용자 행동 로그(activity log)는 서비스에서 발생하는 상품 조회, 검색, 장바구니 추가, 구매 등의 행동을 기록한 데이터입니다. A2는 이 데이터를 활용해 사용자가 어떤 상품에 관심을 보이는지 예측하고 더 좋은 광고를 추천합니다.

A2가 추천한 광고에 대한 반응 외에도 서비스에서 발생한 사용자 행동을 보내세요. 카탈로그의 상품 정보와 행동 로그를 같은 상품 ID로 연결하면, 어떤 상품에 어떤 행동이 발생했는지 함께 파악할 수 있습니다.

행동 로그를 구현할 때는 다음 순서로 진행합니다.

**1. 이벤트 선택**

서비스에 해당하는 이벤트를 확인하고, 수집할 수 있는 행동을 최대한 전달합니다.

**2. 요청 작성**

사용자·기기·상품 ID와 이벤트 발생 시각을 넣습니다.

**3. 전송 확인**

이벤트가 발생할 때 API로 보내고, 실제 수집 확인이 필요한 항목을 점검합니다.

## 시작하기 전에

* **API 접속 주소:** 구축이 완료된 A2 환경의 주소. 행동 로그 전송에는 액세스 토큰이 필요 없습니다.
* **사용자·기기 ID:** 행동을 한 사용자와 기기를 일관되게 구분할 식별자
* **상품 ID:** [상품 카탈로그](https://docs.aiderx.io/development/ko/catalog.md)에 등록한 상품의 ID

> [!NOTE]
>
> 같은 사용자는 이벤트마다 같은 `user_id`를, 같은 상품은 카탈로그와 같은 문자열 ID를 사용하세요. 모든 비회원에게 같은 ID를 부여하거나 이벤트마다 사용자 ID를 새로 만들지 마세요.

## 먼저 상품 조회 이벤트 보내기

아래 예제는 상품 `1001`을 조회한 이벤트를 전송합니다. `<UNIX_TIME_SECONDS>`를 이벤트가 발생한 시각의 초 단위 Unix 시간으로 바꾸고, 사용자·기기·상품 ID도 실제 값으로 바꿔 실행하세요.

```bash theme={null}
curl -i -X POST "<A2_API_URL>/dmp/v0/log/activity" \
  -H "Content-Type: application/json" \
  -d '{
    "schema": "activity.item_view",
    "timestamp": <UNIX_TIME_SECONDS>,
    "channel": "web",
    "user_id": "user-123",
    "device_id": "device-456",
    "catalogs": [
      { "id": "1001" }
    ]
  }'
```

HTTP `200`은 요청이 응답되었다는 뜻이며 개별 이벤트의 실제 수집을 확인해 주지는 않습니다. 아래 이벤트 설명에서 서비스에 맞는 유형과 필드를 확인한 뒤, [이벤트 테스트](#이벤트-테스트)로 요청을 점검하세요.

## 지원 이벤트 유형

서비스에서 발생하는 행동에 해당하는 이벤트를 최대한 보내세요. 아래 표에서 구현할 이벤트를 선택하면 해당 설명과 요청 예시로 이동합니다.

| 이벤트 | `schema` | 행동 |
| - | - | - |
| [상품 조회](#상품-조회-item_view) | `activity.item_view` | 상품을 조회합니다. |
| [상품 클릭](#상품-클릭-item_click) | `activity.item_click` | 상품을 클릭합니다. |
| [장바구니 추가](#장바구니-추가-add_cart) | `activity.add_cart` | 상품을 장바구니에 추가합니다. |
| [페이지 조회](#페이지-조회-page_view) | `activity.page_view` | 서비스의 페이지를 조회합니다. |
| [검색](#검색-search) | `activity.search` | 검색어로 검색을 실행합니다. |
| [구매](#구매-purchase) | `activity.purchase` | 상품 구매를 완료합니다. |
| [대기](#대기-wait) | `activity.wait` | 상품의 대기를 등록합니다. |
| [예약](#예약-resv) | `activity.resv` | 상품 예약을 완료합니다. |
| [즐겨찾기](#즐겨찾기-bmrk) | `activity.bmrk` | 상품을 즐겨찾기에 추가합니다. |
| [상품 목록 노출](#상품-목록-노출-item_impression) | `activity.item_impression` | A2가 송출하지 않은 상품 목록을 보여줍니다. |
| [결제 진입](#결제-진입-checkout) | `activity.checkout` | 구매 완료 전 결제 단계에 진입합니다. |
| [구매·예약 취소](#구매예약-취소-cancel) | `activity.cancel` | 구매나 예약을 전체·부분 취소하거나 환불합니다. |

## 공통 필드

각 요청은 아래 공통 필드에 이벤트별 필드를 더해 작성합니다.

| 필드 | 필수 여부 | 설명 |
| - | - | - |
| `schema` 문자열 | 필수 | 이벤트 유형입니다. 예: `"activity.item_view"`. |
| `timestamp` 정수 | 필수 | 이벤트가 발생한 Unix 시간입니다. **초 단위**로 보냅니다. JavaScript에서는 `Math.floor(Date.now() / 1000)`으로 현재 시각을 구할 수 있습니다. |
| `channel` 문자열 | 필수 | 행동이 발생한 채널입니다. 예: `"web"`, `"app"`. |
| `user_id` 문자열 | 필수 | 행동을 한 사용자의 ID입니다. 같은 사용자의 행동을 연결할 수 있도록 일관된 값을 사용합니다. |
| `device_id` 문자열 | 필수 | 행동이 발생한 기기의 ID입니다. 서버에서 전송하는 경우에도 사용자 기기의 ID를 넣습니다. |
| `page_id` 문자열 | 선택 | 행동이 발생한 페이지를 구분하는 값입니다. |
| `id` 문자열 | 선택 | 고객 서비스에서 이벤트에 부여한 ID입니다. 이 필드를 보내는 것만으로 중복 전송이 제거되지는 않습니다. |
| `device` 객체 | 선택 | 기기의 추가 정보입니다. 상세 필드는 API Reference의 [활동 로그 기록](https://docs.aiderx.io/ko/0.5.0/decision/activity-log/record-activity-log.md)을 참고하세요. |

## 이벤트별 설명

`catalogs`에는 상품 ID만 보내고, 상품명·이미지 등 상세 정보는 카탈로그 API로 등록합니다.

> [!NOTE]
>
> 한 이벤트의 `catalogs`에 상품이 여러 개 있으면 현재 DMP 학습에는 첫 상품인 `catalogs[0].id`만 사용합니다. 나머지 상품까지 모두 학습에 반영된다고 가정하지 않습니다.
>
> 예시의 사용자·기기·상품 ID와 `timestamp`는 실제 값으로 바꿔 사용하세요. 발생 시각은 초 단위 정수입니다.

### 상품 조회 `item_view`

상품을 조회했을 때 보냅니다.

| 필드 | 필수 여부 | 설명 |
| - | - | - |
| `catalogs` 객체 배열 | 필수 | 조회한 상품 목록입니다. |
| `catalogs[].id` 문자열 | 필수 | 카탈로그에 등록한 상품 ID입니다. |

```json theme={null}
{
  "schema": "activity.item_view",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "catalogs": [
    {
      "id": "1001"
    }
  ]
}
```

### 상품 클릭 `item_click`

사용자가 상품을 클릭했을 때 보냅니다.

| 필드 | 필수 여부 | 설명 |
| - | - | - |
| `catalogs` 객체 배열 | 필수 | 클릭한 상품 목록입니다. |
| `catalogs[].id` 문자열 | 필수 | 카탈로그에 등록한 상품 ID입니다. |
| `catalogs[].position` 0 이상의 정수 | 선택 | 목록에서 클릭한 상품의 0부터 시작하는 위치입니다. |

```json theme={null}
{
  "schema": "activity.item_click",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "catalogs": [
    {
      "id": "1001"
    }
  ]
}
```

### 장바구니 추가 `add_cart`

상품이 장바구니에 추가되었을 때 보냅니다.

| 필드 | 필수 여부 | 설명 |
| - | - | - |
| `catalogs` 객체 배열 | 필수 | 장바구니에 추가한 상품 목록입니다. |
| `catalogs[].id` 문자열 | 필수 | 카탈로그에 등록한 상품 ID입니다. |

```json theme={null}
{
  "schema": "activity.add_cart",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "catalogs": [
    {
      "id": "1001"
    }
  ]
}
```

### 페이지 조회 `page_view`

사용자가 페이지를 조회했을 때 보냅니다. 페이지는 공통 필드인 `page_id`로 구분할 수 있습니다.

| 필드 | 필수 여부 | 설명 |
| - | - | - |
| `catalogs` 객체 배열 | 필수 | 페이지와 관련된 상품 목록입니다. |
| `catalogs[].id` 문자열 | 필수 | 카탈로그에 등록한 상품 ID입니다. |

```json theme={null}
{
  "schema": "activity.page_view",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "page_id": "product-list",
  "catalogs": [
    {
      "id": "1001"
    }
  ]
}
```

### 검색 `search`

사용자가 검색을 실행했을 때 보냅니다. 상품 목록 대신 검색어를 전달합니다.

| 필드 | 필수 여부 | 설명 |
| - | - | - |
| `query` 문자열 | 필수 | 사용자가 입력한 검색어입니다. |

```json theme={null}
{
  "schema": "activity.search",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "query": "러닝화"
}
```

### 구매 `purchase`

구매가 완료되었을 때 보냅니다. 구매한 상품과 금액·통화 정보를 함께 전달합니다.

| 필드 | 필수 여부 | 설명 |
| - | - | - |
| `catalogs` 객체 배열 | 필수 | 구매한 상품 목록입니다. |
| `catalogs[].id` 문자열 | 필수 | 카탈로그에 등록한 상품 ID입니다. |
| `revenue` 객체 | 필수 | 금액과 통화를 담는 객체입니다. |
| `revenue.revenue` 숫자 | 필수 | 구매 금액입니다. |
| `revenue.currency` 문자열 | 필수 | 금액의 통화 코드입니다. 예: `"KRW"`, `"USD"`. |
| `transaction_id` 문자열 | 선택 | 구매를 결제 진입·취소와 연결할 주문 식별자입니다. |

```json theme={null}
{
  "schema": "activity.purchase",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "catalogs": [
    {
      "id": "1001"
    }
  ],
  "revenue": {
    "revenue": 89000,
    "currency": "KRW"
  },
  "transaction_id": "order-123"
}
```

### 대기 `wait`

서비스의 대기 기능에서 사용자가 대기를 등록했을 때 보냅니다.

| 필드 | 필수 여부 | 설명 |
| - | - | - |
| `catalogs` 객체 배열 | 필수 | 대기를 등록한 상품 목록입니다. |
| `catalogs[].id` 문자열 | 필수 | 카탈로그에 등록한 상품 ID입니다. |

```json theme={null}
{
  "schema": "activity.wait",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "catalogs": [
    {
      "id": "1001"
    }
  ]
}
```

### 예약 `resv`

예약이 완료되었을 때 보냅니다. 예약한 상품과 금액·통화 정보를 함께 전달합니다.

| 필드 | 필수 여부 | 설명 |
| - | - | - |
| `catalogs` 객체 배열 | 필수 | 예약한 상품 목록입니다. |
| `catalogs[].id` 문자열 | 필수 | 카탈로그에 등록한 상품 ID입니다. |
| `revenue` 객체 | 필수 | 금액과 통화를 담는 객체입니다. |
| `revenue.revenue` 숫자 | 필수 | 예약 금액입니다. |
| `revenue.currency` 문자열 | 필수 | 금액의 통화 코드입니다. 예: `"KRW"`, `"USD"`. |
| `transaction_id` 문자열 | 선택 | 예약 취소와 연결할 예약 식별자입니다. |

```json theme={null}
{
  "schema": "activity.resv",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "catalogs": [
    {
      "id": "1001"
    }
  ],
  "revenue": {
    "revenue": 89000,
    "currency": "KRW"
  },
  "transaction_id": "reservation-123"
}
```

### 즐겨찾기 `bmrk`

사용자가 상품을 즐겨찾기에 추가했을 때 보냅니다.

| 필드 | 필수 여부 | 설명 |
| - | - | - |
| `catalogs` 객체 배열 | 필수 | 즐겨찾기에 추가한 상품 목록입니다. |
| `catalogs[].id` 문자열 | 필수 | 카탈로그에 등록한 상품 ID입니다. |

```json theme={null}
{
  "schema": "activity.bmrk",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "catalogs": [
    {
      "id": "1001"
    }
  ]
}
```

### 상품 목록 노출 `item_impression`

A2가 송출한 추천이 아닌 서비스의 상품 목록에 상품이 표시되었을 때 보냅니다. A2 추천 노출은 별도의 `reco_log`로 전송합니다. `catalogs`와 상품 `id`는 필수이고, 목록에서 0부터 시작하는 `position`은 선택 사항입니다.

```json theme={null}
{
  "schema": "activity.item_impression",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "catalogs": [{ "id": "1001", "position": 0 }, { "id": "1002", "position": 1 }]
}
```

### 결제 진입 `checkout`

구매 완료 전에 결제 단계에 진입했을 때 보냅니다. `catalogs`와 상품 `id`는 필수입니다. 구매와 연결할 `transaction_id`, 결제 예정 금액과 통화를 담은 `revenue`는 선택 사항입니다.

```json theme={null}
{
  "schema": "activity.checkout",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "catalogs": [{ "id": "1001" }],
  "transaction_id": "order-123",
  "revenue": { "revenue": 89000, "currency": "KRW" }
}
```

### 구매·예약 취소 `cancel`

구매나 예약의 전체·부분 취소 및 환불이 확정되면 보냅니다. 원래 구매·예약에 보낸 `transaction_id`가 필수입니다. 취소 상품의 `catalogs`와 취소·환불 금액을 양수로 담은 `revenue`는 선택 사항입니다. 취소를 연결하려면 원래 `purchase` 또는 `resv` 이벤트에도 같은 `transaction_id`를 보내세요.

```json theme={null}
{
  "schema": "activity.cancel",
  "timestamp": 1793836800,
  "channel": "web",
  "user_id": "user-123",
  "device_id": "device-456",
  "transaction_id": "order-123",
  "catalogs": [{ "id": "1001" }],
  "revenue": { "revenue": 89000, "currency": "KRW" }
}
```

## 상품 행동과 광고 반응 연결

상품 ID가 `1001`인 상품을 조회하고 구매하는 흐름에서는 다음 기록을 구분합니다.

| 서비스에서 발생한 행동 | 전달할 데이터 |
| - | - |
| 상품을 조회합니다. | `activity.item_view`의 `catalogs[].id`에 `1001`을 보냅니다. A2 광고를 거쳤는지와 관계없이 수집합니다. |
| 상품 구매를 완료합니다. | `activity.purchase`에 상품 ID와 구매 금액·통화를 보냅니다. 위 구매 예시처럼 `89000`, `KRW`를 전달합니다. |
| A2 광고를 클릭합니다. | 광고의 `ext.ad_log`로 별도의 `clk` 광고 추적을 보냅니다. 상품 클릭 행동도 수집한다면 `activity.item_click`과 각각 구현합니다. |
| 구매를 A2 광고의 성과로 기록합니다. | 고객 서비스가 귀속할 광고를 정한 경우, 보관한 `ad_log`로 별도의 `conv`를 보냅니다. 구매 행동 로그만으로 광고 전환이 생성되지는 않습니다. |

같은 사용자는 광고 요청의 `user.id`와 행동 로그의 `user_id`에 일관된 값을 사용합니다. 여러 상품이 포함된 구매는 첫 상품만 DMP 학습에 사용된다는 범위도 함께 확인합니다. 광고 반응의 전송 방법은 [광고 이벤트 추적](https://docs.aiderx.io/development/ko/ad-events-and-logs.md)에서 확인합니다.

## API로 이벤트 보내기

[활동 로그 기록 API](https://docs.aiderx.io/ko/0.5.0/decision/activity-log/record-activity-log.md)로 이벤트를 전송합니다. 앞의 상품 조회 `curl` 예제처럼 JSON 본문을 `-d '...'`에 직접 넣으면 됩니다.

**1. 이벤트 본문 선택**

위에서 구현할 이벤트의 JSON 예시를 고릅니다. `schema`와 이벤트별 필드를 확인하고 사용자·기기·상품 ID와 발생 시각을 실제 값으로 바꾸세요.

**2. 이벤트 전송**

앞의 `curl` 예제에서 `-d '...'` 안의 JSON을 선택한 이벤트 본문으로 바꿔 전송합니다. `<A2_API_URL>`에는 전달받은 API 접속 주소를 넣습니다. HTTP `200`은 이벤트의 유효성이나 실제 수집·처리 완료를 보장하지 않으므로 아래 테스트 절에서 요청 내용도 확인하세요.

**3. 서비스의 행동 발생 지점에 연결**

상품 조회·검색·구매 등 실제 행동이 발생하는 시점에 요청 본문을 만들어 전송합니다.

웹·앱에서 보내거나 고객 서버를 거쳐 보낼 수 있습니다. 어느 경로를 사용하든 같은 사용자·상품 ID와 실제 이벤트 발생 시각을 유지하고, 같은 행동을 두 경로에서 중복 전송하지 않도록 합니다.

구매 행동 로그와 광고 전환 추적은 각각 구현합니다. 광고의 성과로 전환을 기록하는 방법은 [광고 이벤트 추적](https://docs.aiderx.io/development/ko/ad-events-and-logs.md)을 참고하세요.

## 이벤트 테스트

서비스에서 확인할 행동을 한 번 실행하고, 그 행동에 해당하는 요청을 확인합니다.

| 확인 항목 | 확인 방법 |
| - | - |
| 발생 지점 | 상품 조회·검색·구매 등 의도한 행동에서 요청이 전송되는지 확인합니다. |
| 전송 횟수 | 한 번의 행동에서 같은 요청이 반복 전송되지 않는지 확인합니다. |
| 이벤트 유형 | `schema`가 지원 이벤트 표의 값과 정확히 일치하는지 확인합니다. |
| 필수 필드 | 공통 필드와 이벤트별 `catalogs`, `query`, `revenue`가 필요한 위치에 있는지 확인합니다. |
| 상품 ID | `catalogs[].id`를 카탈로그에 저장된 ID와 문자열로 대조합니다. |
| 사용자·기기 ID | 전송 경로가 달라져도 같은 행동 주체의 ID가 유지되는지 확인합니다. |
| 발생 시각 | `timestamp`가 실제 발생 시각의 초 단위 정수인지 확인합니다. |
| HTTP 응답 | 요청의 응답 상태를 기록합니다. `200`만으로 수집 완료를 판단하지 않습니다. |

현재 개별 행동 로그의 수집 상태를 조회하는 화면이나 API는 제공되지 않습니다. 위 항목으로 고객 서비스의 전송 구현을 확인할 수 있지만, A2에 실제 수집되었는지까지 직접 확인할 수는 없습니다. 수집 확인이 필요하면 테스트 시각, `schema`, 민감정보를 제거한 요청 본문과 HTTP 응답을 준비해 A2 담당자에게 확인을 요청하세요.

## 다음 단계

[검증과 문제 해결](https://docs.aiderx.io/development/ko/integration-verification-and-troubleshooting.md)에서 카탈로그, 사용자 행동 로그, 광고 추적의 전달 상태를 확인합니다.

## 자주 발생하는 문제

> [accordion] 200인데 수집 여부를 알 수 없습니다
>
> 정상 HTTP 응답과 수집 완료는 구분합니다. 요청의 필드·유형을 확인한 뒤 위 테스트 정보로 문의합니다.

> [accordion] 상품 행동이 다른 상품과 연결됩니다
>
> 숫자 ID를 문자열로 바꾸는 과정에서 앞자리 `0`이 사라졌거나 다른 시스템의 ID를 사용하지 않았는지 확인합니다.

> [accordion] 같은 사용자의 행동이 나뉩니다
>
> 요청마다 `user_id`를 새로 만들거나 서버·웹·앱에서 서로 다른 값을 쓰지 않는지 확인합니다.

> [accordion] 요청이 중복 전송됩니다
>
> 중복 이벤트 핸들러, 서버와 클라이언트의 이중 전송, 재시도 처리를 확인합니다. `id`가 같아도 중복 방지를 보장하지 않습니다.

> [accordion] 요청이 실패하거나 타임아웃이 발생합니다
>
> 접속 주소와 요청 본문, 응답 상태·본문을 확인합니다. 타임아웃 뒤의 수집 여부도 알 수 없으므로 재전송 전에 중복 가능성을 검토합니다.


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