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

# 카탈로그

> 상품, 매장, 콘텐츠처럼 반복해서 사용하는 참조 데이터를 등록하고 Liquid로 메시지를 개인화하는 방법을 설명합니다.

카탈로그는 상품, 매장, 콘텐츠처럼 여러 메시지에서 반복해서 사용하는 **참조 데이터**를 프로젝트에 저장하는 기능입니다. 유저 속성에 같은 정보를 복제하거나 메시지를 만들 때마다 외부 API를 호출하지 않고, 아이템 ID로 필요한 값을 조회해 메시지에 넣을 수 있습니다.

유저 데이터가 이름, 등급, 최근 구매일처럼 특정 유저에게 속한 정보라면, 카탈로그 데이터는 상품명, 가격, 이미지 URL처럼 여러 유저가 함께 참조하는 정보입니다. 이벤트에는 상품 ID만 기록하고 세부 정보는 카탈로그에서 관리하면, 메시지를 보낼 때 가장 최근에 반영된 값을 사용할 수 있습니다.

<Note>
  콘솔의 카탈로그 메뉴는 Pro Plan 이상을 이용하는 프로덕트에만 제공됩니다.
</Note>

## 데이터 소스 선택하기

카탈로그를 만들 때 데이터 관리 방식에 맞는 소스를 선택합니다.

* **Google 스프레드시트**: 운영자가 시트에서 데이터를 관리하고, 노티플라이가 전체 범위를 정해진 일정에 맞춰 동기화합니다. 모든 필드는 문자열로 저장됩니다.
* **API**: 서버에서 Catalog REST API로 스키마와 아이템을 직접 관리합니다. `string`, `number`, `boolean`, `time`, `geo`, `object`, `array` 타입을 사용할 수 있습니다.

<Warning>
  카탈로그에는 상품 정보처럼 개인을 식별하지 않는 참조 데이터만 저장하세요. 이름, 이메일, 전화번호, 외부 유저 ID, 프로필 값 등 개인정보를 Google 스프레드시트나 API 아이템에 포함하지 마세요. 노티플라이는 소스 값을 자동으로 분류하거나 가리지 않습니다. API로 카탈로그를 만들 때는 `data_classification`을 `non_personal`로 선언해야 합니다.
</Warning>

## Google 스프레드시트 연결하기

### 1. 시트 준비하기

첫 번째로 값이 있는 행을 헤더로 사용합니다.

```csv theme={null}
id,name,price,category,image_url
sku-001,에브리데이 백팩,59000,bag,https://example.com/sku-001.png
sku-002,라이트 스니커즈,79000,shoes,https://example.com/sku-002.png
```

다음 규칙을 지켜 주세요.

* 첫 번째 열 이름은 반드시 `id`여야 합니다.
* 헤더는 비어 있거나 중복될 수 없으며 최대 250자입니다.
* `id`와 헤더 이름은 영문 대소문자, 숫자, 하이픈(`-`), 밑줄(`_`)만 사용할 수 있습니다.
* 아이템의 `id`는 최대 250자이며 카탈로그 안에서 고유해야 합니다. 상품 정보가 바뀌어도 유지되는 내부 식별자를 사용하는 것이 좋습니다.
* 데이터가 있는 행에서 `id`가 비어 있거나 중복되면 동기화에 실패합니다.
* 완전히 빈 행은 건너뜁니다. 헤더보다 값이 많은 행은 동기화 오류로 처리합니다.
* Google 스프레드시트에 표시되는 값이 문자열로 저장됩니다. 가격과 날짜의 표시 형식을 먼저 확인하고, 숫자 비교나 배열 필드가 필요하면 API 카탈로그를 사용하세요.

### 2. 카탈로그 만들기

