본문으로 건너뛰기

내장 게시판 시작하기

내장 게시판(Builtin Board)은 v1.37.0 에서 추가된 경량 공지 / FAQ / 1:1 문의 모듈입니다. site_board_* 테이블 기반으로 동작하며, SaaS · Tenant 운영 주체가 직접 게시판을 만들어 회원에게 공지를 띄우거나 문의를 응대할 수 있습니다.

"게시판 = 사이트 전체 설정"

내장 게시판은 개별 회원 기능이 아니라 사이트 운영 설정입니다. 게시판 컨테이너(공지/FAQ/문의)는 SaaS(L1) · Tenant(L2) 운영자가 만들고, 회원은 노출된 글을 읽거나 문의를 작성합니다.

외부 Board 플러그인과의 차이

내장 게시판은 외부 Board 플러그인(App\Plugins\Board, 자유게시판)과 완전히 별개의 기능입니다. 이름이 비슷하지만 코드/테이블/용도가 모두 다릅니다.

구분내장 게시판 (Builtin Board)외부 Board 플러그인 (자유게시판)
도입v1.37.0 (Phase 3)별도 플러그인
용도공지 / FAQ / 1:1 문의자유게시판 (글쓰기·댓글·좋아요·스크랩)
모델App\Models\Board, BoardPost, BoardInquiry, BoardInquiryReplyApp\Plugins\Board 네임스페이스 모델
DB 테이블site_board_*plg_board_*
Filament ResourceBoardResource(slug site-boards)BoardResource(slug boards)
활성 방식기본 내장 (CORE_BOARD_ENABLED)운영자 선택형 플러그인
운영 주체SaaS(L1) / Tenant(L2)플러그인 활성화 테넌트
테이블/슬러그 분리 이유

외부 Board 플러그인이 이미 board_*(정확히는 plg_board_*) 테이블과 boards 슬러그를 점유하고 있기 때문에, 내장 게시판은 충돌을 피하기 위해 테이블 접두사를 site_board_ 로, Filament 슬러그를 site-boards 로 분리했습니다. 각 모델은 $table 속성을 명시합니다.

게시판 컨테이너 개념

내장 게시판의 최상위 단위는 게시판 컨테이너(App\Models\Board)입니다. 하나의 컨테이너는 kind 로 종류가 정해지며, 그 안에 글(BoardPost) 또는 문의(BoardInquiry)가 담깁니다.

kind 상수설명담기는 항목
Board::KIND_NOTICEnotice공지사항BoardPost
Board::KIND_FAQfaq자주 묻는 질문 (카테고리 그룹)BoardPost
Board::KIND_INQUIRYinquiry1:1 문의 접수BoardInquiry
Board::KIND_GENERICgeneric범용 게시판 (자유 생성)BoardPost

노출 대상(audience_scope)

각 컨테이너는 audience_scope 로 "누구에게 보일지"를 정합니다.

scope 상수노출 대상
Board::SCOPE_SAASsaasSaaS 전체 (모든 테넌트, tenant_id 무관)
Board::SCOPE_TENANTtenant해당 Tenant 전체
Board::SCOPE_ORGorgaudience_org_id 조직과 그 하위 서브트리 (depth 무관)

회원에게 보이는 게시판은 모델 스코프 Board::scopeVisibleToMember() 가 결정합니다. SaaS 전체 공지, 자신이 속한 Tenant 공지, 자신의 조직 조상 체인(ViewerScopeResolver::orgAncestorIds())에 걸린 org 공지를 합쳐 노출합니다.

누가 게시판을 만드는가

게시판 컨테이너 생성/삭제는 "사이트 전체 설정"이므로 L1 SaaS · L2 Tenant 운영자만 가능합니다. L3 조직 운영자는 본인 서브트리 게시판의 글 관리와 문의 응대만 수행합니다(생성 불가). 이 규칙은 BoardResource::canCreate()(level <= 2) 와 Board::scopeManageableBy() 에서 강제됩니다.

2계층 격리 + RLS

4개 모델(Board, BoardPost, BoardInquiry, BoardInquiryReply)은 모두 SaasTenantOwnedModelInterface 를 구현하고 다음 Trait 을 사용합니다.

  • BelongsToSaasProductsaas_product_id 자동 할당 + SaaS 격리
  • BelongsToTenanttenant_id 자동 할당 + Tenant 격리
  • Auditable — 생성/수정/삭제 감사 로그 자동 기록

SaaS + Tenant 2계층으로 데이터가 격리됩니다. 애플리케이션 레벨 글로벌 스코프에 더해, PostgreSQL Row Level Security(RLS) 정책이 saas_product_id / tenant_id 세션 변수 기준으로 행을 격리합니다.

RLS 정책 (v1.37.1 보정)

초판 RLS 정책이 WITH CHECK 누락·세션변수 NULL bypass 부재로 시드/마이그레이션 INSERT 를 거부하는 문제가 있었습니다. v1.37.1 의 2026_06_01_000001_fix_site_board_rls_policies 마이그레이션이 demo_posts 패턴(saas/tenant 분리 정책 + USING + WITH CHECK + 세션변수가 NULL/''/'0' 일 때 bypass)으로 교체했습니다. 이미 마이그레이션된 DB 는 같은 배치 롤백 위험을 피하기 위해 forward-only 로 보정됩니다.

마이그레이션 / 테이블

database/migrations/site_board_* 마이그레이션 5개 + RLS 보정 1개로 구성됩니다. 모두 Schema::hasTable() 멱등 가드와 데이터 보존형 down()(드롭 보류)을 적용해 백포트에 안전합니다.

