# Catalogs

카탈로그는 사이트가 소유하는 **이름 붙은 목록**입니다. 꽃 도감, 배지 세트, 대여 장비 목록처럼 항목 하나하나에 의미가 있고, 나중에 다른 기록이 그 항목을 가리키게 되는 자료가 카탈로그입니다.

회원이 "무엇을 가졌는지"([보유](/developers/holdings))를 기록하거나 그룹이 서로에게 표식을 남길 때, 그 대상이 되는 것이 카탈로그 항목입니다.

## 컬렉션과 무엇이 다른가

겉모습이 비슷해 헷갈리기 쉽습니다. 둘 다 컨테이너가 필드 스키마를 들고 항목이 자유로운 값을 담습니다. 그런데 규칙이 반대라서, 잘못 고르면 나중에 데이터를 옮겨야 합니다.

| | 카탈로그 | 컬렉션(ContentType) |
|---|---|---|
| 항목의 이름 | **유일해야 한다** (같은 이름의 활성 항목 둘은 불가) | 제약 없음 — 같은 제목 여럿 정상 |
| 삭제 | **없다.** 은퇴(retire)만 있고 ID 는 보존된다 | 하드 삭제가 정상 동작 |
| 다른 기록의 참조 | 보유·표식이 항목 ID 를 가리킨다 | 참조 관계 없음 |
| 쓰기 주체 | 정책으로 고른다 (운영자·회원·그룹원·임원) | 운영자 |

**고르는 기준 한 줄:** 다른 기록이 그 항목을 ID 로 가리키고, 항목이 사라진 뒤에도 그 기록이 남아야 하면 카탈로그입니다. 블로그 글이나 메뉴판처럼 그 자체로 완결되는 자료는 컬렉션입니다.

## 만들기

카탈로그 정의는 사이트 운영자가 합니다. CLI 또는 MCP 로 하며, 둘은 같은 백엔드를 씁니다.

```bash
npx @saeroon/cli catalog apply ./catalogs.json --site <siteId>
```

```json
{
  "catalogs": [
    {
      "key": "flowers",
      "name": "꽃 도감",
      "itemEditPolicy": "GroupMember",
      "itemRetirePolicy": "GroupLeader",
      "attributes": [
        { "key": "grade", "type": "Integer", "label": "등급", "min": 1, "max": 5 },
        { "key": "season", "type": "Select", "options": ["봄", "여름", "가을", "겨울"] }
      ]
    }
  ]
}
```

`key` 는 사이트 안에서 유일하며 **한 번 정하면 바꿀 수 없습니다**. 항목과 보유가 이 키로 카탈로그를 찾기 때문입니다.

고객의 AI 에이전트를 쓴다면 같은 일을 MCP 로 합니다.

```
saeroon_define_catalog { "key": "flowers", "name": "꽃 도감",
  "itemEditPolicy": "GroupMember",
  "attributes": [{ "key": "grade", "type": "Integer", "min": 1, "max": 5 }],
  "items": [{ "name": "장미", "attributes": { "grade": 3 } }] }
```

## 속성 스키마

`attributes` 는 항목이 담을 수 있는 값의 형태를 정합니다. 타입은 여덟 가지입니다.

`Text` · `Integer` · `Number` · `Boolean` · `Date` · `Select` · `MultiSelect` · `Url`

`Select` 와 `MultiSelect` 는 `options` 가 반드시 있어야 합니다. 선택지 없는 선택형은 어떤 값이든 통과시켜서, 제약을 걸어 둔 줄 알았는데 실제로는 아무것도 막지 않는 상태가 됩니다.

**`attributes` 를 생략하는 것과 빈 배열을 보내는 것은 다릅니다.**

- 키를 아예 안 넣으면: 기존 스키마를 건드리지 않습니다.
- `"attributes": []` 를 보내면: 스키마를 **비웁니다**.

이름만 고치려던 요청이 스키마를 날리지 않도록 둘을 구분해 두었습니다. 이름만 바꿀 때는 `attributes` 키를 빼세요.

## 누가 항목을 쓸 수 있나