1. 콘솔에서 **데이터 > 카탈로그**를 엽니다.
2. **카탈로그 만들기**를 누르고 데이터 소스로 **Google Sheets**를 선택합니다.
3. 카탈로그 이름을 입력합니다. 프로젝트 안에서 고유한 이름을 사용하고, 메시지의 Liquid에서 그대로 참조할 수 있도록 `products`처럼 짧고 의미가 분명한 이름을 권장합니다. 이름은 250자 이하의 영문·숫자·한글·하이픈·밑줄을 사용할 수 있습니다.
4. 스프레드시트 ID를 입력합니다. 특정 탭이나 범위만 가져오려면 **시트 탭 이름** 또는 **A1 범위**를 입력합니다. 둘 다 입력할 때는 같은 탭을 가리켜야 합니다. 둘 다 비우면 첫 번째 탭 전체를 읽습니다.
5. Google Sheets의 공유 메뉴에서 `catalog-reader@notifly.tech`에 **뷰어** 권한을 부여합니다. 편집 권한은 필요하지 않습니다.
6. 동기화 일정과 기준 시간대를 선택한 뒤 카탈로그를 만듭니다. 생성하면 첫 동기화가 바로 시작됩니다.

<Frame>
  <img src="https://mintcdn.com/notifly/-uyuxNzFWzUBXxbV/images/user-guide/catalogs/google-sheets-create.jpg?fit=max&auto=format&n=-uyuxNzFWzUBXxbV&q=85&s=cbef85898f51f52c9e12f6c18a61099b" alt="Google Sheets 카탈로그 생성 화면" width="1110" height="295" data-path="images/user-guide/catalogs/google-sheets-create.jpg" />
</Frame>

스프레드시트 ID는 URL의 `/d/`와 `/edit` 사이 값입니다.

```text theme={null}
https://docs.google.com/spreadsheets/d/{SPREADSHEET_ID}/edit
```

예를 들어 `https://docs.google.com/spreadsheets/d/1AbCdEfGhIjKlMnOpQrStUvWxYz/edit`의 스프레드시트 ID는 `1AbCdEfGhIjKlMnOpQrStUvWxYz`입니다. A1 범위는 `A1:Z50000` 또는 `Products!A1:Z50000`처럼 입력합니다.

### 3. 동기화 일정 정하기

반복하지 않거나, 15분·30분 간격, 매시간, 매일, 매주, 매월 중 하나를 선택할 수 있습니다. 일정은 선택한 시간대의 시각을 기준으로 실행됩니다. 시각의 분은 `00`·`15`·`30`·`45` 중에서 선택하며, 매월 일정은 1일부터 28일까지 지원합니다.

* 생성 직후 첫 동기화 및 **지금 동기화**는 정기 일정에 영향을 미치지 않습니다.
* 데이터 소스를 수정하면 상태가 `동기화 대기`로 바뀝니다. 다음 정기 동기화 또는 **지금 동기화** 후 새 설정이 적용됩니다.
* 변경된 내용이 없으면 동기화 기록은 `변경 없음`으로 남습니다.
* 동기화가 실패하면 마지막으로 성공한 버전을 계속 사용합니다. 권한, 헤더, 행 형식을 수정한 뒤 다시 동기화하세요.
* 비활성화한 카탈로그는 자동·수동 동기화와 메시지 조회에서 제외됩니다.

### 4. 결과 확인하고 정리하기

동기화가 끝나면 카탈로그 상세에서 다음 순서로 확인합니다.

1. **상태**와 활성 행 수를 확인합니다.
2. **내용 미리보기**에서 ID와 주요 필드가 원본 시트와 같은지 페이지별로 확인합니다.
3. **동기화 기록**에서 실행 방식, 시작·완료 시각, 행 수, 크기, 성공·변경 없음·실패 상태를 확인합니다.
4. 실패했다면 공유 권한, 첫 번째 `id` 열, 빈·중복 헤더, 잘못된 행을 확인하고 고친 뒤 **지금 동기화**를 다시 실행합니다.

