# 와이글 사이트빌드 — AI 사이트 제작 정의서 v2.0

> Claude에게 이 문서를 전달하고, 사이트 제목과 성격만 알려주면  
> 가져오기(import)로 즉시 사용 가능한 완성된 사이트 ZIP 파일을 받을 수 있습니다.

---

## 1. Claude와의 대화 흐름

### 1단계 — 기본 정보 수집

Claude가 먼저 다음만 묻습니다:

| 질문 | 예시 |
|------|------|
| 사이트 이름(영문 식별자) | `soulcafe`, `greenlab`, `myshop` |
| 사이트 제목 | 소울카페 로스터리 |
| 한 줄 소개 | 수제 커피를 직접 로스팅하는 스페셜티 카페 |
| 주요 색상 분위기 | 따뜻한 에스프레소 브라운 톤 |

나머지(메뉴 구성·상세 페이지 내용·이미지 디자인 등)는 **사이트 주제와 성격에 맞춰 Claude가 알아서 결정**합니다. 사용자가 원하는 내용을 추가로 말하면 그대로 반영합니다.

#### 메뉴 구성 규칙 (Claude 자동 결정)

- 상세 페이지가 연결된 메뉴를 **4개 내외** 구성 (사이트 주제에 맞춰 Claude가 선정)
- **마지막 메뉴는 반드시 "커뮤니티"** — 기본 게시판 4개를 서브메뉴로 구성

```json
{
  "title": "커뮤니티",
  "link": "#",
  "isProtected": true,
  "nav": [
    { "title": "공지사항",   "link": "/board/notice/list", "boardName": "notice" },
    { "title": "뉴스&소식", "link": "/board/news/list",   "boardName": "news"   },
    { "title": "포토갤러리","link": "/board/photo/list",  "boardName": "photo"  },
    { "title": "게시판","link": "/board/free/list",   "boardName": "free"   }
  ]
}
```

### 2단계 — Claude의 작업 순서

```
① 색상 팔레트 결정 (주색·보조색·배경색)
② 메뉴별 상세페이지 HTML 작성
③ 슬라이드·위젯·배너·버튼 이미지 생성 (JPG 사진 또는 SVG)
④ 로고 SVG 생성
⑤ 모든 파일을 ZIP으로 패키징
⑥ ZIP 파일 전달
```

### 3단계 — 사용자 작업

```
① 사이트빌드에서 빈 사이트 생성 (이름만 정하면 됨)
② 해당 사이트 편집 → 가져오기 → ZIP 업로드
③ 미리보기 확인 → "확인, 교체합니다" 클릭 → 완성
```

> ZIP 안의 이미지 경로는 가져오기 시 현재 사이트 이름으로 자동 치환됩니다.  
> 동봉된 예제 ZIP 파일들도 동일하게 빈 사이트에 가져오기하면 즉시 완성된 사이트가 만들어집니다.

---

## 2. ZIP 파일 구조

```
{sitename}_사이트.zip
├── options.json              ← 메타 정보 (필수)
├── site.json                 ← 사이트 전체 레이아웃 (필수)
├── pages.json                ← 상세 페이지 목록 (필수)
├── boards.json               ← 게시판 구조 (필수)
└── cloud/
    ├── layout/               ← 사이트 이미지 (JPG 또는 SVG)
    │   ├── logo.svg
    │   ├── slide-1.jpg  ~  slide-N.jpg   (또는 .svg)
    │   ├── widget-1.jpg  ~  widget-N.jpg (또는 .svg)
    │   ├── banner-1.svg  ~  banner-N.svg (또는 .jpg)
    │   └── button-1.svg  ~  button-N.svg (또는 .jpg)
    ├── page/                 ← 빈 폴더 (페이지 이미지용)
    └── board/                ← 빈 폴더 (게시글 이미지용)
```

---

## 3. 파일 명세

### 3-1. options.json

```json
{
  "_exportVersion": 2,
  "_exportedAt": "2026-04-19T00:00:00.000Z",
  "_sourceSite": "사이트_식별자",
  "includes": {
    "layout": true,
    "pages": true,
    "boards": true,
    "posts": false
  },
  "counts": {
    "pages": 5,
    "boards": 4,
    "posts": 0,
    "cloudLayout": 16,
    "cloudPage": 0,
    "cloudBoard": 0
  }
}
```