`itemEditPolicy`(추가·수정)와 `itemRetirePolicy`(은퇴)를 따로 정합니다.

| 값 | 뜻 |
|---|---|
| `OperatorOnly` | 사이트 소유자와 운영자 키만. **기본값**입니다 — 명시하지 않으면 회원은 쓸 수 없습니다 |
| `SiteMember` | 로그인한 사이트 회원 누구나 |
| `GroupMember` | 활성 그룹의 활성 멤버 (직위 무관) |
| `GroupLeader` | 그중 임원 직위 보유자 또는 권한대행 |

`GroupMember` 와 `GroupLeader` 는 [그룹](/developers/groups) 정책이 먼저 있어야 의미가 생깁니다.

## 항목 넣기

항목은 매니페스트로 한꺼번에 넣습니다. 도감 수백 종을 처음 채울 때 쓰는 경로입니다.

```bash
npx @saeroon/cli catalog import flowers ./items.json --site <siteId>
```

```json
{
  "items": [
    { "name": "장미", "attributes": { "grade": 3, "season": "여름" }, "sortOrder": 1 },
    { "name": "튤립", "attributes": { "grade": 2, "season": "봄" }, "sortOrder": 2 }
  ]
}
```

`name` 이 자연키입니다. 같은 이름의 활성 항목이 있으면 갱신하고, 없으면 새로 만듭니다.

기존 항목을 내보내 그대로 되먹일 수 있습니다.

```bash
npx @saeroon/cli catalog items flowers --site <siteId> --manifest -o items.json
```

## 삭제 대신 은퇴

이 도메인에는 삭제가 없습니다. 카탈로그는 **보관**(archive), 항목은 **은퇴**(retire)합니다.

```bash
npx @saeroon/cli catalog retire flowers <itemId> --site <siteId>   # 목록에서 숨김
npx @saeroon/cli catalog restore flowers <itemId> --site <siteId>  # 되돌림
npx @saeroon/cli catalog archive flowers --site <siteId>           # 카탈로그째 숨김
```

지우지 않는 이유는 회원의 보유 기록과 그룹 표식이 항목 ID 를 가리키고 있기 때문입니다. 항목을 지우면 그 기록들이 가리킬 곳을 잃습니다. 은퇴한 항목은 공개 목록에서 빠지지만 이미 있던 보유는 그대로 남고, 응답에 `isRetired: true` 로 표시됩니다.

주의할 점이 하나 있습니다. **은퇴한 항목과 같은 이름을 다시 넣으면 그 항목이 되살아납니다.** 새 행이 생기는 것이 아니라 원래 ID 가 돌아오고, 그것을 가리키던 보유도 함께 돌아옵니다. `catalog import` 는 그런 경우를 출력에 따로 알려 줍니다.

`apply` 와 `import` 는 매니페스트에 없는 것을 건드리지 않습니다. 정리하려면 `--prune` 을 명시해야 합니다.

## 사이트에 붙이기

방문자에게 카탈로그를 보여 주는 것은 공개 읽기라 로그인이 필요 없습니다.

```html
<div data-saeroon-catalog-list data-catalog="flowers">
  <template>
    <li>
      <span data-saeroon-field="name"></span>
      <em data-saeroon-field="attr:grade"></em>
    </li>
  </template>
  <p data-saeroon-empty>항목이 없습니다.</p>
</div>
```

`<template>` 안이 항목 하나의 모양입니다. `data-saeroon-field` 는 항목의 필드를, `data-saeroon-field="attr:<키>"` 는 속성 값을 채웁니다. 템플릿이 없으면 아무것도 그리지 않습니다 — 임의의 DOM 을 만들어 넣어 디자인을 흐트러뜨리지 않기 위해서입니다.

배포할 때 `@saeroon/web-sdk` 가 자동으로 주입됩니다.

## 관련 문서

- [Holdings](/developers/holdings) — 회원이 카탈로그에서 가진 것을 기록하기
- [Groups](/developers/groups) — 그룹 정책과 직위, 그리고 그룹 기준 쓰기 권한
- [CLI](/developers/cli) — `catalog` 커맨드 전수