<Frame>
  <img src="https://mintcdn.com/notifly/-uyuxNzFWzUBXxbV/images/user-guide/catalogs/google-sheets-detail.jpg?fit=max&auto=format&n=-uyuxNzFWzUBXxbV&q=85&s=db34aa908590e6534d1652d1d500d6d6" alt="Google Sheets 카탈로그 상세와 동기화 기록" width="1657" height="856" data-path="images/user-guide/catalogs/google-sheets-detail.jpg" />
</Frame>

최근 동기화가 실패해도 이전에 성공한 버전이 있으면 그 데이터를 계속 메시지 개인화에 사용합니다.

### 5. 연결 정보와 이름 수정하기

카탈로그 상세 화면에서 **수정**을 누르면 이름, 스프레드시트 ID, 시트 탭, A1 범위, 동기화 일정을 바꿀 수 있습니다.

* 데이터 원본이나 범위를 바꿨다면 저장한 뒤 동기화 결과를 다시 확인하세요.
* 카탈로그 이름을 바꾸면 기존 캠페인과 유저 여정의 `catalog_items`·`catalog_selection_items` 태그도 새 이름으로 수정해야 합니다.
* 데이터소스의 행이나 값을 바꿔도 캠페인과 유저 여정이 자동으로 시작되지는 않습니다. 카탈로그를 이용한 유저 여정 혹은 캠페인을 세팅해야 합니다.

### 6. 비활성화하거나 삭제하기

Google 스프레드시트 카탈로그의 **비활성화**와 **영구 삭제**는 다릅니다.

* **비활성화**: Liquid 조회와 자동·수동 동기화를 중지하지만 카탈로그 데이터와 동기화 기록은 유지합니다. 비활성화 후 콘솔에서는 영구 삭제만 진행할 수 있으므로 먼저 참조 중인 메시지가 없는지 확인하세요.
* **영구 삭제**: 아이템과 동기화 기록을 데이터베이스에서 삭제하며 복구할 수 없습니다.

## API 카탈로그 만들기

API 카탈로그는 콘솔에서 데이터 소스로 **API**를 선택해 `id:string` 필드만 있는 빈 카탈로그를 만든 뒤 REST API로 필드와 아이템을 등록하거나, REST API로 스키마까지 한 번에 만들 수 있습니다.

API로 만들 때는 [API 인증 토큰](/ko/api-reference/getting-started)을 발급받고 다음 요청을 보냅니다.

```bash theme={null}
curl --request POST \
  --url "https://api.notifly.tech/v1/projects/${PROJECT_ID}/catalogs" \
  --header "Authorization: Bearer ${NOTIFLY_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{
    "catalogs": [
      {
        "name": "products",
        "description": "메시지 개인화용 상품 정보",
        "data_classification": "non_personal",
        "fields": [
          { "name": "id", "type": "string" },
          { "name": "name", "type": "string" },
          { "name": "price", "type": "number" },
          { "name": "tags", "type": "array" },
          { "name": "released_at", "type": "time" }
        ]
      }
    ]
  }'
```

첫 번째 필드는 항상 `{ "name": "id", "type": "string" }`이어야 합니다. 카탈로그를 만든 뒤 아이템은 한 번에 최대 50개씩 등록할 수 있습니다.

```bash theme={null}
curl --request POST \
  --url "https://api.notifly.tech/v1/projects/${PROJECT_ID}/catalogs/products/items" \
  --header "Authorization: Bearer ${NOTIFLY_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{
    "items": [
      {
        "id": "sku-001",
        "name": "에브리데이 백팩",
        "price": 59000,
        "tags": ["bag", "new"],
        "released_at": "2026-08-01T09:00:00+09:00"
      }
    ]
  }'
```

REST API로는 API 소스이면서 활성 상태인 카탈로그만 조회·변경할 수 있습니다. Google 스프레드시트 카탈로그는 콘솔에서 관리하세요.

API 카탈로그의 콘솔 화면은 생성·목록·상세·아이템 미리보기와 REST 사용 예시를 제공합니다. 아이템·필드·셀렉션 변경과 카탈로그 영구 삭제는 REST API에서 수행해야 하며, 콘솔에서는 수정·동기화·비활성화·삭제할 수 없습니다.

