1. 시스템 개요
DxCMS Board는 DesignOneX에서 만든 PHP 기반 경량 CMS(Content Management System)입니다. 단일 진입점(index.php) 구조, 파일 기반 라우팅, 플러그인•훅•extend 세 가지 확장 방법을 제공하며, PHP 5.6부터 8.x까지 단일 코드베이스로 동작합니다.
핵심 설계 철학
- 단일 진입점 — 모든 HTTP 요청은 index.php 한 곳으로 집결
- 직접 수정 최소화 — core/ 폴더 수정 없이 extend, 훅, 플러그인으로 기능 추가
- 저가형 호스팅 호환 — APCuRedis 없어도 파일 캐시로 동작
- PHP 5.6 하위 호환 — anonymous fn, ??, return type hint 미사용
- 보안 단일화 — core/Secure.php 한 파일에 모든 보안 코드 집중
지원 환경
| 항목 | 지원 범위 |
|---|---|
| PHP 버전 | 5.6 • 7.0 • 7.1 • 7.2 • 7.3 • 7.4 • 8.0 • 8.1 • 8.2 • 8.3 • 8.4 |
| 데이터베이스 | MySQL 5.6+, MariaDB 10.1+ (PDO) |
| 웹서버 | Apache 2.2+, Nginx, IIS 7+, 저가형 공유호스팅 |
| OS | Windows, Linux |
| 캐시 | Redis (1순위) → APCu (2순위) → 파일 캐시 (기본) |
| 에디터 | CKEditor 4 (내장), Jodit, TinyMCE (플러그인) |
2. 전체 폴더 구조
DxCMS의 최상위 폴더 구조입니다. 각 폴더의 역할과 직접 수정 가능 여부를 정리합니다.
dxcms-board/
├── index.php ← 단일 진입점 (모든 HTTP 요청)
├── .htaccess ← Apache URL Rewrite
├── web.config ← IIS URL Rewrite
├── nginx.conf.example ← Nginx 설정 예시
├── core/ ← CMS 엔진 (직접 수정 비권장)
├── admin/ ← 관리자 페이지
├── boards/ ← 게시판 핸들러 + 스킨
├── themes/ ← 프론트엔드 테마
├── plugins/ ← 플러그인
├── assets/ ← 정적 에셋 (CKEditor 등)
├── extend/ ← 코드 삽입 슬롯
├── data/ ← DB설정•업로드•캐시 (웹 비공개)
├── install/ ← 설치 마법사
├── pages/ ← 기본 페이지 파일
├── routes/ ← 라우트 정의 파일
├── controllers/ ← 컨트롤러 클래스
└── docs/ ← 내부 문서
| 폴더 | 역할 | 직접 수정 |
|---|---|---|
| core/ | CMS 핵심 엔진 (DB, 라우터, 보안, 테마, 캐시 등) | 비권장 (보안 패치 시 Secure.php만) |
| admin/ | 관리자 전용 페이지 (게시판/회원/설정 등 관리) | 가능 (관리자 커스텀 시) |
| boards/ | 게시판 요청 처리 핸들러 + 게시판 전용 스킨 | 가능 (스킨 추가 시) |
| themes/ | 프론트엔드 HTML/CSS 레이아웃 및 게시판 뷰 | 권장 (커스텀 테마 추가) |
| plugins/ | 에디터•결제•소켓 등 확장 플러그인 | 권장 (플러그인 개발) |
| extend/ | 훅 없이 코드 삽입(점검모드•IP차단•방문 로그) | 권장 (운영 커스텀) |
| data/ | DB 접속 정보•업로드 파일•캐시 저장 | 자동 생성, 서버 비공개 |
| assets/ | CKEditor 로컬 파일, 공통 JS | 가능 (에셋 추가) |
| install/ | 설치 마법사 및 마이그레이션 파일 | 설치 후 삭제 권장 |
3. 폴더별 상세 설명
3-1. core/ — CMS 엔진 핵심
CMS의 두뇌입니다. 데이터베이스, 라우팅, 보안, 캐시, 테마, 플러그인 등 모든 핵심 기능이 여기 있습니다. 직접 수정은 최소화하고, 보안 패치가 필요하면 Secure.php 한 파일만 교체합니다.
core/
├── Secure.php ← ★ 보안 전담 (CSRF•XSS•Rate Limit•WAF)
├── functions.php ← 공통 헬퍼 함수 60여 개 (dx_* 접두어)
├── DxCache.php ← Redis/APCu/파일 캐시 통합
├── DxSeo.php ← SEO 메타 자동 생성
├── DxCategory.php ← 카테고리 DB 조회•렌더링
├── DxSanitizer.php ← HTML 입력 정제 (화이트리스트)
├── DxTheme.php ← 테마 파일 해석 + 폴백 체인
├── DxSite.php ← 멀티사이트 (도메인별 설정)
├── DxExtend.php ← extend/ 폴더 자동 실행
├── DxPoint.php ← 포인트/경험치/레벨
├── DxBoardSkin.php ← 게시판 스킨 폴백 체인
├── DxRouter.php ← 라라벨 스타일 라우터 (선택적)
├── DxMarket.php ← 마켓 클라이언트 (플러그인 설치)
├── DxNotification.php ← 실시간 알림 (DB + 소켓)
├── DxFriend.php ← 스크랩/친구/차단
├── DxShop.php ← 포인트샵
├── DxPopup.php ← 팝업 관리
├── DxThumb.php ← GD 썸네일 생성
├── DxContainer.php ← 서비스 컨테이너 (DI)
├── DxCss.php ← Tailwind 호환 CSS SSR
├── DxMemberMonitor.php ← 회원 접속 모니터링
├── DxMigration.php ← 마이그레이션 감지
├── DxSocialAuth.php ← 소셜 로그인 (카카오/네이버/구글/GitHub)
├── PluginRegistry.php ← 플러그인 타입 등록
├── db/
│ ├── Database.php ← PDO 래퍼 싱글턴
│ └── QueryBuilder.php← 빌더 패턴 쿼리 작성
├── router/
│ ├── Router.php ← URL → 라우트 타입 결정
│ └── Dispatcher.php ← 라우트 → 핸들러 실행
├── hook/
│ └── HookManager.php ← 액션/필터 훅 시스템
├── auth/
│ ├── Auth.php ← 세션 기반 인증
│ ├── login.php ← 로그인 페이지
│ ├── register.php ← 회원가입
│ ├── mypage.php ← 마이페이지
│ └── DxSocialAuth.php(소셜 OAuth2 처리)
└── api/
├── upload.php ← 파일 업로드
├── comment.php ← 댓글 CRUD
├── like.php ← 좋아요
├── sitemap.php ← 동적 사이트맵
└── ... ← 기타 AJAX 엔드포인트
3-2. admin/ — 관리자 페이지
모든 관리자 기능이 여기에 있습니다. 각 서브폴더가 하나의 관리 메뉴에 대응합니다. admin/index.php가 사이드바•헤더 레이아웃을 제공하고, 각 서브폴더의 index.php가 내용을 담습니다.
| 경로 | 기능 |
|---|---|
| admin/index.php | 관리자 레이아웃 (헤더•사이드바•공통 CSS) |
| admin/dashboard/ | 방문자 통계, 최근 게시글, 회원 현황 대시보드 |
| admin/boards/ | 게시판 생성•수정•삭제, 에디터 선택, 카테고리 스킨 지정 |
| admin/board_groups/ | 게시판 그룹 관리 |
| admin/categories/ | 게시판 카테고리 (표시 위치•배지색•스킨) |
| admin/members/ | 회원 목록•수정•포인트•레벨 관리 |
| admin/menus/ | 메뉴 관리 (게시판•페이지 빠른 선택 UI) |
| admin/pages/ | 정적 페이지 관리 (홈 설정 포함) |
| admin/themes/ | 테마 목록•활성화 |
| admin/plugins/ | 플러그인 목록•활성화•설정 |
| admin/settings/ | 사이트 기본 설정 (SEO•Analytics•캐시) |
| admin/statistics/ | 방문자 통계•인기 검색어 |
| admin/socket/ | 실시간 소켓 접속자 모니터 |
| admin/popup/ | 팝업 관리 |
| admin/shop/ | 포인트샵 상품 관리 |
| admin/market/ | 마켓 플러그인/테마 설치 |
| admin/social/ | 소셜 로그인 설정 |
| admin/points/ | 포인트 내역 관리 |
| admin/downloads/ | 다운로드 통계 |
| admin/sites/ | 멀티사이트 도메인 관리 |
3-3. boards/ — 게시판 핸들러 및 스킨
게시판의 모든 요청(목록•보기•쓰기•수정•삭제•댓글)은 boards/handler.php 한 파일에서 처리됩니다. 렌더링은 스킨 파일에 위임하여 로직과 표현을 분리합니다.
boards/
├── handler.php ← 모든 게시판 요청 처리 (약 79KB)
├── skins/
│ ├── gallery/ ← 갤러리형 스킨
│ │ ├── list.php ← 목록 뷰
│ │ ├── view.php ← 상세 뷰
│ │ ├── write.php ← 작성/수정 뷰
│ │ └── skin.json ← 스킨 메타 (이름•버전•옵션)
│ ├── erp/ ← ERP형 스킨 (재고관리 액션 포함)
│ │ ├── actions/
│ │ │ └── inventory.php ← 커스텀 액션
│ │ └── skin.json
│ └── shop/ ← 쇼핑몰형 스킨
│ └── actions/cart.php ← 장바구니 액션
└── category/
└── skins/
├── default/ ← 라운드 탭 카테고리
│ ├── list.php
│ └── view.php
└── pill/ ← 언더라인 탭 카테고리
✅ boards/skins/SKIN_GUIDE.md 파일에 커스텀 스킨 제작 가이드가 포함되어 있습니다.
3-4. themes/ — 프론트엔드 테마
HTML 레이아웃과 CSS를 관리합니다. 테마별로 폴더를 만들고, 없는 파일은 자동으로 default 테마에서 폴백됩니다. 따라서 커스텀 테마는 변경하고 싶은 파일만 만들면 됩니다.
themes/
├── default/ ← 기본 테마 (항상 존재, 폴백 기준)
│ ├── theme.json ← 테마 메타 (이름•버전•옵션)
│ ├── style.css ← 테마 전용 CSS
│ ├── layout/
│ │ └── main.php ← 전체 레이아웃 (헤더+본문+푸터)
│ ├── board/
│ │ ├── list.php ← 게시판 목록
│ │ ├── view.php ← 게시글 보기
│ │ └── write.php ← 게시글 작성/수정
│ ├── board_latest/
│ │ ├── list.php ← 최신글 위젯 (기본)
│ │ ├── card.php ← 최신글 위젯 (카드형)
│ │ └── simple.php ← 최신글 위젯 (간소)
│ ├── page/
│ │ ├── home.php ← 홈 페이지
│ │ ├── 403.php
│ │ └── 404.php
│ ├── parts/
│ │ └── pagination.php← 페이지네이션 파셜
│ ├── search/
│ │ └── list.php ← 검색 결과
│ └── icons/ ← SVG 아이콘
└── (my-theme)/ ← 커스텀 테마 (변경 파일만 있으면 됨)
폴백 체인 우선순위
- themes/[현재테마]/board/list.php ← 1순위: 현재 테마 파일
- themes/default/board/list.php ← 2순위: default 테마 폴백
- 404 오류 ← 3순위: 최종 실패
3-5. plugins/ — 플러그인
에디터•결제•소켓 등 선택적 기능을 플러그인으로 제공합니다. 각 플러그인 폴더의 plugin.php가 dx_register_plugin()을 호출해 시스템에 등록됩니다.| 플러그인 | 타입 | 설명 |
|---|---|---|
| dx-socket | socket | 실시간 WebSocket (접속자 표시, 채팅, 알림) |
| jodit-editor | editor | Jodit WYSIWYG 에디터 |
| tinymce-editor | editor | TinyMCE 에디터 |
| tosspay-payment | payment | 토스페이 결제 연동 |
| example-plugin | - | 플러그인 개발 예제 템플릿 |
// 플러그인 등록 예시 (plugins/my-plugin/plugin.php)
dx_register_plugin(array(
'id' => 'my-plugin',
'type' => 'editor',
'name' => 'My Editor',
'version' => '1.0.0',
'settings' => array(
'api_key' => array('label'=>'API 키', 'type'=>'text'),
),
));
3-6. extend/ — 코드 삽입 슬롯
훅 등록 없이 파일을 폴더에 넣기만 하면 자동으로 실행됩니다. 파일명 오름차순으로 실행되며, 에러가 발생해도 다른 파일에는 영향을 주지 않습니다.| 폴더 | 실행 시점 | 주요 용도 |
|---|---|---|
| extend/top/ | DB•세션•인증 완료 직후, 라우팅 전 | 점검 모드, IP 차단, 커스텀 인증, 글로벌 변수 주입 |
| extend/middle/ | 라우트 확정 직후, 핸들러 실행 전 | 방문자 로그(기본 포함), A/B 테스트, 접근 제어 |
| extend/bottom/ | 렌더링 완료 후, 출력 직전 | 성능 로그, 캐시 저장, 정리 작업 |
✅ extend/ 파일은 파일명 앞에 숫자를 붙여 실행 순서를 제어합니다. 예: 01_maintenance.php, 02_ip_block.php
3-7. data/ — 설정•업로드•캐시 (웹 비공개)
웹 브라우저로 직접 접근이 차단된 폴더입니다. DB 접속 정보, 업로드된 파일, 파일 캐시가 저장됩니다.
data/
├── config.php ← DB 연결 정보 + 설정 캐시 (설치 시 자동 생성)
├── cache/ ← 파일 캐시 저장소 (Redis/APCu 없을 때 사용)
│ └── *.cache ← 직렬화된 캐시 파일
├── uploads/ ← 업로드 파일 저장소
│ └── .htaccess ← PHP 실행 차단 (보안)
└── boards/ ← 게시판별 업로드
⚠ data/ 폴더는 절대 웹 루트 외부에 위치시키는 것이 가장 안전합니다. .htaccess로 PHP 실행이 차단됩니다.
3-8. assets/ — 정적 에셋
CDN 의존 없이 로컬에서 서빙하는 정적 파일들입니다.| 경로 | 내용 |
|---|---|
| assets/ckeditor4/ | CKEditor 4.25.1-LTS 전체 파일 (로컬 서빙, CDN 없음) |
| assets/ckeditor4/config.js | CKEditor 설정 (툴바, 언어, 업로드 URL) |
| assets/js/dxb-css.js | Tailwind 호환 런타임 CSS 엔진 |
| assets/js/dx.js | 공통 JavaScript (CSRF 헤더, 플래시 메시지 등) |
4. CORE 파일 상세 분석
core/ 폴더 안의 모든 파일을 하나씩 자세히 설명합니다. 각 파일의 역할, 주요 메서드, 사용 방법을 포함합니다.
4-1. Secure.php — 보안 전담 파일 (v5.2.2)
CMS 전체의 보안을 책임지는 핵심 파일입니다. 보안 패치가 필요하면 이 파일 하나만 교체하면 됩니다.
담당 기능
- 세션 보안 — HttpOnly•Secure•SameSite=Lax 쿠키 설정
- CSRF 토큰 — 발급•검증 (TTL 24시간, 활동 시 자동 갱신)
- 보안 헤더 — X-Frame-Options, X-Content-Type-Options, Referrer-Policy
- XSS 방어 — 출력 이스케이프 (dx_esc 함수)
- WAF — SQL Injection, XSS 패턴 탐지 (POST 본문 에디터 필드 제외)
- Rate Limit — Redis 기반 (파일 폴백) 요청 제한
- Bot 탐지 — 검색엔진 봇은 허용, 의심 봇은 로그만 기록
- 업로드 검증 — MIME + 확장자 이중 검증, 이중 확장자 차단
- 비밀번호 해시 — bcrypt (PHP 5.6 폴백 포함)
- 안전 난수 — random_bytes 폴백 체인 (PHP 5.6 호환)
- Redis 연결 관리 — DxCache와 공유하는 단일 Redis 인스턴스
주요 메서드
| 메서드 | 설명 |
|---|---|
| getInstance() | 싱글턴 인스턴스 반환 |
| initSession() | 보안 세션 초기화 (쿠키 설정 적용) |
| startSession() | 비로그인 GET 요청은 세션 시작 생략 (성능) |
| sendSecurityHeaders() | CSRF 토큰 문자열 반환 (없으면 새로 생성) |
| csrfField() | <input type="hidden"> HTML 태그 반환 |
| csrfCheck() | POST 요청의 CSRF 토큰 검증 (실패 시 403) |
| validateUpload($file) | 업로드 파일 보안 검증 (MIME•확장자•이중확장자) |
| bcryptHash($password) | 비밀번호 bcrypt 해시 생성 |
| bcryptVerify($pw, $hash) | 비밀번호 해시 검증 |
| rateLimit($key, $max, $window) | Rate Limit 검사 (초과 시 true 반환) |
| clientIp() | 클라이언트 실IP 반환 (프록시 투과) |
| getRedis() | Redis 인스턴스 반환 (없으면 null) |
| sanitize($input) | HTML 특수문자 이스케이프 |
| esc($str) | HTML 출력 이스케이프 (= dx_esc) |
// 사용 예시
$secure = Secure::getInstance();
// CSRF 보호 (폼)
echo $secure->csrfField(); // <input type="hidden" ...>
$secure->csrfCheck(); // POST 수신 시 검증
// Rate Limit
if ($secure->rateLimit("login_" . dx_ip(), 5, 60)) {
dx_json(["error" => "너무 많은 시도"], 429);
}
// 업로드 검증
$err = $secure->validateUpload($_FILES["file"]);
if ($err) dx_json(["error" => $err], 400);
4-2. functions.php — 공통 헬퍼 함수 라이브러리
CMS 전체에서 사용하는 60여 개의 전역 함수를 정의합니다. 모든 함수는 dx_ 접두어를 사용합니다.
카테고리별 함수 목록
보안 난수
| 함수 | 설명 |
|---|---|
| dx_random_bytes($length) | PHP 7 random_bytes → OpenSSL → mcrypt 폴백 체인 |
| dx_random_hex($length) | 16진수 난수 문자열 생성 |
URL•경로 유틸
| 함수 | 설명 |
|---|---|
| dx_base_url($path) | 사이트 기본 URL 반환 (HTTPS 자동 감지) |
| dx_static_url($path) | 정적 파일 URL 반환 |
| dx_request_uri() | 현재 요청 URI 반환 (Rewrite 환경 대응) |
| dx_current_url() | 현재 전체 URL 반환 |
| dx_redirect($url, $code) | HTTP 리다이렉트 (기본 302) |
| dx_redirect_back($fallback) | 이전 페이지로 리다이렉트 |
| dx_realpath($path) | Windows 백슬래시 통일 realpath |
| dx_path_inside($path, $base) | 경로 이탈 공격 방어 검사 |
입력 처리
| 함수 | 설명 |
|---|---|
| dx_get($key, $default, $type) | $_GET 안전 읽기 (string/int/float/bool 타입 캐스팅) |
| dx_post($key, $default, $type) | $_POST 안전 읽기 |
| dx_request($key, $default, $type) | $_REQUEST 안전 읽기 |
| dx_method($method) | 현재 HTTP 메서드 확인 (dx_method("POST")) |
설정
| 함수 | 설명 |
|---|---|
| dx_config($key, $default) | DB 설정값 읽기 (캐시에서 조회) |
| dx_set_config($key, $value) | 설정값 저장 (DB + 캐시 갱신) |
출력•응답
| 함수 | 설명 |
|---|---|
| dx_esc($str) | HTML 출력 이스케이프 (htmlspecialchars) |
| dx_safe_url($url) | URL 안전 이스케이프 |
| dx_json($data, $code) | JSON 응답 전송 후 exit |
| dx_error($msg, $code) | HTTP 오류 응답 (기본 500) |
| dx_csrf_token() | CSRF 토큰 문자열 반환 |
| dx_csrf_field() | CSRF hidden input HTML 반환 |
| dx_csrf_check() | CSRF 토큰 검증 |
문자열•날짜
| 함수 | 설명 |
|---|---|
| dx_substr($str, $length, $suffix) | mbstring 폴백 포함 문자열 자르기 |
| dx_mb_substr($str, $start, $length) | mb_substr PHP 5.6 폴백 |
| dx_date($datetime, $format) | 날짜 포맷 변환 |
| dx_time_ago($datetime) | "3분 전" 상대 시간 문자열 |
| dx_filesize($bytes) | 파일 크기 포맷 (KB/MB/GB) |
플래시 메시지•로그
| 함수 | 설명 |
|---|---|
| dx_set_flash($msg, $type) | 플래시 메시지 저장 (세션) |
| dx_get_flash() | 플래시 메시지 읽기 후 삭제 |
| dx_log($message, $level) | 로그 기록 (error/warning/info) |
게시판 유틸
| 함수 | 설명 |
|---|---|
| dx_board_posts($boardKey, $limit) | 게시판 최신글 배열 반환 |
| dx_board_latest($boardKey, $limit, $skin) | 게시판 최신글 HTML 위젯 출력 |
| dx_pagination($total, $perPage, $current, $urlPattern) | 페이지네이션 HTML 생성 |
| dx_upload_url($path) | 업로드 파일 공개 URL 반환 |
| dx_is_ajax() | AJAX 요청 여부 확인 |
4-3. db/Database.php — PDO 래퍼 싱글턴
MySQL/MariaDB PDO 연결을 관리하는 싱글턴 클래스입니다. prefix 기반 테이블 관리, 안전한 파라미터 바인딩, 트랜잭션을 지원합니다.주요 메서드
| 메서드 | 반환 타입 | 설명 |
|---|---|---|
| getInstance() | Database | 싱글턴 인스턴스 |
| connect($host, $name, $user, $pass) | string | prefix 붙인 테이블명 반환 (예: dx_posts) |
| row($sql, $params) | array|null | SELECT 단일 행 |
| rows($sql, $params) | array | SELECT 전체 행 |
| value($sql, $params) | mixed | SELECT 단일 값 (COUNT 등) |
| find($table, $conditions) | array|null | WHERE 조건으로 단일 행 조회 |
| findAll($table, $conditions) | array | WHERE 조건으로 전체 행 조회 |
| insertRow($table, $data) | int | 행 삽입, insert_id 반환 |
| updateRow($table, $data, $conditions) | int | 행 수정, 영향 행 수 반환 |
| deleteRow($table, $conditions) | int | 행 삭제 |
| query($sql, $params) | PDOStatement | 직접 쿼리 실행 |
| execute($sql, $params) | PDOStatement | 파라미터 바인딩 실행 |
| beginTransaction() | void | 트랜잭션 시작 |
| commit() | void | 트랜잭션 커밋 |
| rollback() | void | 트랜잭션 롤백 |
| tableExists($table) | bool | 테이블 존재 여부 확인 |
| pdo() | PDO | PDO 인스턴스 직접 접근 |
$db = Database::getInstance();
// 단일 행 조회
$post = $db->row("SELECT * FROM `{$db->table('posts')}` WHERE id=?", [123]);
// 조건부 조회
$member = $db->find('members', ['login_id' => 'admin', 'status' => 1]);
// 삽입
$id = $db->insertRow('posts', [
'title' => '제목',
'content' => '내용',
'created_at' => date('Y-m-d H:i:s'),
]);
// 트랜잭션
$db->beginTransaction();
try { /* ... */ $db->commit(); }
catch (Exception $e) { $db->rollback(); }
4-4. db/QueryBuilder.php — 빌더 패턴 쿼리
라라벨 Query Builder와 동일한 철학으로 구현된 쿼리 빌더입니다. 기존 Database 메서드와 100% 하위 호환됩니다.
/ 기본 사용 ($db->table()로 빌더 시작)
$posts = $db->table('posts')
->where('status', 1)
->orderBy('id', 'desc')
->paginate(20);
// 복합 조건
$members = $db->table('members')
->where('level', '>=', 5)
->orWhere('is_admin', 1)
->get();
// whereIn
$db->table('posts')->whereIn('id', [1, 2, 3])->first();
// JOIN
$db->table('posts')
->join('members', 'posts.member_id', '=', 'members.id')
->select(['posts.*', 'members.name'])
->get();
4-5. DxCache.php — 멀티 드라이버 캐시 (v5.0.0)
Redis → APCu → 파일 캐시 순서로 자동 드라이버를 선택합니다. 개발자는 드라이버 종류에 상관없이 동일한 API를 사용하면 됩니다.
드라이버 선택 우선순위
- 1순위: Redis — REDIS_SESSION_URL 상수 설정 + Redis 익스텐션
- 2순위: APCu — apc.enabled + apcu_fetch 함수 존재
- 3순위: 파일 캐시 — data/cache/ 폴더 쓰기 가능
- 4순위: none — 캐시 없이 동작 (모두 실패 시)
캐시 항목별 TTL
| 캐시 항목 | TTL | 무효화 시점 |
|---|---|---|
| 전체 설정 (dx_settings) | 5분 | 설정 저장 시 전체 flush |
| 게시판 목록 캐시 | 1분 | 게시글 작성/수정/삭제 시 |
| 사이트맵 XML | 10분 | 게시글 변경 시 |
주요 메서드
| 메서드 | 설명 |
|---|---|
| DxCache::get($key) | 캐시 읽기 (없으면 null) |
| DxCache::set($key, $value, $ttl) | 캐시 저장 (ttl=0 영구) |
| DxCache::delete($key) | 캐시 삭제 |
| DxCache::flush() | 전체 캐시 삭제 |
| DxCache::remember($key, $ttl, $callback) | 없으면 콜백 실행 후 저장 |
| DxCache::driver() | 현재 사용 중인 드라이버명 반환 |
// 기본 사용
$settings = DxCache::get("dx_settings");
if ($settings === null) {
$settings = $db->rows("SELECT * FROM dx_settings");
DxCache::set("dx_settings", $settings, 300);
}
// remember 패턴 (더 간편)
$board = DxCache::remember("board_" . $id, 60, function() use ($id, $db) {
return $db->find("boards", ["id" => $id]);
});
4-6. DxTheme.php — 테마 파일 해석기
테마 파일 경로를 결정하는 폴백 체인 엔진입니다. 현재 테마에 파일이 없으면 자동으로 default 테마로 폴백합니다.폴백 체인
// 게시판 목록 예시: themes/my-theme/board/list.php 없으면
// themes/default/board/list.php 사용
$file = DxTheme::getInstance()->resolve("board/list.php");
주요 메서드
| 메서드 | 설명 |
|---|---|
| resolve($relPath) | 테마 파일 절대 경로 반환 (폴백 체인 적용) |
| resolveBoardSkin($skin, $action) | 게시판 스킨 파일 경로 반환 |
| getTheme() | 현재 테마명 반환 |
| getThemeDir() | 현재 테마 폴더 절대 경로 |
| getMeta($theme) | theme.json 메타 배열 반환 |
| getAll() | 설치된 모든 테마 목록 |
4-7. router/Router.php + Dispatcher.php — URL 라우팅
Router는 URL을 파싱해 라우트 타입과 파라미터를 결정하고, Dispatcher는 라우트에 맞는 핸들러를 실행합니다.Router 타입 상수
| 상수 | 값 | URL 패턴 예시 |
|---|---|---|
| TYPE_HOME | home | / |
| TYPE_BOARD | board | /free, /free/view/123, /free/write |
| TYPE_PAGE | page | /about, /contact |
| TYPE_ADMIN | admin | /admin, /admin/boards |
| TYPE_AUTH | auth | /auth/login, /auth/register |
| TYPE_API | api | /api/upload, /api/comment |
| TYPE_SEARCH | search | /?s=검색어 |
| TYPE_404 | 404 | 존재하지 않는 경로 |
Dispatcher 실행 흐름
- Router::resolve() 호출 → 라우트 타입•파라미터 결정
- $GLOBALS["dx_route"] 에 라우트 정보 저장
- extend/middle/ 실행
- 라우트 타입에 따라 dispatchHome / dispatchBoard / dispatchAdmin 등 실행
- boards/handler.php 또는 DxTheme 폴백으로 파일 렌더링
URL Rewrite 없는 환경 지원
Apache mod_rewrite나 Nginx 없는 저가형 공유호스팅에서는 ?_url=/free/view/123 방식으로 자동 전환됩니다.
4-8. hook/HookManager.php — 훅 시스템
WordPress 훅과 유사한 액션/필터 시스템입니다. 훅에 콜백을 등록하면 해당 포인트에서 자동으로 실행됩니다.주요 훅 포인트
| 훅 이름 | 위치 | 설명 |
|---|---|---|
| dx_head | <head> 내부 | 커스텀 CSS/JS 태그 삽입 |
| dx_body_bottom | </body> 직전 | 공통 스크립트 삽입 |
| dx_board_list_context | 목록 컨텍스트 생성 후 | 목록 데이터 수정 |
| dx_board_view_context | 보기 컨텍스트 생성 후 | 게시글 데이터 수정 |
| dx_board_after_save | 게시글 저장 후 | 저장 후처리 (알림, 통계 등) |
| dx_admin_top | 관리자 본문 상단 | 관리자 페이지 커스텀 |
| dx_editor_render | 에디터 렌더링 시 | 에디터 커스텀 |
| dx_extend_top | extend/top/ 실행 후 | 추가 초기화 |
| dx_extend_middle | extend/middle/ 실행 후 | 라우팅 후 처리 |
| dx_extend_bottom | extend/bottom/ 실행 후 | 렌더링 후 처리 |
// 훅 등록
dx_add_hook("dx_head", function() {
echo '<link rel="stylesheet" href="/my.css">';
}, 10); // 세 번째 인자: 우선순위 (낮을수록 먼저)
// 필터 훅 (값 변형)
dx_add_filter("dx_board_list_context", function($ctx) {
$ctx["extra_data"] = "추가 데이터";
return $ctx;
});
// 훅 실행 (코어에서 호출)
dx_run_hook("dx_head");
$ctx = dx_apply_filter("dx_board_list_context", $ctx);
4-9. auth/Auth.php — 세션 기반 인증
로그인•로그아웃•회원 정보 관리를 담당합니다. 세션에 사용자 정보를 저장하고, 권한 검사 메서드를 제공합니다.주요 메서드
| 메서드 | 설명 |
|---|---|
| getInstance() | 싱글턴 인스턴스 |
| login($loginId, $password) | 로그인 처리, 성공 시 세션 저장 |
| logout() | 로그아웃 (세션 파괴) |
| check() | 로그인 여부 확인 (bool) |
| user() | 현재 로그인 회원 배열 반환 |
| id() | 현재 회원 ID 반환 |
| isAdmin() | 관리자 여부 확인 |
| can($permission) | 특정 권한 확인 |
| loginById($memberId) | 관리자 대리 로그인 |
| refresh() | 세션의 회원 정보 갱신 (프로필 수정 후) |
$auth = Auth::getInstance();
if (!$auth->check()) {
dx_redirect("/auth/login");
}
$user = $auth->user();
echo $user["nickname"] . "님 환영합니다.";
if ($auth->isAdmin()) {
// 관리자 전용 코드
}
4-10. DxSanitizer.php — HTML 입력 정제
에디터로 작성된 HTML을 서버사이드에서 XSS 공격으로부터 보호합니다. 허용 태그 화이트리스트 방식으로 동작하며, script•iframe 등 위험 태그는 완전 차단합니다.
화이트리스트 허용 태그
텍스트: p, br, b, strong, i, em, u, s, strike, del 구조: h1~h6, ul, ol, li, blockquote, pre, code 링크/미디어: a, img, video, source 테이블: table, thead, tbody, tfoot, tr, th, td 기타: span, div, hr, sup, sub, figure, figcaption
차단 태그
script, iframe, object, embed, form, input, style, link, meta, svg, canvas, frame
주요 메서드
| 메서드 | 설명 |
|---|---|
| DxSanitizer::html($content) | 에디터 HTML 정제 (화이트리스트 필터링) |
| DxSanitizer::text($input) | 일반 텍스트 정제 (태그 완전 제거) |
| DxSanitizer::filename($filename) | 파일명 안전하게 정제 |
| DxSanitizer::url($url) | URL 안전성 검증 |
4-11. DxSeo.php — SEO 메타 자동 생성
페이지 타입(게시글•목록•홈 등)에 따라 title, description, OG 태그, Twitter Card, JSON-LD 구조화 데이터를 자동 생성합니다.지원 페이지 타입
| 타입 값 | 해당 페이지 | 자동 생성 내용 |
|---|---|---|
| board_view | 게시글 보기 | 제목, 본문 앞 150자 설명, 첫 이미지 OG, Article JSON-LD |
| board_list | 게시판 목록 | 게시판명 + 카테고리 title, 게시판 설명 |
| page | 정적 페이지 | 페이지 title, 설명 |
| home | 홈 페이지 | 사이트명, 기본 설명, 기본 OG 이미지 |
| search | 검색 결과 | noindex 자동 설정 |
자동 처리 항목
- 비밀글 → noindex 자동 설정
- ?s= 검색 결과 → noindex 자동 설정
- canonical URL → 쿼리스트링 제거
- 본문 첫 이미지 → OG 이미지 자동 추출
- Google Analytics ID → 레이아웃 자동 삽입
- 멀티도메인 → 도메인별 독립 사이트맵/canonical
4-12. DxCategory.php — 카테고리 렌더링
게시판 카테고리를 DB에서 조회하고, 계층 트리 구조로 변환하며, 스킨 파일로 렌더링합니다.
카테고리 스킨 우선순위
// 우선순위 (높은 것부터)
1. themes/[테마]/category/list.php ← 테마별 커스텀
2. boards/category/skins/[스킨]/list.php ← 지정 스킨
3. boards/category/skins/default/list.php ← 기본 폴백
주요 메서드
| 메서드 | 설명 |
|---|---|
| getByBoard($boardId, $showIn) | 게시판 카테고리 목록 (showIn: list/view/전체) |
| buildTree($rows, $parentId) | 평면 배열 → 계층 트리 변환 |
| flattenTree($tree, $depth) | 트리 → select 옵션용 평면 배열 (들여쓰기 포함) |
| renderTabs($boardId, $skin, $active) | 카테고리 탭 HTML 렌더링 |
4-13. DxSite.php — 멀티사이트 관리
같은 DB와 코드베이스로 여러 도메인을 운영할 수 있게 합니다. 도메인을 감지하고, dx_sites 테이블에서 해당 도메인의 설정을 로드해 전역 $dx_config를 오버라이드합니다.
// dx_sites 테이블 예시
// domain | theme | menu_group | site_name
// example.com | default | main | 메인사이트
// shop.example.com| clean | shop | 쇼핑몰
// blog.example.com| simple | blog | 블로그
도메인별 독립 항목
- 사이트명•설명•기본 URL
- 활성 테마
- 메뉴 그룹
- 언어•시간대
- SEO 사이트맵 (각 도메인 기준으로 독립 생성)
4-14. DxExtend.php — extend/ 폴더 자동 실행
extend/ 폴더의 PHP 파일을 지정된 시점에 자동으로 실행하는 엔진입니다. 파일명 오름차순 실행, 에러 격리, 하위 폴더 재귀 탐색을 지원합니다.
// 실행 포인트 예시
// extend/top/01_maintenance.php — 점검 모드
if (file_exists(DX_ROOT . "/maintenance.flag")) {
header("HTTP/1.1 503 Service Unavailable");
echo "점검 중입니다.";
exit;
}
// extend/middle/01_visit_tracker.php — 방문자 로그 (기본 포함)
// extend/bottom/99_perf_log.php — 성능 측정
4-15. DxPoint.php — 포인트/경험치/레벨
회원 활동에 따른 포인트 지급/차감, 경험치 누적, 레벨 계산을 담당합니다. 레벨은 1(새싹)~15(전설왕)까지 있습니다.기본 포인트 규칙
| 활동 타입 | 포인트 | 경험치 |
|---|---|---|
| signup (회원가입) | 10 | 20 |
| login (로그인) | 1 | 2 |
| write (글쓰기) | 5 | 10 |
| comment (댓글) | 2 | 5 |
| like_recv (좋아요 받음) | 1 | 2 |
| scrap_recv (스크랩 받음) | 1 | - |
레벨 임계값 (누적 경험치 기준)
Lv.1(0) → Lv.2(50) → Lv.3(150) → Lv.4(350) → Lv.5(700) → Lv.6(1,200) → Lv.10(8,000) → Lv.15(50,000)
주요 메서드
| 메서드 | 설명 |
|---|---|
| DxPoint::add($memberId, $type, $point) | 포인트 지급/차감 (0이면 규칙 자동 적용) |
| DxPoint::addExp($memberId, $type, $exp) | 경험치 지급 + 레벨업 처리 |
| DxPoint::getPoint($memberId) | 현재 포인트 잔액 |
| DxPoint::getExp($memberId) | 현재 누적 경험치 |
| DxPoint::getLevel($memberId) | 현재 레벨 숫자 |
| DxPoint::getLevelName($level) | 레벨 이름 문자열 |
| DxPoint::getLevelInfo($memberId) | 레벨•경험치•다음레벨 까지 정보 배열 |
4-16. DxNotification.php — 실시간 알림
댓글•친구추가•스크랩•쪽지 등의 이벤트를 DB에 저장하고, DXMB socket 플러그인이 활성화된 경우 실시간 push를 보냅니다.알림 타입 상수
| 상수 | 값 | 발생 시점 |
|---|---|---|
| TYPE_COMMENT | comment | 내 게시글에 댓글 달림 |
| TYPE_COMMENT_REPLY | comment_reply | 내 댓글에 답글 달림 |
| TYPE_FRIEND | friend | 친구 추가됨 |
| TYPE_SCRAP | scrap | 내 게시글이 스크랩됨 |
| TYPE_MEMO | memo | 쪽지 수신 |
// 알림 전송 예시
DxNotification::add(
$postAuthorId, // 수신자 member.id
$commentAuthorId, // 발신자 member.id
DxNotification::TYPE_COMMENT,
$commenterName . "님이 댓글을 남겼습니다.",
dx_base_url($board["board_key"] . "/view/" . $postId)
);
4-17. DxSocialAuth.php — 소셜 로그인
카카오•네이버•구글•GitHub OAuth2 소셜 로그인을 처리합니다. 각 제공자의 API 키는 관리자 설정에서 입력합니다.| 제공자 | 필요 설정 키 | 특이사항 |
|---|---|---|
| kakao | social_kakao_client_id/secret | 카카오톡 공유 스크랩도 봇 허용 |
| naver | social_naver_client_id/secret | Naver Login API 사용 |
| social_google_client_id/secret | Google OAuth 2.0 | |
| github | social_github_client_id/secret | GitHub OAuth Apps |
주요 메서드
| 메서드 | 설명 |
|---|---|
| providers() | 지원 제공자 목록 배열 |
| enabledProviders() | 관리자에서 활성화된 제공자만 반환 |
| getAuthUrl($provider) | OAuth 인증 URL 생성 |
| handleCallback($provider, $code) | 콜백 코드로 사용자 정보 수신•로그인 처리 |
| providerMeta($provider) | 제공자 표시명•색상•아이콘 정보 |
4-18. DxContainer.php — 서비스 컨테이너 (DI)
라라벨 Service Container와 동일한 철학으로 구현된 경량 DI 컨테이너입니다. 기존 getInstance() 싱글턴과 100% 호환됩니다.
// 바인딩
dx_app()->bind("mailer", function() {
return new MyMailer(dx_config("smtp_host"));
});
// 싱글턴 바인딩
dx_app()->singleton("sms", function() {
return new AlimtalkSMS(dx_config("alimtalk_key"));
});
// 꺼내 쓰기
$mailer = dx_make("mailer");
$mailer->send("to@example.com", "제목", "내용");
// 컨트롤러 자동 의존성 주입
dx_app()->call("BoardController@index");
4-19. DxMarket.php — 마켓 클라이언트
designonex.com 중앙 마켓 서버에서 플러그인과 테마를 검색•설치합니다. SHA-256 해시 검증으로 파일 무결성을 보장합니다.
설치 흐름
- getMarket() → 마켓에서 플러그인/테마 목록 탐색
- installItem() → 중앙 서버에서 download_url + file_hash 수신
- 개발자 서버에서 ZIP 직접 다운로드
- SHA-256 해시 검증 후 설치
4-20. PluginRegistry.php — 플러그인 등록소
플러그인이 자신의 타입(editor•payment•captcha•sms 등)을 등록하면, 관리자 설정 화면에 자동으로 선택 옵션이 나타납니다.지원 플러그인 타입
| 타입 | 용도 | 활성 설정 키 |
|---|---|---|
| editor | 게시글 작성 에디터 | active_editor |
| payment | 결제 모듈 | active_payment |
| captcha | CAPTCHA 인증 | active_captcha |
| sms | SMS 발송 | active_sms |
| socket | 실시간 WebSocket | active_socket |
| (임의) | 개발자 정의 타입 | (임의 설정 키) |
5. 요청 처리 전체 흐름
브라우저에서 요청이 들어와 응답이 나가기까지의 전체 과정입니다.
브라우저 → HTTP 요청
↓
.htaccess / web.config / Nginx → index.php 로 모든 요청 전달
↓
index.php
1. DX_CMS 상수 정의 (직접 접근 차단용)
2. data/config.php 로드 (DB 접속 정보)
3. core 클래스 파일 require (Secure, functions, Database, ...)
4. Secure::initSession() — 세션 + 보안 헤더
5. Secure::WAF 검사 — SQL Injection, XSS 패턴 탐지
6. Database::connect() — DB 연결
7. 설정 로드 (dx_settings 테이블 → DxCache)
8. DxSite::getInstance() — 멀티사이트 도메인 감지
9. DxTheme::getInstance() — 테마 결정
10. load_plugins() — plugins/ 플러그인 로드
11. DxExtend::runTop() — extend/top/ 실행
↓
Router::resolve() — URL → 라우트 타입•파라미터 결정
↓
DxExtend::runMiddle() — extend/middle/ 실행
↓
Dispatcher::dispatch()
├─ home → themes/[테마]/page/home.php
├─ board → boards/handler.php → 스킨 파일
├─ page → 정적 페이지 렌더링
├─ admin → admin/[섹션]/index.php
├─ auth → core/auth/[페이지].php
├─ api → core/api/[핸들러].php
└─ 404 → themes/[테마]/page/404.php
↓
DxExtend::runBottom() — extend/bottom/ 실행
↓
ob_end_flush() → 브라우저로 응답 전송
6. 확장 개발 가이드
DxCMS의 세 가지 확장 방법과 각각의 적합한 사용 사례를 정리합니다.
| 방법 | 난이도 | 적합한 경우 | 파일 위치 |
|---|---|---|---|
| extend/ | 쉬움 | 점검모드, IP차단, 방문 로그, 간단한 초기화 | extend/top|middle|bottom/ |
| 훅(Hook) | 보통 | 데이터 변형, 헤더/푸터 삽입, 저장 후 처리 | plugin.php 또는 extend 파일 |
| 플러그인 | 어려움 | 에디터, 결제, SMS, 소켓 등 독립 기능 모듈 | plugins/my-plugin/ |
6-1. 커스텀 테마 만들기
- themes/my-theme/ 폴더 생성
- theme.json 파일 생성 (이름•버전 정보)
- 변경하고 싶은 파일만 추가 (없는 파일은 default 테마 자동 폴백)
- 관리자 > 테마 관리에서 활성화
6-2. 게시판 커스텀 스킨 만들기
- boards/skins/my-skin/ 폴더 생성
- skin.json 파일 생성 (스킨 이름•버전)
- list.php, view.php, write.php 생성
- 관리자 > 게시판 관리에서 해당 게시판에 스킨 지정
6-3. 플러그인 개발 3단계
- Step 1: plugins/my-plugin/ 폴더와 plugin.php 생성
- Step 2: plugin.php에서 dx_register_plugin() 호출
- Step 3: 해당 타입의 훅(dx_editor_init 등)에서 기능 구현
// plugins/my-editor/plugin.php
dx_register_plugin(array(
'id' => 'my-editor',
'type' => 'editor',
'name' => 'My Custom Editor',
'version' => '1.0.0',
));
dx_add_hook('dx_editor_render', function() {
echo '<script src="/plugins/my-editor/editor.js"></script>';
});
7. 보안 체크리스트
| 항목 | 구현 방법 | 담당 파일 |
|---|---|---|
| CSRF 방어 | 모든 POST 폼에 dx_csrf_field() 삽입, 수신 시 dx_csrf_check() | Secure.php |
| XSS 방어 | 출력 시 dx_esc(), 에디터 HTML은 DxSanitizer::html() |
Secure.php, |
| SQL Injection | PDO 파라미터 바인딩 (? 플레이스홀더) | Database.php |
| 업로드 보안 | MIME+확장자 이중 검증, 이중확장자 차단, 업로드 디렉토리 PHP 실행 차단 | Secure.php, core/api/upload.php |
| 경로 이탈 방어 | dx_path_inside() 로 업로드•다운로드 경로 검증 | functions.php |
| Rate Limit | Secure::rateLimit() 또는 Secure.php 자동 적용 | Secure.php |
| WAF | SQL/XSS 패턴 자동 탐지 (에디터 필드 제외) | Secure.php |
| 세션 보안 | HttpOnly•Secure•SameSite=Lax 자동 설정 | Secure.php |
| data/ 보안 | .htaccess로 PHP 실행 + 디렉토리 리스팅 차단 | data/.htaccess |
| install/ 보안 | 설치 후 폴더 삭제 | - |
✅ 보안 패치가 필요한 경우 core/Secure.php 파일 하나만 교체하면 됩니다. 이 파일은 다른 CMS 기능과 분리되어 있습니다.
8. 캐시 및 성능 최적화
8-1. 동접 1만명 권장 서버 구성
| 컴포넌트 | 설정 권장값 | 효과 |
|---|---|---|
| PHP OPcache | opcache.enable=1, emory_consumption=256 |
PHP 파싱 캐시 → 응답 2~5배 향상 |
| PHP-FPM | pm.max_children=200~500 | 동시 요청 처리 수 증가 |
| Redis | REDIS_SESSION_URL 설정 | 설정•게시판 캐시 메모리 저장 |
| MySQL | innodb_buffer_pool_size=RAM 70% | DB 쿼리 캐시 |
| Nginx 정적파일 | CSS/JS/이미지 직접 서빙 | PHP 히트 감소 |
| Cloudflare CDN | 앞단 CDN 적용 | 실제 PHP 히트 80% 감소 |
8-2. 세션 최적화
비로그인 GET 요청은 세션을 시작하지 않습니다. 이로 인해 파일 lock이 제거되어 동시 처리 성능이 향상됩니다.
8-3. 캐시 자동 무효화
설정 저장 → 전체 캐시 flush게시글 작성/수정/삭제 → 해당 게시판 목록 캐시 삭제
카테고리 변경 → 전체 게시판 목록 캐시 삭제