# Phase 4: 콘텐츠 관리

**기간:** 1-2주
**우선순위:** 중간 (사용자 경험 향상)
**의존성:** Phase 1 (회원), Phase 2 (카테고리), Phase 3 (템플릿 참조)

## 📋 Phase 개요

사용자 경험 향상을 위한 콘텐츠 관리 기능을 구현합니다.

**포함 기능:**
1. 템플릿관리 (Template Management)
2. 게시판관리 (Board Management) - EAV 패턴
3. 설정 - 배너/팝업 관리

---

## 1️⃣ 템플릿관리 (Template Management)

### 기능 목록

#### 1.1 템플릿 목록 조회
- **경로:** `/mng/templates`
- **기능:**
  - 타입별 필터 (견적서, 계약서, 결재 문서)
  - 검색 (템플릿명)
  - 미리보기
- **권한:** `templates.index`

#### 1.2 템플릿 생성
- **경로:** `/mng/templates/create`
- **기능:**
  - 템플릿명, 타입 선택
  - HTML 에디터 (Tiptap 또는 TinyMCE)
  - 변수 치환 시스템 ({{company_name}}, {{date}} 등)
  - 미리보기 기능
- **권한:** `templates.create`

#### 1.3 템플릿 수정
- **경로:** `/mng/templates/{id}/edit`
- **기능:**
  - 내용 수정
  - 변수 추가/수정
  - 버전 관리 (히스토리)
- **권한:** `templates.update`

### DB 스키마