### 3-2. site.json 타입 정의

```typescript
type Site = {
  name: string      // URL-safe 식별자 (영문 소문자·숫자·하이픈만, 생성 후 불변)
  title: string     // 사이트 표시 제목
  state: "ok"       // 항상 "ok"
  site: string      // name과 동일한 값
  creatorId: ""     // 빈 문자열
  createdAt: ""     // 빈 문자열
  logo: {
    text: string    // 텍스트 로고 (이미지 없을 때 헤더에 표시)
    image: string   // "/cloud/{site}/layout/logo.svg"
    link: string    // "/{site}" 또는 "/"
  }
  nav: Nav[]
  slide: Slide[]    // 3~5장 권장
  widget: Widget[]  // 1~16개 지원 (3열·4열 조합으로 자동 배치)
  button: Button[]  // 4·8개 권장
  banner: Banner[]
  info: {
    text: string    // 푸터 텍스트 (\n으로 줄바꿈)
    image: string   // 빈 문자열 "" 권장
  }
}

type Nav = {
  title: string          // 메뉴 표시명
  link: string           // 아래 링크 유형 참고
  target?: "_blank"      // 새 탭으로 열 때만 추가
  isProtected?: boolean  // true: 삭제 불가 (커뮤니티 메뉴 등)
  boardName?: string     // 게시판 메뉴일 때 게시판 name 값
  nav?: Nav[]            // 서브메뉴 (1단계만 지원)
}

// 링크 유형
// - 상세 페이지: "/page/{pages.json의 _originalId}"
// - 게시판:     "/board/{boardName}/list"
// - 서브메뉴:  "#"
// - 외부 URL:  "https://..."

type Slide = {
  title?: string  // 슬라이드 제목
  memo?: string   // 부제목·설명
  image: string   // "/cloud/{site}/layout/slide-N.jpg" (권장: 1920×640px)
  link: string    // 클릭 시 이동 링크
  target?: "_blank"
}

type Widget = {
  title: string   // 카드 제목
  memo?: string   // 카드 설명
  image: string   // "/cloud/{site}/layout/widget-N.jpg" (권장: 960×540px)
  link: string
  target?: "_blank"
}

type Button = {
  title?: string  // 버튼 라벨
  memo?: string
  image: string   // "/cloud/{site}/layout/button-N.svg" (권장: 360×120px, 3:1)
  link: string
  target?: "_blank"
}

type Banner = {
  title?: string
  memo?: string
  image: string   // "/cloud/{site}/layout/banner-N.svg" (권장: 1920×320px)
  link: string
  target?: "_blank"
}

```

### 3-3. pages.json 타입 정의

```typescript
type Page = {
  _originalId: string  // Claude가 생성한 16자리 hex (예: "a3f8b2c1d4e56789")
  site: string         // 사이트 식별자 (name과 동일)
  title: string        // 페이지 제목
  content: string      // HTML 본문
  creatorId: ""
  createdAt: "ISO 날짜 문자열"
}
```

> **중요**: nav에서 이 페이지를 링크할 때 반드시 `/page/{_originalId}` 형식 사용.  
> import 시스템이 `_originalId` → 새 MongoDB `_id`로 교체하고 nav 링크도 함께 업데이트합니다.

### 3-4. boards.json 타입 정의

```typescript
type Board = {
  _originalId: string     // 임의 ID (예: "b001")
  name: string            // URL-safe 식별자 (변경 불가)
  title: string           // 표시명
  site: string
  isDefault?: boolean     // 기본 게시판이면 true
  adminOnly?: boolean     // true: 관리자만 작성
  creatorId: ""
  createdAt: "ISO 날짜 문자열"
}
```

**기본 게시판 4개는 반드시 포함** (이름 변경 가능, name은 고정):

| name | title 예시 | adminOnly |
|------|-----------|-----------|
| `notice` | 공지사항 | true |
| `news` | 뉴스&소식 | true |
| `photo` | 포토갤러리 | false |
| `free` | 게시판 | false |

---

## 4. 링크 연결 규칙

