# 게시판 시스템 스펙 **작성일**: 2025-11-27 **상태**: 설계 완료, 구현 대기 **관련 프로젝트**: mng, api --- ## 1. 개요 ### 1.1 목적 - mng에서 **시스템 게시판**을 생성하여 모든 테넌트에게 제공 - sam(api)에서 테넌트별 **자체 게시판** 생성 가능 - 공지사항, 1:1 문의, FAQ 등 다양한 게시판 유형 지원 ### 1.2 핵심 개념 | 구분 | 설명 | |------|------| | **시스템 게시판** | mng에서 생성, `is_system=true`, 모든 테넌트에서 접근 가능 | | **테넌트 게시판** | sam에서 생성, `is_system=false`, 해당 테넌트만 접근 | | **board_type** | 자유 입력 (notice, qna, faq, free 등 제한 없음) | ### 1.3 역할 분리 ``` ┌─────────────────────────────────────────────────────────────┐ │ mng (상위 관리자) │ ├─────────────────────────────────────────────────────────────┤ │ • 시스템 게시판 CRUD (is_system = true) │ │ • 게시판 필드 정의 (board_settings) │ │ • 게시판 유형 자유 설정 (board_type) │ │ • tenant_id = NULL │ └─────────────────────────────────────────────────────────────┘ ↓ 전체 테넌트 공개 ┌─────────────────────────────────────────────────────────────┐ │ sam (api - 테넌트용) │ ├─────────────────────────────────────────────────────────────┤ │ • 테넌트 게시판 CRUD (is_system = false) │ │ • 시스템 게시판 사용 (읽기 전용 구조) │ │ • 게시글 CRUD (posts) │ │ • tenant_id = 현재 테넌트 │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 2. 데이터베이스 스키마 ### 2.1 기존 테이블 현황 현재 구현된 테이블 (6개): - `boards` - 게시판 정의 - `board_settings` - EAV 커스텀 필드 정의 - `posts` - 게시글 - `post_custom_field_values` - EAV 커스텀 필드 값 - `board_comments` - 댓글 - `board_files` - 파일 첨부 ### 2.2 boards 테이블 확장 **추가 컬럼:** | 컬럼 | 타입 | 설명 | |------|------|------| | `is_system` | TINYINT(1) DEFAULT 0 | 시스템 게시판 여부 (1=전체 공개) | | `board_type` | VARCHAR(50) NULL | 게시판 유형 (자유 입력) | **변경 컬럼:** | 컬럼 | 변경 내용 | |------|----------| | `tenant_id` | NOT NULL → **NULL 허용** (시스템 게시판용) | **최종 boards 테이블 구조:** ```sql CREATE TABLE boards ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT UNSIGNED NULL COMMENT '테넌트 ID (NULL=시스템 게시판)', is_system TINYINT(1) DEFAULT 0 COMMENT '시스템 게시판 여부 (1=전체 테넌트 공개)', board_type VARCHAR(50) NULL COMMENT '게시판 유형 (notice, qna, faq, free 등)', board_code VARCHAR(50) NOT NULL COMMENT '게시판 코드 (URL용)', name VARCHAR(100) NOT NULL COMMENT '게시판명', description TEXT NULL COMMENT '게시판 설명', editor_type VARCHAR(20) DEFAULT 'wysiwyg' COMMENT '에디터 타입', allow_files TINYINT(1) DEFAULT 1 COMMENT '파일 첨부 허용', max_file_count INT DEFAULT 5 COMMENT '최대 파일 수', max_file_size INT DEFAULT 20480 COMMENT '최대 파일 크기 (KB)', extra_settings JSON NULL COMMENT '추가 설정 (권한, 옵션 등)', is_active TINYINT(1) DEFAULT 1 COMMENT '활성 여부', created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, created_by BIGINT UNSIGNED NULL, updated_by BIGINT UNSIGNED NULL, deleted_at TIMESTAMP NULL, deleted_by BIGINT UNSIGNED NULL, INDEX idx_boards_tenant (tenant_id), INDEX idx_boards_is_system (is_system), INDEX idx_boards_board_type (board_type), UNIQUE INDEX uk_boards_code (tenant_id, board_code) ); ``` ### 2.3 board_settings 테이블 (EAV 필드 정의) 현재 구조 유지: ```sql CREATE TABLE board_settings ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, board_id BIGINT UNSIGNED NOT NULL COMMENT '게시판 ID', name VARCHAR(100) NOT NULL COMMENT '필드명', field_key VARCHAR(50) NOT NULL COMMENT '필드 키', field_type VARCHAR(30) NOT NULL COMMENT '필드 타입 (text, number, select, date 등)', field_meta JSON NULL COMMENT '필드 메타 (옵션, 유효성 등)', is_required TINYINT(1) DEFAULT 0 COMMENT '필수 여부', sort_order INT DEFAULT 0 COMMENT '정렬 순서', created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, created_by BIGINT UNSIGNED NULL, updated_by BIGINT UNSIGNED NULL, INDEX idx_board_settings_board (board_id), FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE ); ``` ### 2.4 posts 테이블 현재 구조 유지 (SoftDeletes 적용): ```sql CREATE TABLE posts ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID', board_id BIGINT UNSIGNED NOT NULL COMMENT '게시판 ID', user_id BIGINT UNSIGNED NOT NULL COMMENT '작성자 ID', title VARCHAR(255) NOT NULL COMMENT '제목', content LONGTEXT NULL COMMENT '내용', editor_type VARCHAR(20) DEFAULT 'wysiwyg' COMMENT '에디터 타입', ip_address VARCHAR(45) NULL COMMENT 'IP 주소', is_notice TINYINT(1) DEFAULT 0 COMMENT '공지 여부', is_secret TINYINT(1) DEFAULT 0 COMMENT '비밀글 여부', views INT DEFAULT 0 COMMENT '조회수', status VARCHAR(20) DEFAULT 'active' COMMENT '상태', created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, created_by BIGINT UNSIGNED NULL, updated_by BIGINT UNSIGNED NULL, deleted_at TIMESTAMP NULL, deleted_by BIGINT UNSIGNED NULL, INDEX idx_posts_tenant_board (tenant_id, board_id), INDEX idx_posts_status (status), FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE ); ``` ### 2.5 post_custom_field_values 테이블 (EAV 값) 현재 구조 유지: ```sql CREATE TABLE post_custom_field_values ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, post_id BIGINT UNSIGNED NOT NULL COMMENT '게시글 ID', field_id BIGINT UNSIGNED NOT NULL COMMENT '필드 ID (board_settings.id)', value TEXT NULL COMMENT '필드 값', created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, created_by BIGINT UNSIGNED NULL, updated_by BIGINT UNSIGNED NULL, INDEX idx_pcfv_post (post_id), INDEX idx_pcfv_field (field_id), FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE, FOREIGN KEY (field_id) REFERENCES board_settings(id) ON DELETE CASCADE ); ``` --- ## 3. 게시판 유형 (board_type) ### 3.1 권장 유형 | board_type | 설명 | 특징 | |------------|------|------| | `notice` | 공지사항 | 관리자만 작성, 전체 공개 | | `qna` | 1:1 문의 | 작성자+관리자만 열람, 비밀글 기본 | | `faq` | FAQ | 관리자 작성, 카테고리 분류 | | `free` | 자유게시판 | 댓글 허용, 전체 공개 | | `gallery` | 갤러리 | 이미지 중심 | | `download` | 자료실 | 파일 첨부 중심 | ### 3.2 유형별 extra_settings 예시 ```json // notice (공지사항) { "write_roles": ["admin", "manager"], "allow_comment": false, "allow_secret": false } // qna (1:1 문의) { "write_roles": ["*"], "allow_comment": true, "default_secret": true, "only_author_view": true } // faq { "write_roles": ["admin"], "use_category": true, "allow_comment": false } // free (자유게시판) { "write_roles": ["*"], "allow_comment": true, "allow_secret": true } ``` --- ## 4. API 설계 ### 4.1 mng API (시스템 게시판 관리) ``` # 시스템 게시판 CRUD GET /mng/boards # 시스템 게시판 목록 POST /mng/boards # 시스템 게시판 생성 GET /mng/boards/{id} # 시스템 게시판 상세 PUT /mng/boards/{id} # 시스템 게시판 수정 DELETE /mng/boards/{id} # 시스템 게시판 삭제 # 게시판 필드 관리 GET /mng/boards/{id}/fields # 필드 목록 POST /mng/boards/{id}/fields # 필드 추가 PUT /mng/boards/{id}/fields/{fid} # 필드 수정 DELETE /mng/boards/{id}/fields/{fid} # 필드 삭제 POST /mng/boards/{id}/fields/reorder # 필드 순서 변경 ``` ### 4.2 sam API (테넌트용) ``` # 게시판 조회 (시스템 + 테넌트) GET /v1/boards # 접근 가능한 게시판 목록 GET /v1/boards/{code} # 게시판 상세 # 테넌트 게시판 관리 POST /v1/boards # 테넌트 게시판 생성 PUT /v1/boards/{code} # 테넌트 게시판 수정 DELETE /v1/boards/{code} # 테넌트 게시판 삭제 # 게시글 CRUD GET /v1/boards/{code}/posts # 게시글 목록 POST /v1/boards/{code}/posts # 게시글 작성 GET /v1/boards/{code}/posts/{id} # 게시글 상세 PUT /v1/boards/{code}/posts/{id} # 게시글 수정 DELETE /v1/boards/{code}/posts/{id} # 게시글 삭제 # 댓글 GET /v1/posts/{id}/comments # 댓글 목록 POST /v1/posts/{id}/comments # 댓글 작성 PUT /v1/comments/{id} # 댓글 수정 DELETE /v1/comments/{id} # 댓글 삭제 ``` --- ## 5. 모델 설계 ### 5.1 Board 모델 ```php // api/app/Models/Boards/Board.php namespace App\Models\Boards; use App\Traits\BelongsToTenant; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Board extends Model { use SoftDeletes; protected $fillable = [ 'tenant_id', 'is_system', 'board_type', 'board_code', 'name', 'description', 'editor_type', 'allow_files', 'max_file_count', 'max_file_size', 'extra_settings', 'is_active', 'created_by', 'updated_by', 'deleted_by', ]; protected $casts = [ 'is_system' => 'boolean', 'is_active' => 'boolean', 'allow_files' => 'boolean', 'extra_settings' => 'array', ]; /** * 현재 테넌트에서 접근 가능한 게시판 * - 시스템 게시판 (is_system = true) * - 해당 테넌트 게시판 (tenant_id = 현재 테넌트) */ public function scopeAccessible($query, int $tenantId) { return $query->where(function ($q) use ($tenantId) { $q->where('is_system', true) ->orWhere('tenant_id', $tenantId); })->where('is_active', true); } /** * 시스템 게시판만 (mng용) */ public function scopeSystemOnly($query) { return $query->where('is_system', true); } /** * 테넌트 게시판만 */ public function scopeTenantOnly($query, int $tenantId) { return $query->where('is_system', false) ->where('tenant_id', $tenantId); } // 관계 public function fields() { return $this->hasMany(BoardSetting::class, 'board_id') ->orderBy('sort_order'); } public function posts() { return $this->hasMany(Post::class, 'board_id'); } // Helper public function getSetting(string $key, $default = null) { return data_get($this->extra_settings, $key, $default); } } ``` ### 5.2 BoardService ```php // api/app/Services/Boards/BoardService.php namespace App\Services\Boards; use App\Models\Boards\Board; use App\Services\Service; class BoardService extends Service { /** * 접근 가능한 게시판 목록 (시스템 + 테넌트) */ public function getAccessibleBoards(array $filters = []) { return Board::accessible($this->tenantId()) ->when(isset($filters['board_type']), fn($q) => $q->where('board_type', $filters['board_type'])) ->orderBy('is_system', 'desc') ->orderBy('name') ->get(); } /** * 시스템 게시판 목록 (mng용) */ public function getSystemBoards() { return Board::systemOnly() ->orderBy('name') ->get(); } /** * 시스템 게시판 생성 (mng용) */ public function createSystemBoard(array $data): Board { $data['is_system'] = true; $data['tenant_id'] = null; $data['created_by'] = $this->apiUserId(); return Board::create($data); } /** * 테넌트 게시판 생성 (sam용) */ public function createTenantBoard(array $data): Board { $data['is_system'] = false; $data['tenant_id'] = $this->tenantId(); $data['created_by'] = $this->apiUserId(); return Board::create($data); } /** * 게시판 코드로 조회 */ public function getBoardByCode(string $code): ?Board { return Board::accessible($this->tenantId()) ->where('board_code', $code) ->first(); } } ``` --- ## 6. 권한 모델 ### 6.1 extra_settings 기반 권한 게시판별 권한은 `extra_settings` JSON 필드로 관리: ```json { "permissions": { "read": ["*"], // 모든 역할 읽기 가능 "write": ["admin", "user"], // admin, user 역할만 작성 "manage": ["admin"] // admin만 관리 } } ``` ### 6.2 권한 검증 ```php // Board 모델에 추가 public function canRead(User $user): bool { $roles = $this->getSetting('permissions.read', ['*']); return in_array('*', $roles) || $user->hasAnyRole($roles); } public function canWrite(User $user): bool { $roles = $this->getSetting('permissions.write', ['*']); return in_array('*', $roles) || $user->hasAnyRole($roles); } public function canManage(User $user): bool { $roles = $this->getSetting('permissions.manage', ['admin']); return $user->hasAnyRole($roles); } ``` --- ## 7. 구현 계획 ### 7.1 Phase 1: DB 마이그레이션 (api/) 1. `boards` 테이블 확장 - `tenant_id` nullable 변경 - `is_system` 컬럼 추가 - `board_type` 컬럼 추가 - `deleted_at`, `deleted_by` 추가 (없으면) - 인덱스 추가 ### 7.2 Phase 2: 모델/서비스 수정 (api/) 1. `Board` 모델 업데이트 - 스코프 추가 (accessible, systemOnly, tenantOnly) - SoftDeletes 적용 - 권한 헬퍼 메서드 2. `BoardService` 구현 - 시스템/테넌트 게시판 분리 로직 ### 7.3 Phase 3: mng 화면 개발 1. 게시판 목록 (`/mng/boards`) 2. 게시판 생성/수정 폼 3. 게시판 필드 관리 ### 7.4 Phase 4: sam API 개발 1. 게시판 API (`/v1/boards`) 2. 게시글 API (`/v1/boards/{code}/posts`) 3. Swagger 문서 --- ## 8. 관련 문서 - [데이터베이스 스키마](./database-schema.md) - [API 개발 규칙](../reference/api-rules.md) - [MNG Critical Rules](../../mng/docs/MNG_CRITICAL_RULES.md) --- **최종 업데이트**: 2025-11-27 **버전**: 1.0 **상태**: 설계 완료