1. 시작 전 필독
DXCMS는 PHP 5.6 ~ 8.x 를 모두 지원합니다. 아래 규칙을 어기면 구형 서버에서 Fatal Error 가 발생하거나, 데이터가 손상될 수 있습니다. 막코딩이라도 이 규칙만큼은 반드시 지켜주세요.| 항목 | 올바른 값 | 이유 |
| PHP 버전 | 5.6 이상 호환 작성 | 공유호스팅 PHP 5.6 다수 사용 |
| 문자셋 | UTF-8 필수 | 한글 깨짐 방지 |
| DX_CMS 상수 | defined('DX_CMS') 체크 필수 | 직접 접근 차단 |
| BIGINT ID | 문자열 그대로 사용 | 32bit (int) 오버플로우 방지 |
2. PHP 5.6 호환 — 절대 쓰면 안 되는 문법
2-1. ?? null 병합 연산자 금지
❌ 틀림 (PHP 7+)
$name = $user['name'] ?? '기본값'; // ❌ PHP 5.6 Fatal Error
✅ 맞음 (PHP 5.6 호환)
$name = isset($user['name']) ? $user['name'] : '기본값';
2-2. 배열 단축 문법 [] 금지
❌ 틀림
$arr = ['key' => 'value']; // ❌ PHP 5.3 미만에서 에러
✅ 맞음
$arr = array('key' => 'value'); // ✅
2-3. IIFE (즉시실행 익명함수) 금지
❌ 틀림 (PHP 7+)
(function() {
define('MY_CONST', true);
})(); // ❌ PHP 5.6 에서 동작 안 함
✅ 맞음
if (!defined('MY_CONST')) {
define('MY_CONST', true);
}
2-4. 훅에 클로저(익명함수) 금지
dx_add_hook() 의 콜백에는 반드시 일반 함수명(문자열)을 사용해야 합니다.❌ 틀림
dx_add_hook('dx_after_login', function($args) { // ❌ 클로저 금지
// ...
});
✅ 맞음
dx_add_hook('dx_after_login', 'my_login_handler');
function my_login_handler($args) {
// ...
}
3. BIGINT ID — 절대 (int) 캐스팅 금지
DXCMS 의 id 컬럼은 BIGINT(밀리초 타임스탬프, 예: 1747123456789) 입니다. 32bit PHP 에서 (int) 캐스팅하면 오버플로우로 음수나 잘못된 값이 됩니다.❌ 틀림
$id = (int)$user['id']; // ❌ 32bit 오버플로우
$pid = (int)$_GET['post_id']; // ❌
$row = $db->row("... WHERE id=" . (int)$id); // ❌
✅ 맞음
$id = $user['id']; // ✅ 문자열 그대로
$pid = dx_get('post_id', '', 'bigint'); // ✅ dx_get bigint 타입 사용
$row = $db->row("... WHERE id=?", array($id)); // ✅ 파라미터 바인딩
⚠ URL 에 넣을 때도 그대로 사용합니다.
// ✅ BIGINT 문자열 그대로 URL에 사용
$url = dx_base_url('view/' . $post['id']);
// ✅ 파라미터 바인딩으로 DB 쿼리
$post = $db->row("SELECT * FROM `{$db->table('posts')}` WHERE id=? LIMIT 1",
array($post['id']) // 문자열 그대로
);
4. DB 쿼리 안전 패턴
4-1. 반드시 파라미터 바인딩 사용
? 자리에 배열로 값을 전달하면 SQL 인젝션이 자동으로 방어됩니다.❌ 틀림 (SQL 인젝션 위험)
$db->row("SELECT * FROM dx_posts WHERE id=" . $id); // ❌
✅ 맞음
$db->row("SELECT * FROM `{$db->table('posts')}` WHERE id=? LIMIT 1",
array($id)
);
4-2. 테이블명은 $db->table() 사용
테이블 prefix(기본 dx_)가 자동으로 붙습니다. 하드코딩 금지.
$db->table('posts') // → 'dx_posts'
$db->table('members') // → 'dx_members'
$db->table('boards') // → 'dx_boards'
4-3. try/catch 감싸기
테이블이 없거나 컬럼명이 다른 서버에서 500 에러가 나지 않도록 항상 감쌉니다.
$rows = array();
try {
$rows = $db->rows(
"SELECT id, title FROM `{$db->table('posts')}` WHERE status=1 LIMIT 10"
);
} catch (Exception $e) {
// 조용히 무시하거나 로그 기록
}
4-4. dx_members 주요 컬럼명
그누보드와 컬럼명이 다릅니다. 헷갈리지 마세요.| 그누보드 | DXCMS | 비고 |
| mb_id | login_id | 로그인 아이디 |
| mb_name | name | 회원 이름 |
| mb_email | ||
| mb_point | point | |
| mb_level | level | |
| mb_datetime | join_date | 가입일 (created_at ❌) |
| (없음) | id | BIGINT 식별자 — (int) 캐스팅 금지 |
5. 출력 보안 — XSS 방지
HTML 에 데이터를 출력할 때는 반드시 htmlspecialchars 를 사용합니다.❌ 틀림 (XSS 위험)
echo $user['name']; // ❌
echo $_GET['keyword']; // ❌
✅ 맞음
echo htmlspecialchars($user['name'], ENT_QUOTES, 'UTF-8');
echo htmlspecialchars(dx_get('keyword', ''), ENT_QUOTES, 'UTF-8');
6. POST 처리 — CSRF 보안
POST 요청은 반드시 첫 줄에 CSRF 검증을 해야 합니다. 실패하면 자동으로 종료됩니다.
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST') {
dx_csrf_check(); // ← 반드시 첫 줄! 실패 시 자동 exit
$title = dx_post('title', ''); // $_POST 안전하게 가져오기
$id = dx_post('id', '', 'bigint'); // BIGINT 타입
}
HTML 폼에 CSRF 토큰을 반드시 추가합니다.
<form method="post">
<?php echo dx_csrf_field(); ?> <!-- hidden input 자동 생성 -->
<input type="text" name="title">
<button type="submit">저장</button>
</form>
7. 직접 접근 차단
core/, api/, extend/ 등 모든 PHP 파일 첫 줄에 반드시 추가합니다.
<?php
if (!defined('DX_CMS')) exit('Direct access not allowed.');
// 이 아래부터 실제 코드
8. 외부 파일에서 DXCMS 사용 (Direct 방식)
그누보드의 _common.php 처럼 dx_load.php 한 줄로 DXCMS 전체 환경을 불러옵니다.
// 방법 1: dx_load.php 가 같은 폴더
require_once 'dx_load.php';
// 방법 2: 어느 폴더에서든 자동 탐색 (권장)
$p = __DIR__;
while (!file_exists($p . '/dx_load.php')) {
$pp = dirname($p);
if ($pp === $p) break;
$p = $pp;
}
require_once $p . '/dx_load.php';
// 이후 바로 DXCMS 전체 기능 사용 가능
$db = Database::getInstance();
$auth = Auth::getInstance();
$user = dx_is_login() ? $auth->user() : null;
9. 자주 쓰는 공용 함수 요약
| 함수 | 설명 | 그누보드 대응 |
| dx_is_login() | 로그인 여부 bool | is_login() |
| dx_is_admin() | 관리자 여부 bool | is_admin() |
| dx_base_url($path) | 사이트 URL 생성 | G5_URL |
| dx_current_url() | 현재 페이지 전체 URL | — |
| dx_redirect($url) | 리다이렉트 + exit | goto_url() |
| dx_config($key, $default) | 사이트 설정 조회 | $config[] |
| dx_get($key, $default) | $_GET 안전 취득 | 직접 접근 |
| dx_post($key, $default) | $_POST 안전 취득 | 직접 접근 |
| dx_method('POST') | 요청 메서드 확인 | — |
| dx_csrf_check() | CSRF 검증 (실패 → exit) | check_token() |
| dx_csrf_field() | hidden input 출력 | get_token(true) |
| dx_json($data) | JSON 응답 + exit | — |
| dx_ip() | 접속자 IP (프록시 고려) | G5_IP |
| dx_pagination(...) | 페이지네이션 HTML 반환 | get_paging() |
| dx_board_posts($key, $n) | 게시판 최신글 배열 반환 | — |
| DxPoint::add($id, $n, $msg) | 포인트 지급 | insert_point() |
| dx_theme_file($path) | 테마 파일 절대경로 반환 | — |
10. 테마 레이아웃(헤더/푸터) 적용
Direct 파일에서 사이트 테마를 그대로 사용할 수 있습니다.
ob_start(); // ← 출력 캡처 시작
?>
<div>내 HTML 콘텐츠</div>
<?php
$dx_content = ob_get_clean(); // ← 캡처 완료
// type='direct' : 사이드바 없이 전체 폭으로 출력
$context = array('type' => 'direct');
$_layoutFile = dx_theme_file('layout/main.php');
if ($_layoutFile && file_exists($_layoutFile)) {
include $_layoutFile; // ← 테마가 $dx_content 를 감싸서 출력
} else {
echo $dx_content;
}
11. 커밋 전 체크리스트
- ?? 연산자 사용 없음 → isset() ? : 로 대체
- 배열을 array() 로 작성 ([] 단축 없음)
- IIFE (function(){})() 없음
- 훅 콜백이 문자열 함수명 (클로저 없음)
- BIGINT id 에 (int) 캐스팅 없음
- DB 쿼리에 파라미터 바인딩 array() 사용
- 테이블명 $db->table('name') 사용
- HTML 출력 htmlspecialchars 적용
- POST 첫 줄에 dx_csrf_check() 호출
- 파일 첫 줄에 defined('DX_CMS') 체크
- dx_members 컬럼명 join_date / login_id 사용