387 lines
12 KiB
Markdown
387 lines
12 KiB
Markdown
|
|
# 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;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 개발 체크리스트
|
|||
|
|
|
|||
|
|
- [ ] EAV 패턴 구현 (동적 필드)
|
|||
|
|
- [ ] `BoardSetting`, `Post`, `PostComment` 모델 작성
|
|||
|
|
- [ ] `BoardService` 클래스 작성
|
|||
|
|
- [ ] 게시판별 동적 필드 렌더링
|
|||
|
|
- [ ] 에디터 통합 (Tiptap)
|
|||
|
|
- [ ] 파일 첨부 기능
|
|||
|
|
- [ ] 댓글/대댓글 UI
|
|||
|
|
- [ ] 전문 검색 (FULLTEXT)
|
|||
|
|
- [ ] i18n 키 작성
|
|||
|
|
- [ ] 테스트 작성
|
|||
|
|
|
|||
|
|
**중요:** `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-11-21
|
|||
|
|
**작성자:** Claude Code
|
|||
|
|
**버전:** 1.0.0
|