Files
sam-docs/specs/board-system-spec.md
hskwon 08a8259313 docs: 5130 레거시 분석 문서 및 기존 문서 초기 커밋
- 5130 레거시 시스템 분석 (00_OVERVIEW ~ 08_SAM_COMPARISON)
- MES 프로젝트 문서
- API/프론트엔드 스펙 문서
- 가이드 및 레퍼런스 문서
2025-12-04 18:47:19 +09:00

16 KiB

게시판 시스템 스펙

작성일: 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 테이블 구조:

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 필드 정의)

현재 구조 유지:

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 적용):

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 값)

현재 구조 유지:

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 예시

// 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 모델

// 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

// 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 필드로 관리:

{
  "permissions": {
    "read": ["*"],              // 모든 역할 읽기 가능
    "write": ["admin", "user"], // admin, user 역할만 작성
    "manage": ["admin"]         // admin만 관리
  }
}

6.2 권한 검증

// 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. 관련 문서


최종 업데이트: 2025-11-27 버전: 1.0 상태: 설계 완료