## 메시지에서 아이템 사용하기

메시지 에디터에서 `catalog_items` 태그에 카탈로그 이름과 아이템 ID를 차례로 전달합니다. 조회 결과는 `items` 배열에 저장됩니다.

<Note>
  **지원 범위**

  예약·이벤트 기반 캠페인과 유저 여정에서는 앱/웹 푸시, 문자, 카카오 알림톡, 카카오 브랜드메시지, 웹훅에서 사용할 수 있습니다.

  채널과 관계없이 API 기반 발송 캠페인은 지원하지 않습니다. 이 경로에서는 카탈로그를 조회하지 않아 `items`가 빈 배열로 처리됩니다. API 직접 발송도 카탈로그를 조회하지 않으므로, 호출하기 전에 필요한 값을 직접 조회해 메시지에 넣어야 합니다.
</Note>

### 이벤트 기반 캠페인

이벤트 파라미터에 상품 ID가 들어 있다면 `event`에서 읽을 수 있습니다.

```json theme={null}
{
  "item_id": "sku-001"
}
```

```liquid theme={null}
{% catalog_items products {{ event["item_id"] }} %}
{% if items.size > 0 %}
  {{ items[0].name }} 상품을 다시 확인해 보세요.
  현재 가격은 {{ items[0].price }}원입니다.
{% endif %}
```

이벤트 기반 캠페인에서는 `event`를 사용합니다.

### 유저 여정

이벤트로 시작한 유저 여정에서는 진입 이벤트의 파라미터를 `entry_event`에서 읽습니다.

```liquid theme={null}
{% catalog_items products {{ entry_event["item_id"] }} %}
{% if items.size > 0 %}
  {{ items[0].name }}의 현재 가격은 {{ items[0].price }}원입니다.
{% endif %}
```

### 여러 항목 조회하기

여러 ID를 공백으로 구분하면 한 번에 조회할 수 있습니다. 유저 속성이나 이벤트 파라미터도 ID 자리에 넣을 수 있습니다.

```liquid theme={null}
{% catalog_items products {{ event["primary_item_id"] }} {{ event["secondary_item_id"] }} %}
{% for item in items %}
  - {{ item.name }}: {{ item.price }}원
{% endfor %}
```

존재하는 아이템은 요청한 ID 순서대로 저장됩니다. 찾지 못한 ID는 결과에서 빠져 뒤 아이템의 배열 번호가 앞당겨질 수 있고, 같은 ID를 여러 번 요청하면 한 번만 들어갑니다. 항상 `items.size`를 확인하거나 `for`로 순회한 뒤 값을 사용하세요. 조회 결과가 비어 있으면 대체 문구를 보여주거나, 채널에서 지원하는 경우 `abort_message`로 발송을 중단할 수 있습니다.

같은 메시지에서 카탈로그 태그를 다시 호출하면 `items` 배열이 새 결과로 바뀝니다.

카탈로그 필드 안에 Liquid 문법이 들어 있고 그 값까지 다시 렌더링해야 한다면 `:rerender` 옵션을 사용합니다.

```liquid theme={null}
{% catalog_items products sku-001 :rerender %}
{{ items[0].description }}
```

재귀적인 카탈로그 호출은 허용되지 않습니다. 신뢰할 수 있는 템플릿 값에만 `:rerender`를 사용하세요.

## 셀렉션 사용하기

API 카탈로그에서는 필터·정렬·개수 조건으로 셀렉션을 만들 수 있습니다. 셀렉션은 최대 30개, 셀렉션마다 필터는 최대 4개, 결과는 최대 50개입니다.

필드 타입별로 사용할 수 있는 연산자가 정해져 있습니다. `string`은 `equals`·`does not equal`, `number`는 두 연산자와 `greater than`·`less than`, `boolean`은 `is`, `time`은 `before`·`after`, `array`는 `includes value`·`does not include value`를 지원합니다. `geo`와 `object`는 필터링할 수 없고 필터 값에 `null`을 사용할 수 없습니다.