마이그레이션테이블모델
2026_05_31_000002_create_site_board_boards_tablesite_board_boardsBoard
2026_05_31_000003_create_site_board_posts_tablesite_board_postsBoardPost
2026_05_31_000004_create_site_board_inquiries_tablesite_board_inquiriesBoardInquiry
2026_05_31_000005_create_site_board_inquiry_replies_tablesite_board_inquiry_repliesBoardInquiryReply
2026_05_31_000006_setup_site_board_rls_policies(RLS 정책)
2026_06_01_000001_fix_site_board_rls_policies(RLS 보정, v1.37.1)
운영 500 대응

relation "site_board_boards" does not exist 오류는 코드 문제가 아니라 운영 DB 에 마이그레이션이 적용되지 않은 상태입니다. make migrate NAME={project} 로 해결합니다.

활성화 / 비활성화

내장 게시판은 기본 활성화 상태로 출하됩니다. config/core.phpboard 블록에서 제어합니다.

// config/core.php
'board' => [
'enabled' => env('CORE_BOARD_ENABLED', true),
'tables' => [
'boards' => 'site_board_boards',
'posts' => 'site_board_posts',
'inquiries' => 'site_board_inquiries',
'replies' => 'site_board_inquiry_replies',
],
'kinds' => ['notice', 'faq', 'inquiry', 'generic'],
'inquiry' => [
// 미해결 문의를 상위 org 로 한 단계 에스컬레이션 허용
'escalation_enabled' => env('CORE_BOARD_INQUIRY_ESCALATION', true),
],
],
# .env (기본값)
CORE_BOARD_ENABLED=true # 내장 게시판 전체 on/off
CORE_BOARD_INQUIRY_ESCALATION=true # 문의 에스컬레이션 액션 노출 여부

Filament Resource / 회원 페이지는 모두 config('board.enabled', true) 를 게이트로 사용하므로, false 로 끄면 운영자 메뉴와 회원 메뉴가 모두 숨겨집니다.

기본 시드 (CoreSeeder)

CoreSeeder::createDemoBoards() 가 테넌트마다 기본 게시판 3개(모두 is_system=true)를 멱등 시드합니다(tenant_id + key 기준 updateOrCreate). board.enabled=false 이거나 site_board_boards 테이블이 없으면 건너뜁니다(백포트 안전).

keykind이름audience_scope
noticenotice공지사항tenant
faqfaqFAQtenant
inquiryinquiry1:1 문의tenant

추가로 데모 데이터로 공지글 1건("환영합니다", 고정), FAQ 글 1건("비밀번호를 잊었어요", 카테고리 계정), 그리고 member@ 회원이 작성한 문의 1건("서비스 이용 문의")을 시드합니다. 회원 문의는 member@ 의 소속 조직(없으면 HQ 조직)을 organization_id · assigned_org_id 로 지정해, org@ 운영자가 응대하는 계층 시나리오를 바로 시연할 수 있습니다.

접속 경로

운영자 (Filament 관리 패널)

내장 게시판 Filament 플러그인(BuiltinBoardFilamentPlugin, id builtin-board)은 SaaS · Tenant · Org 패널에 등록됩니다. App 패널에는 등록되지 않습니다.

패널메뉴navigation 그룹슬러그/경로
SaaS게시판 (BoardResource)사이트 관리/saas/site-boards
SaaS1:1 문의 (InquiryResource)고객 문의/saas/{inquiry-slug}
Tenant게시판 / 1:1 문의사이트 관리 / 고객 문의/tenant/.../site-boards
Org게시판 / 1:1 문의사이트 관리 / 고객 문의/org/site-boards
  • BoardResource사이트 관리 navigation 그룹, 슬러그 site-boards(외부 Board 플러그인의 boards 와 분리)입니다.
  • InquiryResource고객 문의 navigation 그룹에 표시됩니다.
  • 게시판 메뉴는 "사이트 전체 설정"이므로 관리 가능한 사이트(독립 사이트 또는 SaaS 기본 사이트) 컨텍스트에서만 노출됩니다. 미러 사이트(Platform 기본 설정 공유)에는 표시되지 않습니다(SiteTypeHelper::currentContextIsManageableSite()).

회원 (App 패널)

회원용 페이지 3개는 App 패널(/app)에 자동 등록(discoverPages)됩니다.

페이지 클래스메뉴역할
App\Filament\App\Pages\NoticesPage공지사항자기 계층에 노출된 공지글 열람
App\Filament\App\Pages\FaqPageFAQFAQ 글을 카테고리별로 그룹 열람
App\Filament\App\Pages\MyInquiriesPage1:1 문의본인 문의 작성·조회·답글

세 페이지 모두 config('board.enabled', true)true 일 때만 메뉴에 노출됩니다.

권한 카탈로그

서브관리자(Chief 아래 폼으로 생성된 관리자)는 메뉴별 권한이 있어야 접근할 수 있습니다(v1.38.0 부터 강제). 1차 관리자(chief_id=null)와 Level 0 은 전권입니다. config/permissions.php 에 다음 feature 키가 정의되어 있습니다.

feature 키라벨custom_actions
boards게시판 관리manage_notice(공지 관리), manage_faq(FAQ 관리), reply(답글 작성)
inquiries문의 관리reply(답변 작성), assign(담당자 배정)

BoardResource 의 feature 키는 boards, InquiryResourceinquiries 입니다. 노출/접근은 config('board.enabled') + 사이트 컨텍스트 + 해당 키의 read 권한이 AND 병합됩니다.

관련 문서

  • 운영 절차는 운영자 가이드를 참조하세요.
  • 설계 배경: workspace/_docs/design/03_features/23_board-commerce-plugins.md, workspace/_docs/design/01_decisions/10_board-commerce-strategy.md