```sql
CREATE TABLE templates (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    name VARCHAR(255) NOT NULL COMMENT '템플릿명',
    type ENUM('quotation', 'contract', 'approval', 'email', 'custom') NOT NULL COMMENT '템플릿 타입',
    content TEXT NOT NULL COMMENT 'HTML 내용',
    variables JSON NULL COMMENT '사용 가능한 변수 목록',
    is_default BOOLEAN DEFAULT FALSE COMMENT '기본 템플릿 여부',
    version INT DEFAULT 1 COMMENT '버전',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant_id (tenant_id),
    INDEX idx_type (type),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

### Service 클래스

```php
// app/Services/TemplateService.php
class TemplateService
{
    public function list(array $filters): Collection;
    public function find(int $id): Template;
    public function create(array $data): Template;
    public function update(Template $template, array $data): Template;
    public function delete(Template $template): bool;
    public function render(Template $template, array $variables): string; // 변수 치환
    public function preview(Template $template, array $sampleData): string;
}
```

### 개발 체크리스트

- [ ] `Template` 모델 작성
- [ ] `TemplateService` 클래스 작성
- [ ] 변수 치환 로직 구현 (정규식)
- [ ] HTML 에디터 통합 (Tiptap)
- [ ] 미리보기 기능
- [ ] i18n 키 작성
- [ ] 테스트 작성

---

## 2️⃣ 게시판관리 (Board Management) - EAV 패턴

### 기능 목록

#### 2.1 게시판 목록 관리
- **경로:** `/mng/boards`
- **기능:**
  - 게시판 생성/수정/삭제
  - 게시판 설정 (공지사항, FAQ, 자료실 등)
  - 필드 구성 (EAV 동적 필드)
- **권한:** `boards.manage`

#### 2.2 게시물 목록 조회
- **경로:** `/mng/boards/{board}/posts`
- **기능:**
  - 페이지네이션
  - 검색 (제목, 내용, 작성자)
  - 필터 (카테고리, 날짜)
  - 정렬 (최신순, 조회순)
- **권한:** `boards.posts.index`

#### 2.3 게시물 상세 조회
- **경로:** `/mng/boards/{board}/posts/{id}`
- **기능:**
  - 제목, 내용, 첨부파일
  - 동적 필드 표시 (EAV)
  - 댓글 목록
  - 조회수 증가
- **권한:** `boards.posts.show`

#### 2.4 게시물 작성
- **경로:** `/mng/boards/{board}/posts/create`
- **기능:**
  - 제목, 내용 (에디터)
  - 카테고리 선택
  - 동적 필드 입력 (게시판별 설정)
  - 첨부파일 업로드
  - 공지사항 설정
- **권한:** `boards.posts.create`

### DB 스키마

```sql
-- 게시판 설정
CREATE TABLE board_settings (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    name VARCHAR(255) NOT NULL COMMENT '게시판명',
    slug VARCHAR(100) UNIQUE NOT NULL COMMENT 'URL 슬러그',
    description TEXT NULL,
    allow_comments BOOLEAN DEFAULT TRUE,
    allow_attachments BOOLEAN DEFAULT TRUE,
    require_approval BOOLEAN DEFAULT FALSE COMMENT '작성 시 승인 필요',
    custom_fields JSON NULL COMMENT '동적 필드 설정 (EAV)',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant_id (tenant_id),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 게시물
CREATE TABLE posts (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    board_id BIGINT UNSIGNED NOT NULL,
    category_id BIGINT UNSIGNED NULL,
    user_id BIGINT UNSIGNED NOT NULL COMMENT '작성자',
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    is_notice BOOLEAN DEFAULT FALSE,
    is_approved BOOLEAN DEFAULT TRUE,
    view_count INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant_id (tenant_id),
    INDEX idx_board_id (board_id),
    INDEX idx_category_id (category_id),
    INDEX idx_user_id (user_id),
    FULLTEXT idx_search (title, content),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    FOREIGN KEY (board_id) REFERENCES board_settings(id) ON DELETE CASCADE,
    FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- EAV 동적 필드 값
CREATE TABLE post_custom_field_values (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    post_id BIGINT UNSIGNED NOT NULL,
    field_name VARCHAR(100) NOT NULL COMMENT '필드명',
    field_value TEXT NULL COMMENT '필드값',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    INDEX idx_post_id (post_id),
    FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 댓글
CREATE TABLE post_comments (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    post_id BIGINT UNSIGNED NOT NULL,
    user_id BIGINT UNSIGNED NOT NULL,
    parent_id BIGINT UNSIGNED NULL COMMENT '대댓글',
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL,

    INDEX idx_post_id (post_id),
    INDEX idx_user_id (user_id),
    INDEX idx_parent_id (parent_id),
    FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    FOREIGN KEY (parent_id) REFERENCES post_comments(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

### Service 클래스

```php
// app/Services/BoardService.php
class BoardService
{
    public function listBoards(): Collection;
    public function createBoard(array $data): BoardSetting;
    public function updateBoard(BoardSetting $board, array $data): BoardSetting;

    public function listPosts(BoardSetting $board, array $filters): LengthAwarePaginator;
    public function findPost(int $id): Post;
    public function createPost(BoardSetting $board, array $data): Post;
    public function updatePost(Post $post, array $data): Post;
    public function deletePost(Post $post): bool;

    public function addComment(Post $post, array $data): PostComment;
    public function getComments(Post $post): Collection;
}
```

### 개발 체크리스트

- [x] EAV 패턴 구현 (동적 필드) - 2025-11-27 완료
- [x] `BoardSetting`, `Post`, `PostComment` 모델 작성 - 2025-11-27 완료
- [x] `BoardService` 클래스 작성 - 2025-11-27 완료
- [x] 게시판별 동적 필드 렌더링 - 2025-11-27 완료
- [ ] 에디터 통합 (Tiptap)
- [x] 파일 첨부 기능 - 2025-12-02 완료
- [ ] 댓글/대댓글 UI
- [ ] 전문 검색 (FULLTEXT)
- [ ] i18n 키 작성
- [ ] 테스트 작성
- [x] 템플릿 기반 게시판 생성 - 2025-12-02 완료 (공지사항, FAQ, 자료실, 갤러리 등)

**중요:** `CLAUDE.md`의 **EAV + Atomic Design 전략** 참조하여 구현

---

## 3️⃣ 설정 - 배너/팝업 관리

### 기능 목록

#### 3.1 배너 관리
- **경로:** `/mng/settings/banners`
- **기능:**
  - 배너 생성/수정/삭제
  - 이미지 업로드
  - 링크 URL 설정
  - 노출 기간, 위치 설정
  - 드래그앤드롭 정렬
- **권한:** `settings.banners.manage`

#### 3.2 팝업 관리
- **경로:** `/mng/settings/popups`
- **기능:**
  - 팝업 생성/수정/삭제
  - 내용 편집 (HTML)
  - 노출 기간, 타겟 (전체/특정 테넌트)
  - 오늘 하루 보지 않기 설정
- **권한:** `settings.popups.manage`

### DB 스키마

```sql
CREATE TABLE banners (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NULL COMMENT '특정 테넌트 or NULL (전체)',
    title VARCHAR(255) NOT NULL,
    image_url VARCHAR(500) NOT NULL COMMENT '배너 이미지',
    link_url VARCHAR(500) NULL COMMENT '클릭 시 이동 URL',
    position ENUM('main_top', 'main_middle', 'main_bottom', 'sidebar') DEFAULT 'main_top',
    display_from DATETIME NOT NULL,
    display_until DATETIME NOT NULL,
    sort_order INT DEFAULT 0,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant_id (tenant_id),
    INDEX idx_position (position),
    INDEX idx_is_active (is_active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE popups (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NULL,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL COMMENT 'HTML 내용',
    width INT DEFAULT 600,
    height INT DEFAULT 400,
    display_from DATETIME NOT NULL,
    display_until DATETIME NOT NULL,
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant_id (tenant_id),
    INDEX idx_is_active (is_active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

### Service 클래스

```php
// app/Services/BannerService.php
class BannerService
{
    public function list(): Collection;
    public function create(array $data): Banner;
    public function update(Banner $banner, array $data): Banner;
    public function delete(Banner $banner): bool;
    public function reorder(array $order): bool;
    public function getActiveBanners(string $position, int $tenantId = null): Collection;
}

// app/Services/PopupService.php
class PopupService
{
    public function list(): Collection;
    public function create(array $data): Popup;
    public function update(Popup $popup, array $data): Popup;
    public function delete(Popup $popup): bool;
    public function getActivePopups(int $tenantId = null): Collection;
}
```

### 개발 체크리스트

- [ ] `Banner`, `Popup` 모델 작성
- [ ] Service 클래스 작성
- [ ] 이미지 업로드 기능 (파일 저장소)
- [ ] 드래그앤드롭 정렬 UI
- [ ] 노출 기간 검증 로직
- [ ] 프론트엔드 표시 기능 (MNG 메인)
- [ ] i18n 키 작성
- [ ] 테스트 작성

---

## 🎯 Phase 4 완료 조건

### 기능 완성도
- [ ] 템플릿 변수 치환 동작
- [ ] 게시판 EAV 동적 필드 동작
- [ ] 배너/팝업 노출 정상 작동

### 코드 품질
- [ ] Service-First, FormRequest 준수
- [ ] EAV 패턴 올바른 구현
- [ ] i18n 키 사용
- [ ] Pint, PHPStan 통과

### 데이터 무결성
- [ ] 게시판별 동적 필드 격리
- [ ] 노출 기간 검증
- [ ] 파일 첨부 안전성

### 테스트
- [ ] EAV 패턴 테스트
- [ ] 템플릿 렌더링 테스트
- [ ] 배너/팝업 노출 로직 테스트

---

**최종 업데이트:** 2025-12-02
**작성자:** Claude Code
**버전:** 1.1.0