```bash theme={null}
curl --request POST \
  --url "https://api.notifly.tech/v1/projects/${PROJECT_ID}/catalogs/products/selections" \
  --header "Authorization: Bearer ${NOTIFLY_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{
    "selection": {
      "name": "new-bags",
      "description": "신상품 가방",
      "filters": [
        { "field": "tags", "operator": "includes value", "value": "new" }
      ],
      "results_limit": 10,
      "sort_field": "price",
      "sort_order": "asc"
    }
  }'
```

메시지에서는 다음처럼 조회합니다.

```liquid theme={null}
{% catalog_selection_items products new-bags %}
{% for item in items %}
  {{ item.name }} - {{ item.price }}원
{% endfor %}
```

아이템이나 필드를 수정하면 저장된 셀렉션 결과도 함께 다시 계산됩니다. 셀렉션에서 사용 중인 필드는 먼저 셀렉션을 삭제하기 전까지 삭제할 수 없습니다.

## 문제 해결

<AccordionGroup>
  <Accordion title="Google Sheets 권한 오류가 표시됩니다.">
    공유 설정에서 `catalog-reader@notifly.tech`에 뷰어 권한이 있는지 확인하세요. 스프레드시트 ID와 시트 탭 이름도 함께 확인합니다.
  </Accordion>

  <Accordion title="데이터 검증 오류가 표시됩니다.">
    첫 번째 열 이름이 소문자 `id`인지, 헤더가 비어 있거나 중복되지 않았는지 확인하세요. 각 행의 `id`와 헤더에는 영문·숫자·하이픈·밑줄만 사용할 수 있으며, 데이터 행의 열 수가 헤더보다 많아도 동기화에 실패합니다.
  </Accordion>

  <Accordion title="시트를 수정했는데 메시지 값이 바뀌지 않습니다.">
    시트를 수정해도 메시지 값이 즉시 바뀌지는 않습니다. 다음 정기 동기화를 기다리거나 **지금 동기화**를 실행한 뒤, 상세 화면의 최근 동기화 시각과 **내용 미리보기**를 확인하세요.
  </Accordion>

  <Accordion title="재입고나 가격 인하 메시지가 자동으로 발송되나요?">
    카탈로그 값이 바뀌어도 발송은 시작되지 않습니다. 재입고나 가격 인하 이벤트를 노티플라이로 보내고, 해당 이벤트를 캠페인 또는 유저 여정의 시작 조건으로 설정하세요. 카탈로그에서는 메시지에 표시할 상품명, 가격, 링크 등을 조회합니다.
  </Accordion>
</AccordionGroup>

## 주요 제한

* 프로젝트당 활성 카탈로그: 최대 20개
* Google 스프레드시트 카탈로그 필드: `id` 제외 최대 1,000개
* API 카탈로그 필드: `id` 포함 최대 500개
* 필드 추가 요청: 한 번에 최대 50개
* 아이템 생성·수정·삭제 요청: 한 번에 최대 50개
* 아이템 한 개의 저장 크기: 최대 1 MiB
* 카탈로그 한 개의 저장 크기: 최대 2 GiB
* 프로젝트의 활성 카탈로그 합계: 최대 15 GiB
* 카탈로그·필드·아이템 ID: 최대 250자
* 문자열: 최대 5,000자
* 배열: 문자열 최대 100개
* `object`·`array` 중첩: 최대 50단계, object key에는 `.` 또는 `$` 사용 불가
* Google 스프레드시트 동기화 소스: 1회 최대 100 MiB
* API JSON 요청 본문: 최대 4 MiB

API의 전체 경로, 요청·응답 스키마, 상태 코드는 [Catalog REST API 시작하기](/ko/api-reference/catalogs/getting-started)와 **API Reference > 카탈로그**에서 확인할 수 있습니다. Braze에서 옮기는 경우 [Braze Catalog에서 마이그레이션하기](/ko/developer-guide/migration/braze-catalogs)를 참고하세요.