### 슬라이드 · 배너 링크
- **link는 빈 문자열 `""`로 설정** — 클릭해도 이동하지 않음 (이미지 감상용)
- 사용자가 필요 시 사이트 편집 화면에서 직접 링크를 추가하면 됨

```json
{ "link": "" }  ← 슬라이드/배너 기본값
```

### 위젯 링크
- **반드시 pages.json에 존재하는 `_originalId`를 참조하는 `/page/{id}` 형식 사용**
- 각 위젯이 관련된 페이지로 연결되도록 구성
- 게시판으로 연결할 경우: `/board/{boardName}/list`

```json
{ "link": "/page/a3f8b2c1d4e56789" }  ← 위젯 링크 예시
```

> 가져오기 시 `/page/{_originalId}` → `/page/{새ID}`로 자동 치환됩니다.

### 버튼 링크
- **외부 URL**: `https://...` 형식, 반드시 `"target": "_blank"` 추가 (새 탭으로 열기)
- **내부 페이지**: `/page/{_originalId}` 형식 (가져오기 시 자동 치환)
- **게시판**: `/board/{boardName}/list` 형식

```json
{ "link": "https://instagram.com/mycafe", "target": "_blank" }  ← 외부 링크
{ "link": "/page/a3f8b2c1d4e56789" }                           ← 내부 페이지
```

> **빈 링크 동작**: `link: ""` 이면 클릭해도 현재 페이지에 유지됩니다 (404 없음).

---

## 5. Nav ↔ Pages 연결 규칙 (매우 중요)

```
pages.json                               site.json nav
───────────────────────────────          ─────────────────────────────────────
{ "_originalId": "a3f8b2c1d4e56789",  ←→  { "title": "소개", 
  "title": "소개", ... }                     "link": "/page/a3f8b2c1d4e56789" }
```

`_originalId`는 Claude가 임의로 생성하는 16자리 hex 문자열입니다.  
nav의 `/page/{id}` 값이 `_originalId`와 정확히 일치해야 페이지가 연결됩니다.

---

## 6. 이미지 제작 지침

사이트 분위기에 따라 **JPG 사진**과 **SVG 일러스트**를 조합하여 사용합니다.

### 이미지 종류별 명세

| 종류 | 파일명 | 권장 크기 | 형식 | 용도 |
|------|--------|---------|------|------|
| 로고 | logo.svg | 360×100px | SVG | 헤더 브랜드 로고 |
| 슬라이드 | slide-N.jpg/.svg | 1920×640px | JPG 권장 | 메인 비주얼 (3~5장) |
| 위젯 | widget-N.jpg/.svg | 960×540px | JPG 권장 | 카드 썸네일 |
| 배너 | banner-N.svg/.jpg | 1920×320px | SVG 권장 | 전면 광고 배너 |
| 버튼 | button-N.svg | 360×120px (3:1) | SVG 권장 | 바로가기 버튼 |

### JPG 사진 활용 (슬라이드·위젯)

