- INDEX.md: 메뉴 드래그앤드롭, 프로필 설정, 비밀번호 변경 강제, 게시판 템플릿, 파일 첨부, 일일 스크럼 기능 추가 - 04_PHASE4_CONTENT.md: 게시판 관리 체크리스트 업데이트 (EAV, 파일 첨부, 템플릿 완료 표시) - CURRENT_WORKS.md: 2025-12-02 작업 내역 추가
388 lines
12 KiB
Markdown
388 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;
|
||
}
|
||
```
|
||
|
||
### 개발 체크리스트
|
||
|
||
- [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 |