사진 느낌이 필요한 슬라이드와 위젯은 [Unsplash](https://unsplash.com) 무료 사진을 활용합니다.  
Unsplash License: 상업적 이용 가능, 출처 표기 불필요, 사진 재판매 불가.

**Unsplash 다운로드 URL 형식:**
```
https://images.unsplash.com/{photo-id}?w={width}&h={height}&fit=crop&q=78
```

예시:
```
# 슬라이드 (1920×640)
https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=1920&h=640&fit=crop&q=78

# 위젯 (960×540)
https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=960&h=540&fit=crop&q=78
```

Python으로 다운로드:
```python
import urllib.request
HEADERS = {'User-Agent': 'Mozilla/5.0'}
def dl(photo_id, w, h, q=78):
    url = f'https://images.unsplash.com/{photo_id}?w={w}&h={h}&fit=crop&q={q}'
    req = urllib.request.Request(url, headers=HEADERS)
    with urllib.request.urlopen(req, timeout=20) as r:
        return r.read()
```

### SVG 활용 (로고·버튼·배너)

로고, 버튼, 배너는 SVG로 직접 생성합니다. SVG는 텍스트 코드로 완전히 출력 가능하고, 벡터 기반으로 어떤 해상도에서도 선명합니다.

### SVG 제작 원칙

1. **색상 팔레트 먼저** — 주색·보조색·포인트색을 확정한 뒤 모든 이미지에 일관 적용
2. **그라디언트 필수** — `<linearGradient>` / `<radialGradient>`로 깊이감 표현
3. **텍스트 삽입** — 이미지 안에 핵심 메시지를 직접 포함
4. **viewBox만 사용** — `width`/`height` 속성 대신 `viewBox`로 반응형 보장
5. **한글 폰트 스택** — `font-family="'Apple SD Gothic Neo','Malgun Gothic','Noto Sans KR',sans-serif"`
6. **외부 참조 금지** — `<image href="...">` 사용 금지

### 이미지 디자인 패턴

**슬라이드 (1920×640) — JPG 사진 권장**
- Unsplash에서 사이트 분위기에 맞는 사진 선택
- 가로로 넓은 구도, 1920×640 crop

**위젯 (960×540) — JPG 사진 권장**
- 각 위젯 주제에 맞는 사진 선택
- 960×540 crop

**배너 (1920×320) — SVG 권장**
- 좌우 대칭 그라디언트
- 중앙 정렬 제목·부제목
- 상하 포인트 라인

**버튼 (360×120, 3:1) — SVG 권장**
- 그라디언트 또는 단색 배경 + 둥근 모서리(rx="12")
- 왼쪽 60%에 아이콘 영역 (세로 구분선 포함)
- 오른쪽 60%에 라벨·부제목 텍스트
- 예시 구조:
  ```svg
  <svg viewBox="0 0 360 120">
    <rect width="360" height="120" fill="url(#bg)" rx="12"/>
    <!-- 왼쪽 아이콘 (x≈72 중심) -->
    <line x1="126" y1="18" x2="126" y2="102" stroke="#FFF" stroke-width="1" opacity=".2"/>
    <!-- 오른쪽 텍스트 (x=243 중심) -->
    <text x="243" y="50" text-anchor="middle" font-size="20" font-weight="700" fill="#FFF">라벨</text>
    <text x="243" y="74" text-anchor="middle" font-size="12" fill="#FFF" opacity=".7">부제목</text>
  </svg>
  ```

**로고 (360×100) — SVG**
- 흰색 배경 (`fill="#ffffff"`) — 헤더 배경색과 일치
- 왼쪽에 브랜드 심볼 아이콘
- 오른쪽에 브랜드명(serif 폰트)·슬로건(소형)
- 얇은 포인트 라인으로 구분

---

## 7. 페이지 HTML 작성 지침

페이지 content 필드에 들어가는 HTML입니다. 브라우저에서 `dangerouslySetInnerHTML`로 렌더링됩니다.

### 권장 구조

```html
<div style="max-width:860px;margin:0 auto;padding:48px 24px;
    font-family:'Apple SD Gothic Neo','Malgun Gothic','Noto Sans KR',sans-serif;color:#222;">

  <!-- 페이지 타이틀 영역 -->
  <div style="margin-bottom:40px;padding-bottom:24px;border-bottom:2px solid {주색};">
    <h1 style="font-size:2rem;font-weight:700;margin:0 0 8px;">페이지 제목</h1>
    <p style="color:{보조색};font-size:1rem;margin:0;">부제목 또는 한 줄 소개</p>
  </div>

  <!-- 섹션들 -->
  <section style="margin-bottom:44px;">
    <h2 style="font-size:1.35rem;font-weight:700;margin-bottom:16px;">섹션 제목</h2>
    <!-- 내용 -->
  </section>

</div>
```

### HTML 작성 원칙

- **인라인 스타일만 사용** (외부 CSS 클래스 사용 불가)
- 이모지 적극 활용으로 시각적 포인트 추가
- 카드/그리드 레이아웃으로 정보 구조화
- `display:grid` / `display:flex` 활용

---

## 8. 권장 구성값

| 항목 | 기본 권장 | 조정 가능 범위 |
|------|----------|--------------|
| 메뉴 + 상세 페이지 | 5개 | 3~8개 |
| 슬라이드 | 3장 | 1~5장 |
| 위젯 | 7개 | 1~16개 자유 선택 (3열+4열 자동 배치) |
| 버튼 | 4개 | 1~8개 (1~3개=3열, 4개=4열, 5개=5열, 6개+=행 반복) |
| 배너 | 2개 | 1~3개 |
| 게시판 | 4개 (기본) | 4개 + 추가 가능 |

> 💡 위젯은 3열·4열 조합으로 자동 배치됩니다. **7개(3+4줄)** 가 시각적으로 가장 균형감 있습니다. 17개 이상은 렌더링되지 않으니 1~16개로 구성하세요.

---

## 9. 완성 체크리스트

ZIP을 만들기 전 Claude가 반드시 확인해야 할 항목:

- [ ] `options._exportVersion: 2` 확인
- [ ] `site.name` = `site.site` = `options._sourceSite` 모두 동일
- [ ] `site.state: "ok"` 확인
- [ ] 위젯 수가 1~16개 범위인지 확인 (17개 이상은 렌더링 안 됨)
- [ ] 모든 nav 링크가 존재하는 `_originalId` 또는 `boardName` 참조인지 확인
- [ ] 이미지 파일명과 site.json의 image 경로가 일치하는지 확인  
  (예: `cloud/layout/slide-1.jpg` ↔ `/cloud/{site}/layout/slide-1.jpg`)
- [ ] boards.json에 `notice`, `news`, `photo`, `free` 4개 포함 확인
- [ ] `site.json`에 `plate` 필드 **포함하지 않을 것** — 가져오기 시 빈 사이트 기본값(공지/뉴스/게시판) 자동 유지
- [ ] 슬라이드·배너 `link`가 `""` (빈 문자열)인지 확인
- [ ] 위젯 `link`가 유효한 `/page/{_originalId}` 또는 `/board/{name}/list`인지 확인
- [ ] 버튼 외부링크에 `"target": "_blank"` 추가 확인
- [ ] 로고 SVG에 `width`·`height` 속성 포함 확인 (예: `width="360" height="100"`)
- [ ] `cloud/page/`, `cloud/board/` 빈 폴더 포함 확인
- [ ] `options.counts.cloudLayout` = 실제 layout/ 파일 수와 일치 확인

---

## 10. 예제 파일

동봉된 예제 ZIP 파일 3개를 참고하세요. 모두 빈 사이트에 **가져오기**로 즉시 임포트할 수 있습니다.

| 파일 | 주제 | 특징 |
|------|------|------|
| `예제사이트1.zip` | 소울카페 로스터리 (스페셜티 카페) | 브라운/캐러멜 팔레트, 카페 분위기 사진 |
| `예제사이트2.zip` | TechCore (IT 기업) | 다크 네이비/블루 팔레트, 기술 분위기 사진 |
| `예제사이트3.zip` | 비전선교회 (기독교 선교단체) | 딥블루/골드 팔레트, 선교 분위기 사진 |

**공통 구성**: 메뉴 6개(상세페이지 5개+커뮤니티), 슬라이드 3장(JPG), 위젯 7개(JPG), 버튼 4개(SVG), 배너 2개(SVG), 로고(SVG)

---

## 11. 빠른 시작 프롬프트 예시

Claude에게 다음과 같이 전달하세요:

```
[사이트정의서.md 파일 첨부]
[예제사이트1.zip 파일 첨부]  ← 분위기에 맞는 예제 선택

위 정의서에 따라 아래 사이트의 ZIP 파일을 만들어줘.

- 사이트 식별자: greenlab
- 사이트 제목: 그린랩 도시농업센터
- 한 줄 소개: 도심 속 텃밭 체험과 친환경 농업 교육을 제공합니다
- 색상 분위기: 자연스러운 그린 계열
- 슬라이드 3장 (Unsplash 사진), 위젯 7개 (Unsplash 사진), 버튼 4개 (SVG), 배너 2개 (SVG)
```

> **Claude 내부 참고**: ZIP 생성은 Python `zipfile` 모듈을 사용합니다.  
> JPG 파일은 `urllib.request`로 Unsplash에서 다운로드하여 바이트로 zip에 추가합니다.  
> SVG는 `str.encode('utf-8')`로 바이트 변환 후 zip에 추가합니다.  
> 빈 폴더(`cloud/page/`, `cloud/board/`)는 `.gitkeep` 파일을 넣어 폴더가 ZIP에 포함되게 합니다.
