회원가입 | 고객센터 |
DESIGNONEX
dxcms.kr
로그인 회원가입
고객센터
3.9 공통 함수 / 유틸

전역 함수 구조

D DX
2026.04.21 01:01(수정됨) 136 0

1. 전역 함수 라이브러리 개요

core/functions.php는 DXCMS 전체에서 가장 먼저 로드되는 공통 함수 라이브러리입니다. 이 파일이 다른 클래스들보다 먼저 실행되어야 하기 때문에, Database, HookManager, Auth 등 클래스를 직접 인스턴스화하지 않고 순수 함수 정의만 담습니다. 대신 클래스가 로드된 후 호출되면 클래스 기능을 위임(delegate)합니다.


1.1 로드 순서와 역할

// index.php STEP 1에서 require_once 순서
require_once DX_CORE . '/functions.php';        // ← 1번째: 전역 함수 정의
require_once DX_CORE . '/DxCache.php';           // 2번째
require_once DX_CORE . '/hook/HookManager.php';  // 3번째
require_once DX_CORE . '/PluginRegistry.php';    // 4번째
// ...

// functions.php가 먼저 로드되어야 하는 이유:
// • dx_config(), dx_log() 등이 다른 클래스 __construct에서 사용됨
// • dx_random_bytes()가 Secure.php 세션 초기화에 필요
// • dx_is_https()가 세션 쿠키 Secure 플래그에 필요


1.2 함수 그룹 전체 지도

그룹 함수 수 주요 함수 특이사항
보안 난수 2 dx_random_bytes, dx_random_hex PHP 5.6~8.x 폴백 체인
경로 유틸 2 dx_realpath, dx_path_inside 윈도우 백슬래시 처리
HTTPS 감지 1 dx_is_https Cloudflare·IIS·리버스프록시 지원
URL 생성 5 dx_base_url, dx_static_url, dx_request_uri, dx_current_url, dx_redirect 이중슬래시 제거 자동화
요청 헬퍼 5 dx_get, dx_post, dx_request, dx_method, dx_cast 타입 캐스팅 내장
IP 주소 1 dx_ip CF·프록시 우선순위 체인
UA / 디바이스 4 dx_ua, dx_device, dx_os, dx_browser mobile·tablet·pc 구분
설정 3 dx_config, dx_set_config, dx_config_set $dx_config 전역 배열 래퍼
보안 4 dx_esc, dx_safe_url, dx_csrf_token, dx_csrf_field, dx_csrf_check Secure.php 위임
응답 2 dx_error, dx_json HTTP 상태코드 + exit
플래시 메시지 3 dx_set_flash, dx_flash, dx_get_flash 세션 기반 1회성 메시지
문자열 2 dx_substr, dx_mb_substr mb_* 폴백
날짜 1 dx_date 타임스탬프·문자열 양쪽 지원
업로드 2 dx_upload_url, dx_upload_exists data/uploads/ 기준
배열·기타 3 dx_array_column, dx_time_ago, dx_filesize PHP 5.4 폴백
페이지네이션 1 dx_pagination HTML 문자열 반환
로그 1 dx_log info/debug는 기록 안 함
AJAX 1 dx_is_ajax X-Requested-With 감지
게시판 헬퍼 2 dx_board_posts, dx_board_latest 홈 제작 전용
테마 자산 2 dx_head_assets, dx_font_css Pretendard + Space Grotesk


2. 보안 난수 함수

암호화에 사용할 수 있는 예측 불가능한 난수 바이트를 생성합니다. PHP 버전과 서버 환경에 따라 자동으로 최적 방법을 선택하는 폴백 체인이 핵심입니다.


2.1 dx_random_bytes($length)

dx_random_bytes(int $length) string 암호화 안전 난수 바이트 생성
 
매개변수 타입 기본값 설명
$length int (필수) 생성할 바이트 수


PHP 버전과 서버 환경에 따라 4단계 폴백 체인을 사용합니다.

// 폴백 체인 (성능 + 보안 최우선순)
// 1단계: PHP 7.0+ 내장 (CSPRNG, 가장 안전)
if (function_exists('random_bytes')) {
    return random_bytes($length);
}

// 2단계: PHP 5.x + OpenSSL (CSPRNG 수준)
if (function_exists('openssl_random_pseudo_bytes')) {
    $strong = false;
    $bytes  = openssl_random_pseudo_bytes($length, $strong);
    if ($bytes !== false) return $bytes;
}

// 3단계: PHP 5.x + mcrypt (구형 서버)
if (function_exists('mcrypt_create_iv')) {
    $bytes = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
    if ($bytes !== false && strlen($bytes) === $length) return $bytes;
}

// 4단계: mt_rand 폴백 (보안 취약 — 최후 수단)
$bytes = '';
for ($i = 0; $i < $length; $i++) { $bytes .= chr(mt_rand(0, 255)); }
return $bytes;


2.2 dx_random_hex($length)

dx_random_hex(int $length = 32) string 지정 길이의 16진수 난수 문자열 반환
 
매개변수 타입 기본값 설명
$length int 32 반환할 16진수 문자열 길이
 
// 사용 예
$token = dx_random_hex(32);  // 32자리 hex 문자열 (예: 'a3f8c1...')
$key   = dx_random_hex(64);  // 64자리 hex 문자열

// 내부: bin2hex(dx_random_bytes(ceil(length/2)))
// 바이트를 hex로 변환 → 길이가 2배 → ceil(32/2)=16바이트 생성

// 실제 활용: CSRF 토큰, API 키, 세션 ID 등
$csrf  = dx_random_hex(32);   // CSRF 토큰
$apiKey = dx_random_hex(48);  // API 키


3. 경로 유틸리티 함수

파일 경로를 안전하게 처리합니다. Windows(백슬래시)와 Linux(슬래시)를 동시에 지원하며, 경로 탈출(path traversal) 공격을 방어합니다.


3.1 dx_realpath($path)

dx_realpath(string $path) string|false 실제 절대경로 반환 (백슬래시 정규화)

PHP 내장 realpath()를 래핑하여 Windows 백슬래시를 슬래시로 정규화합니다. 실제 존재하지 않는 경로는 false를 반환합니다.
 
// PHP 내장 realpath() + 백슬래시 정규화
$path = str_replace('\\\\', '/', $path);
$real = realpath($path);
return $real ? str_replace('\\\\', '/', $real) : false;

// 예시
$real = dx_realpath('/var/www/html/extend/../top/01_maint.php');
// → '/var/www/html/top/01_maint.php'

// Windows 경로도 동일하게 처리
$real = dx_realpath('C:\\inetpub\\wwwroot\\extend\\top\\file.php');
// → 'C:/inetpub/wwwroot/extend/top/file.php'


3.2 dx_path_inside($path, $base)

dx_path_inside(string $path, string $base) bool $path가 $base 디렉토리 내부인지 검증
 
매개변수 타입 기본값 설명
$path string (필수) 검사할 파일/디렉토리 경로
$base string (필수) 기준 베이스 디렉토리 경로

경로 탈출(path traversal) 공격 방어를 위해 DxExtend, Dispatcher 등 파일 로더가 실행 전 반드시 호출합니다.
 
// ① 두 경로 모두 realpath()로 정규화
$path = dx_realpath($path);
$base = dx_realpath($base);
if ($path === false || $base === false) return false;

// ② base 경계 검증
$base = rtrim($base, '/');

// ③ Windows는 대소문자 무시 비교 (NTFS 특성)
if (DIRECTORY_SEPARATOR === '\\\\') {
    return stripos($path, $base . '/') === 0 || strtolower($path) === strtolower($base);
}
return strpos($path, $base . '/') === 0 || $path === $base;

// 실제 사용 예 (Dispatcher.php)
$fullPath = DX_PAGES . '/' . ltrim($filePath, '/');
if (!dx_path_inside($fullPath, DX_PAGES)) {
    $this->dispatch404(); return;
}
// ../../../etc/passwd 같은 경로 탈출 시도를 원천 차단


4. URL 생성 함수

루트 설치, 서브디렉토리 설치, IIS URL Rewrite 없는 환경, Cloudflare 등 다양한 환경에서 올바른 URL을 생성합니다. 이중 슬래시(//) 제거와 자동 도메인 감지가 핵심입니다.


4.1 내부 헬퍼: _dx_resolve_base()

모든 URL 생성 함수가 공유하는 내부 헬퍼입니다. site_url 설정값과 실제 요청 도메인을 비교하여 불일치 시 자동 감지로 전환합니다. static 변수로 캐싱하여 요청당 1회만 계산합니다.
 
// static 캐싱으로 요청당 1회만 계산
static $_cached = null;
if ($_cached !== null) return $_cached;

// 1단계: site_url 설정값 읽기
$base = dx_config('site_url', '');

// 2단계: 도메인 불일치 감지
if ($base && isset($_SERVER['HTTP_HOST'])) {
    $configHost  = strtolower(parse_url($base, PHP_URL_HOST));
    $requestHost = strtolower(preg_replace('/:\\d+$/', '', $_SERVER['HTTP_HOST']));
    if ($configHost !== $requestHost) $base = '';  // 자동 감지로 전환
}

// 3단계: 자동 감지 (site_url 없거나 불일치 시)
if (!$base) {
    $scheme = dx_is_https() ? 'https' : 'http';
    $host   = $_SERVER['HTTP_HOST'];
    $dir    = dirname($_SERVER['SCRIPT_NAME']);  // 서브디렉토리 추출
    $base   = $scheme . '://' . $host . $dir;
}

// 4단계: 이중 슬래시 제거 후 캐싱
$base = preg_replace_callback('#^(https?://)(.*)$#i', function($m) {
    return $m[1] . preg_replace('#/+#', '/', $m[2]);
}, $base);
$_cached = rtrim($base, '/');


4.2 dx_base_url($path)

dx_base_url(string $path = '') string 내부 절대 URL 생성
 
매개변수 타입 기본값 설명
$path string '' '' 또는 '/'이면 루트 URL, 그 외 경로 문자열
 
// 기본 사용
echo dx_base_url();            // https://example.com/
echo dx_base_url('notice');    // https://example.com/notice
echo dx_base_url('auth/login'); // https://example.com/auth/login

// 서브디렉토리 설치 시
echo dx_base_url('notice');    // https://example.com/cms/notice

// URL Rewrite 없는 IIS 환경
// dx_config('url_rewrite', '1') === '0' 인 경우
echo dx_base_url('notice');    // https://example.com/index.php?_url=/notice

// 테마, extend, 플러그인 파일에서 공통으로 사용
$url = dx_base_url($boardKey . '/view/' . $postId);
$redirect = dx_base_url('auth/login') . '?redirect=' . urlencode(dx_current_url());


4.3 dx_static_url($path)

dx_static_url(string $path = '') string 정적 자산(CSS/JS/이미지) URL 생성

dx_base_url과 동일한 베이스를 사용하지만 URL Rewrite 폴백 없이 항상 직접 경로를 반환합니다. CDN 분리가 필요한 경우 이 함수만 오버라이드하면 됩니다.
 
echo dx_static_url('assets/css/main.css');   // https://example.com/assets/css/main.css
echo dx_static_url('assets/js/app.js');       // https://example.com/assets/js/app.js

// 테마 파일에서
echo '<link rel="stylesheet" href="' . dx_static_url('themes/default/assets/style.css') . '">';


4.4 dx_request_uri()

dx_request_uri() string 이중 슬래시 정규화된 REQUEST_URI 반환

$_SERVER['REQUEST_URI']를 직접 사용하면 //로 시작하는 URI가 들어올 수 있습니다. 이 함수는 이중 슬래시를 정규화하여 반환합니다. Router, Dispatcher 등 모든 URI 처리에서 이 함수를 사용합니다.
 
// 직접 접근 대신 항상 이 함수 사용
// $_SERVER['REQUEST_URI'] = '//notice//view/42' (비정상 요청)
echo dx_request_uri();  // '/notice/view/42' (정규화됨)

// extend/ 파일에서 현재 경로 확인
$uri = dx_request_uri();
if (strpos($uri, '/admin') === 0) return;  // 관리자 경로 건너뜀


4.5 dx_current_url()

dx_current_url() string 현재 요청의 전체 URL (scheme + host + URI)
 
// 현재 URL 전체 반환 (쿼리스트링 포함)
// 현재 요청: GET https://example.com/notice/view/42?page=1
echo dx_current_url();  // 'https://example.com/notice/view/42?page=1'

// 활용: 로그인 후 원래 페이지로 돌아가기
$redirect = dx_base_url('auth/login') . '?redirect=' . urlencode(dx_current_url());
dx_redirect($redirect);


4.6 dx_redirect($url, $code) / dx_redirect_back($fallback)

dx_redirect(string $url, int $code = 302) void (exit) 리다이렉트 후 exit

ob_start 버퍼를 모두 비운 후 header()를 시도하고, 실패 시 HTML meta/JS로 폴백합니다. 카페24 등 일부 공유호스팅에서 header()가 실패하는 환경을 대응합니다.
 
// 기본 302 리다이렉트
dx_redirect(dx_base_url('notice'));

// 301 영구 리다이렉트
dx_redirect(dx_base_url('notice'), 301);

// 이전 페이지로 돌아가기
dx_redirect_back('/');   // Referer 없으면 '/'로

// 내부 동작:
// 1. $GLOBALS['DX_SHUTTING_DOWN'] = true (shutdown 에러 방지)
// 2. ob_end_clean() 반복으로 버퍼 모두 비움
// 3. header('Location: ...') 시도
// 4. HTML meta refresh + JS location.replace 출력 (폴백)
// 5. exit


5. 요청 헬퍼 함수

GET, POST, REQUEST 데이터를 안전하게 읽고 타입 캐스팅합니다. 직접 $_GET/$_POST에 접근하는 대신 이 함수들을 사용하면 타입 안전성과 기본값 처리가 자동으로 이루어집니다.


5.1 dx_get / dx_post / dx_request

dx_get(string $key, mixed $default = '', string $type = 'string') mixed $_GET 값을 타입 캐스팅하여 반환
 
dx_post(string $key, mixed $default = '', string $type = 'string') mixed $_POST 값을 타입 캐스팅하여 반환
 
dx_request(string $key, mixed $default = '', string $type = 'string') mixed $_REQUEST 값을 타입 캐스팅하여 반환
 
매개변수 타입 기본값 설명
$key string (필수) 읽을 파라미터 키
$default mixed '' 키가 없을 때 반환할 기본값
$type string 'string' string | int | float | bool | array | bigint
 
// 타입별 사용 예
$page    = dx_get('page', 1, 'int');        // 없으면 1, int로 변환
$keyword = dx_get('q', '');                 // 없으면 빈 문자열
$postId  = dx_post('id', 0, 'bigint');      // BIGINT 안전 처리 (32bit 오버플로우 방지)
$active  = dx_post('active', false, 'bool');
$tags    = dx_post('tags', array(), 'array');

// bigint 타입의 특별 처리
// PHP 32bit에서 (int) 캐스팅 시 2147483647 초과값 오버플로우 → 문자열로 반환
$id = dx_post('post_id', '0', 'bigint');   // '12345678901234' (문자열)
if (ctype_digit($id) && $id !== '0') { /* 유효한 BIGINT */ }


5.2 dx_method($method)

dx_method(string $method) bool 현재 HTTP 메서드 확인
 
if (dx_method('POST')) {
    // POST 요청 처리
    dx_csrf_check();
    $title = dx_post('title');
}

if (dx_method('DELETE')) {
    // DELETE 요청 처리 (REST API)
}


5.3 dx_cast($val, $type) — 내부 타입 캐스터

타입 문자열 변환 방식 특이사항
'int' (int)$val 정수 변환
'float' (float)$val 실수 변환
'bool' (bool)$val 불리언 변환
'array' (array)$val 배열 변환
'bigint' ctype_digit 검증 후 문자열 반환 32bit PHP 오버플로우 방지, 양의 정수만
'string' trim($val) — 문자열+앞뒤공백제거 기본 타입, 문자열이 아닌 값은 그대로 반환


6. IP 주소 및 User-Agent 함수


6.1 dx_ip()

dx_ip() string 실제 클라이언트 IP 반환 (Cloudflare·프록시 우선)

Cloudflare, 리버스 프록시, 로드밸런서 뒤에서 실제 클라이언트 IP를 정확히 가져옵니다. 다음 순서로 검사하여 유효한 IP를 반환합니다.
 
우선순위 $_SERVER 설명
1 HTTP_CF_CONNECTING_IP Cloudflare가 추가하는 실제 방문자 IP
2 HTTP_X_REAL_IP Nginx 리버스 프록시
3 HTTP_X_FORWARDED_FOR L4/L7 로드밸런서 (첫 번째 IP 사용)
4 HTTP_CLIENT_IP 일부 프록시 서버
5 REMOTE_ADDR 직접 연결 IP (폴백)
 
$ip = dx_ip();   // '203.0.113.42'

// 내부 로직: filter_var()로 유효성 검증
foreach ($keys as $k) {
    if (!empty($_SERVER[$k])) {
        $ips = explode(',', $_SERVER[$k]);  // X-Forwarded-For: ip1, ip2, ip3
        $ip  = trim($ips[0]);               // 첫 번째 IP만 사용
        if (filter_var($ip, FILTER_VALIDATE_IP)) return $ip;
    }
}
return '0.0.0.0';  // 폴백

// 활용: IP 차단, 방문자 로그, 중복 방지
dx_add_hook('dx_top', function() {
    $blocked = array('192.168.1.100', '10.0.0.1');
    if (in_array(dx_ip(), $blocked)) { http_response_code(403); exit; }
});


6.2 dx_ua() / dx_device() / dx_os() / dx_browser()

dx_ua() string User-Agent 문자열 반환 (최대 500자)
 
dx_device() string 디바이스 타입 반환: 'mobile' | 'tablet' | 'pc'
 
dx_os() string OS 정보 반환: 'Windows 10/11', 'Android 13', 'iOS 17.0' 등
 
dx_browser() string 브라우저 반환: 'Chrome 120', 'Firefox 121', 'Edge', 'Safari' 등

dx_device()는 태블릿 감지를 모바일보다 먼저 수행합니다. Android 태블릿은 UA에 'mobile'이 없는 경우가 많으므로 android(?!.*mobile) 패턴으로 감지합니다.
 
// 디바이스별 분기 예시
$device = dx_device();  // 'mobile' | 'tablet' | 'pc'

if ($device === 'mobile') {
    // 모바일 전용 레이아웃
    include DX_THEMES . '/' . $theme . '/layout/mobile.php';
} else {
    include DX_THEMES . '/' . $theme . '/layout/main.php';
}

// 방문자 통계에서 활용
$os      = dx_os();       // 'Android 13'
$browser = dx_browser();  // 'Chrome 120'
$device  = dx_device();   // 'mobile'

// dx_device() 내부: 태블릿 먼저 체크 (순서 중요)
if (preg_match('/ipad|tablet|kindle|playbook|silk|(android(?!.*mobile))/i', $ua))
    return 'tablet';
if (preg_match('/mobile|iphone|ipod|android|blackberry|.../i', $ua))
    return 'mobile';
return 'pc';


7. 설정 함수

$dx_config 전역 배열을 읽고 쓰는 래퍼 함수입니다. data/config.php가 로드된 후 DB의 dx_settings 테이블 값들이 이 배열에 채워집니다.


7.1 dx_config($key, $default)

dx_config(string $key, mixed $default = '') mixed $dx_config 배열에서 설정값 반환
 
매개변수 타입 기본값 설명
$key string (필수) 설정 키 이름
$default mixed '' 키가 없을 때 반환할 기본값
 
// 기본 사용
$theme    = dx_config('theme', 'default');
$siteName = dx_config('site_name', 'DXCMS');
$siteUrl  = dx_config('site_url', '');

// 주요 설정 키 목록
dx_config('site_name')       // 사이트 이름
dx_config('site_url')        // 사이트 URL
dx_config('theme')           // 현재 테마명
dx_config('url_rewrite')     // URL Rewrite 사용 여부 ('1'/'0')
dx_config('secret_key')      // 시크릿 키 (64자리 랜덤)
dx_config('editor')          // 활성 에디터 플러그인 ID
dx_config('socket_enabled')  // 소켓 활성화 여부

// 내부 구현: global $dx_config 배열 직접 접근
function dx_config($key, $default = '') {
    global $dx_config;
    if (isset($dx_config) && is_array($dx_config) && array_key_exists($key, $dx_config)) {
        return $dx_config[$key];
    }
    return $default;
}


7.2 dx_set_config($key, $value) / dx_config_set($key, $value)

dx_set_config(string $key, mixed $value) void $dx_config 배열에 런타임 값 설정

두 함수는 동일합니다. dx_config_set은 DxSite.php 등 내부 클래스에서 사용하는 별칭입니다. 런타임에만 반영되며 DB를 변경하지 않습니다.
 
// 멀티사이트: DxSite가 도메인별 설정을 오버라이드할 때 사용
dx_set_config('theme', 'shop-theme');     // 현재 요청의 테마 변경
dx_set_config('site_name', 'Shop Site');  // 현재 요청의 사이트명 변경

// extend/top/ 파일에서 조건부 설정 변경
if (strpos(dx_request_uri(), '/shop') === 0) {
    dx_set_config('theme', 'shop-theme');
}

// 주의: 이 함수는 DB를 변경하지 않음
// DB에 영구 저장하려면 Database::getInstance()를 통해 dx_settings 테이블 직접 수정


8. 보안 함수

XSS 방어, URL 안전 처리, CSRF 토큰 발급•검증을 담당합니다. 보안 핵심 로직은 Secure.php에 위임하며, functions.php의 함수들은 편의 래퍼 역할을 합니다.


8.1 dx_esc($str)

dx_esc(string $str) string HTML 특수문자 이스케이프 (XSS 방어)

htmlspecialchars($str, ENT_QUOTES, 'UTF-8')의 단축 함수입니다. 테마 파일에서 사용자 입력을 출력할 때 반드시 이 함수를 거쳐야 합니다.
 
// 잘못된 방법 (XSS 취약)
echo $user['name'];
echo $_GET['keyword'];

// 올바른 방법
echo dx_esc($user['name']);
echo dx_esc(dx_get('keyword'));

// HTML 속성에서도 동일하게 사용
echo '<input value="' . dx_esc($value) . '">';
echo '<a href="' . dx_safe_url($link) . '">' . dx_esc($title) . '</a>';

ENT_HTML5는 PHP 5.4+에서만 지원합니다.
일부 저가형 호스팅의 패치 미적용 환경을 위해 ENT_QUOTES를 사용합니다.
PHP 5.6 최소 요구사항이지만 패치 수준이 낮은 환경도 커버합니다.


8.2 dx_safe_url($url)

dx_safe_url(string $url) string XSS·프로토콜 인젝션 방어 URL 반환

blocked:, blocked:, data: 등 위험 프로토콜을 차단하고, 상대 경로를 절대 URL로 변환합니다.
 
// 위험 URL 차단
echo dx_safe_url('blocked:alert(1)');  // '#'
echo dx_safe_url('data:text/html,...');    // '#'
echo dx_safe_url('blocked:...');          // '#'

// 상대 경로 → 절대 URL 변환 (서브디렉토리 안전)
echo dx_safe_url('/notice');      // 'https://example.com/notice'
echo dx_safe_url('/notice/view/42'); // 'https://example.com/notice/view/42'

// 테마에서 메뉴 링크 출력 시
foreach ($menus as $menu) {
    echo '<a href="' . dx_safe_url($menu['url']) . '">' . dx_esc($menu['name']) . '</a>';
}


8.3 CSRF 토큰 함수

dx_csrf_token() string CSRF 토큰 반환 (없으면 생성)
 
dx_csrf_field() string <input type="hidden" name="_csrf" ...> HTML 반환
 
dx_csrf_check() void CSRF 토큰 검증 실패 시 403 exit

세 함수 모두 Secure::getInstance()에 위임합니다. Secure가 로드되지 않은 환경(설치 화면 등)에서는 세션 기반 폴백을 사용합니다.
 
// 폼에 CSRF 필드 삽입
<form method="POST" action="<?php echo dx_base_url('auth/register'); ?>">
    <?php echo dx_csrf_field(); ?>
    <!-- 출력: <input type="hidden" name="_csrf" value="a3f8c1..."> -->
    <input type="text" name="name">
    <button type="submit">가입</button>
</form>

// POST 처리 시 검증 (검증 실패 → 403 exit)
if (dx_method('POST')) {
    dx_csrf_check();
    // 이 아래 코드는 CSRF 검증 통과한 요청만 실행
    $name = dx_post('name');
}

// AJAX에서 토큰 가져오기
fetch('/api/comment', {
    method: 'POST',
    headers: { 'X-CSRF-Token': '<?php echo dx_csrf_token(); ?>' },
    body: JSON.stringify({ content: '...' })
});


9. 응답 함수


9.1 dx_error($msg, $code)

dx_error(string $msg, int $code = 500) void (exit) HTTP 에러 응답 출력 후 종료
 
매개변수 타입 기본값 설명
$msg string (필수) 에러 메시지
$code int 500 HTTP 상태 코드

DX_DEBUG 모드 여부에 따라 출력 내용이 달라집니다. 항상 data/error.log에 스택 트레이스를 기록합니다.
 
// DX_DEBUG = true 시: 상세 에러 출력
// <pre class="text-red-600 ..."> [DX Error 500] 상세 메시지 </pre>

// DX_DEBUG = false 시 (운영): 일반 에러 페이지
// <div> 500 / 오류가 발생했습니다. </div>
// 403은 "접근이 거부되었습니다." 메시지

// 활용 예
if (!dx_is_admin()) dx_error('관리자만 접근 가능합니다.', 403);
if (!$board) dx_error('게시판을 찾을 수 없습니다.', 404);

// 로그 기록: data/error.log에 자동 기록
// [DXCMS DX_ERROR] 2026-05-01 12:00:00 | code=403 | msg=... | trace=...


9.2 dx_json($data, $code)

dx_json(mixed $data, int $code = 200) void (exit) JSON 응답 출력 후 종료
 
매개변수 타입 기본값 설명
$data mixed (필수) JSON으로 인코딩할 데이터
$code int 200 HTTP 상태 코드
 
// API 핸들러에서 성공 응답
dx_json(array('success' => true, 'id' => 42));

// 에러 응답
dx_json(array('error' => '권한이 없습니다.'), 403);
dx_json(array('error' => '찾을 수 없습니다.'), 404);

// 내부 동작:
// 1. http_response_code($code)
// 2. header('Content-Type: application/json; charset=utf-8')
// 3. json_encode($data, JSON_UNESCAPED_UNICODE)
// 4. exit

// 한글 JSON 출력 예
dx_json(array('message' => '댓글이 등록되었습니다.'));
// {"message":"댓글이 등록되었습니다."}  ← JSON_UNESCAPED_UNICODE로 한글 유지


10. 플래시 메시지 함수

HTTP 리다이렉트를 거쳐 다음 요청에서 1회 표시되는 알림 메시지를 세션에 저장하고 읽습니다.


10.1 dx_set_flash / dx_flash / dx_get_flash

dx_set_flash(string $message, string $type = 'success') void 플래시 메시지 세션 저장
 
dx_flash(string $message, string $type = 'success') void dx_set_flash의 별칭
 
dx_get_flash() array|null 플래시 메시지 읽고 세션에서 삭제 (1회성)
 
$type 의미 테마에서 활용
'success' 성공 (초록) 게시글 작성 완료, 로그인 성공 등
'error' 오류 (빨강) 인증 실패, 권한 없음 등
'warning' 경고 (노랑) 입력값 오류, 주의사항 등
'info' 정보 (파랑) 안내 메시지 등
 
// 1. 처리 후 메시지 저장 → 리다이렉트
// core/api/post.php 또는 boards/handler.php
dx_set_flash('게시글이 등록되었습니다.', 'success');
dx_redirect(dx_base_url($boardKey . '/view/' . $postId));

// 로그인 실패 시
dx_flash('아이디 또는 비밀번호가 올바르지 않습니다.', 'error');
dx_redirect(dx_base_url('auth/login'));

// 2. 테마 레이아웃에서 표시
// themes/default/layout/main.php
$flash = dx_get_flash();
if ($flash) {
    echo '<div class="flash flash-' . dx_esc($flash['type']) . '">'
       . dx_esc($flash['message']) . '</div>';
    // dx_get_flash()는 읽는 즉시 세션에서 삭제됨 (1회성)
}


11. 문자열 • 날짜 • 파일 유틸리티 함수


11.1 문자열 함수

dx_substr(string $str, int $length, string $suffix = '...') string 멀티바이트 안전 문자열 자르기
 
dx_mb_substr(string $str, int $start, int $length = null, string $enc = 'UTF-8') string mb_substr 폴백 래퍼
 
// 제목 길이 제한 (한글 포함)
$title = dx_substr($post['title'], 30);
// "안녕하세요 이것은 매우 긴 게시글..." → "안녕하세요 이것은 매우 긴 게시..."

// 커스텀 접미사
$excerpt = dx_substr($content, 100, ' •••');

// mb_substr 폴백: mb_string 확장 없는 환경에서도 동작
$sub = dx_mb_substr('안녕하세요', 2, 3);  // '하세요'


11.2 dx_date($datetime, $format)

dx_date(mixed $datetime, string $format = 'Y-m-d') string 날짜 포맷 변환 (타임스탬프·문자열 양쪽 지원)
 
매개변수 타입 기본값 설명
$datetime mixed (필수) Unix 타임스탬프(int) 또는 날짜 문자열
$format string 'Y-m-d' PHP date() 포맷 문자열
 
// 날짜 문자열 → 포맷 변환
echo dx_date('2026-05-01 12:30:00', 'Y.m.d');   // '2026.05.01'
echo dx_date('2026-05-01 12:30:00', 'H:i');      // '12:30'
echo dx_date('2026-05-01 12:30:00', 'Y년 m월 d일'); // '2026년 05월 01일'

// 타임스탬프도 직접 지원
echo dx_date(time(), 'Y-m-d');   // '2026-05-01'

// 빈 값, 유효하지 않은 날짜 → 빈 문자열 반환 (오류 없음)
echo dx_date('');        // ''
echo dx_date('invalid'); // ''

// 게시판에서 활용
echo dx_date($post['created_at'], 'm.d');   // '05.01'  (짧은 날짜)
echo dx_date($post['created_at'], 'Y-m-d'); // '2026-05-01'


11.3 파일 업로드 함수

dx_upload_url(string $path = '') string data/uploads/ 기준 URL 생성
 
dx_upload_exists(string $path = '') bool data/uploads/ 기준 파일 존재 여부 확인
 
// 업로드 파일 URL 생성
echo dx_upload_url('2026/05/photo.jpg');
// 'https://example.com/data/uploads/2026/05/photo.jpg'

// 썸네일 이미지 출력 (파일 존재 확인 후)
if (dx_upload_exists($post['thumbnail'])) {
    echo '<img src="' . dx_upload_url($post['thumbnail']) . '">';
} else {
    echo '<img src="' . dx_static_url('assets/img/no-image.png') . '">';
}


12. 배열 • 시간 • 용량 유틸리티 함수


12.1 dx_array_column($arr, $columnKey, $indexKey)

dx_array_column(array $arr, string $columnKey, string $indexKey = null) array PHP 5.4 이하 array_column() 폴백
 
// PHP 5.5+에서는 내장 array_column()으로 자동 위임
// PHP 5.4 이하에서는 수동 구현으로 폴백
$rows = array(
    array('id'=>1, 'name'=>'홍길동'),
    array('id'=>2, 'name'=>'김철수'),
);

// name만 추출
$names = dx_array_column($rows, 'name');  // ['홍길동', '김철수']

// id를 인덱스로 사용
$byId = dx_array_column($rows, 'name', 'id');  // [1=>'홍길동', 2=>'김철수']


12.2 dx_time_ago($datetime)

dx_time_ago(string $datetime) string 상대 시간 문자열 반환 ('방금 전', 'N분 전' 등)
 
경과 시간 반환값
60초 미만 '방금 전'
1시간 미만 'N분 전'  (예: '30분 전')
24시간 미만 'N시간 전'  (예: '3시간 전')
7일 미만 'N일 전'  (예: '2일 전')
7일 이상 'm/d'  (예: '04/28')
 
echo dx_time_ago('2026-05-01 11:58:00');  // '방금 전'
echo dx_time_ago('2026-05-01 11:00:00');  // '58분 전'
echo dx_time_ago('2026-05-01 09:00:00');  // '3시간 전'
echo dx_time_ago('2026-04-30 12:00:00');  // '1일 전'
echo dx_time_ago('2026-04-20 12:00:00');  // '04/20'

// 게시판 목록에서 활용
echo dx_time_ago($post['created_at']);


12.3 dx_filesize($bytes)

dx_filesize(int $bytes) string 파일 크기를 읽기 쉬운 형식으로 변환
 
echo dx_filesize(512);          // '512B'
echo dx_filesize(1536);         // '1.5KB'
echo dx_filesize(1572864);      // '1.5MB'
echo dx_filesize(1610612736);   // '1.5GB'

// 첨부파일 목록에서 활용
echo dx_filesize($file['file_size']);


13. 페이지네이션 함수

HTML 페이지네이션 링크를 생성합니다. 테마 파일에서 바로 echo할 수 있는 HTML 문자열을 반환합니다.


13.1 dx_pagination($total, $perPage, $current, $urlPattern, $range)

dx_pagination(int $total, int $perPage, int $current, string $urlPattern, int $range = 5) string 페이지네이션 HTML 반환
 
매개변수 타입 기본값 설명
$total int (필수) 전체 레코드 수
$perPage int (필수) 페이지당 표시 수
$current int (필수) 현재 페이지 번호 (1부터 시작)
$urlPattern string (필수) {page} 플레이스홀더를 포함한 URL 패턴
$range int 5 표시할 페이지 번호 수
 
// 게시판 목록 페이지에서
$page    = dx_get('page', 1, 'int');
$perPage = 15;
$total   = $db->value("SELECT COUNT(*) FROM posts WHERE board_id = ?", array($boardId));

// {page} 플레이스홀더 사용
$urlPattern = dx_base_url($boardKey) . '?page={page}';

echo dx_pagination($total, $perPage, $page, $urlPattern, 5);
// 출력 예:
// <a href="...?page=2" class="dx-page-btn">‹</a>
// <a href="...?page=1" class="dx-page-btn">1</a>
// <a href="...?page=2" class="dx-page-btn dx-page-active">2</a>
// <a href="...?page=3" class="dx-page-btn">3</a>
// <a href="...?page=4" class="dx-page-btn">4</a>
// <a href="...?page=5" class="dx-page-btn">5</a>
// <a href="...?page=3" class="dx-page-btn">›</a>

// 반환 조건: 전체 데이터가 perPage 이하이거나 totalPage가 1이면 빈 문자열 반환
스타일 커스터마이징
dx_pagination()이 생성하는 링크의 CSS 클래스는 dx-page-btn과 dx-page-active입니다.
테마의 CSS에서 이 클래스를 스타일링하거나, 테마에서 dx_pagination() 대신
직접 페이지네이션 HTML을 구현할 수도 있습니다.


14. 로그 및 AJAX 감지 함수


14.1 dx_log($message, $level)

dx_log(string $message, string $level = 'info') void data/error.log에 로그 기록
 
매개변수 타입 기본값 설명
$message string (필수) 기록할 메시지
$level string 'info' 'info' | 'debug' | 'warning' | 'error'

info와 debug 레벨은 기록하지 않습니다. warning과 error만 data/error.log에 기록됩니다. 호출 파일명과 줄 번호가 자동으로 추적되어 함께 기록됩니다.
 
// info/debug는 기록되지 않음 (성능 최적화)
dx_log('사용자 접속', 'info');     // 기록 안 됨
dx_log('디버그 정보', 'debug');    // 기록 안 됨

// warning/error는 기록됨
dx_log('IP 차단 시도: ' . dx_ip(), 'warning');
dx_log('DB 연결 실패: ' . $e->getMessage(), 'error');

// 로그 파일 형식 (data/error.log)
// [2026-05-01 12:00:00][WARNING][extend/top/02_ip_block.php:25] IP 차단 시도: 10.0.0.1
// [2026-05-01 12:00:01][ERROR][core/db/Database.php:88] DB 연결 실패: ...

// 내부: debug_backtrace()로 호출 파일/줄 자동 추적
// functions.php 자신은 스킵하고 실제 호출자 위치를 기록


14.2 dx_is_ajax()

dx_is_ajax() bool AJAX 요청인지 확인
 
// X-Requested-With: XMLHttpRequest 헤더 감지
if (dx_is_ajax()) {
    // AJAX 응답: JSON
    dx_json(array('success' => true, 'data' => $result));
} else {
    // 일반 페이지 응답: HTML 리다이렉트
    dx_set_flash('완료되었습니다.', 'success');
    dx_redirect(dx_base_url('notice'));
}

// 주의: fetch API는 기본적으로 이 헤더를 보내지 않음
// fetch 사용 시 명시적으로 헤더 추가 필요:
fetch('/api/like', {
    method: 'POST',
    headers: {
        'X-Requested-With': 'XMLHttpRequest',
        'Content-Type': 'application/json'
    }
});


15. 게시판 헬퍼 함수 (홈 제작용)

홈페이지 레이아웃에서 최신 게시글을 가져오거나 스킨으로 출력하기 위한 함수입니다. 테마의 홈 페이지 파일에서 가장 많이 사용됩니다.


15.1 dx_board_posts($boardKey, $limit, $withNotice, $titleLen, $excerptLen)

dx_board_posts(string $boardKey, int $limit = 5, bool $withNotice = false, int $titleLen = 0, int $excerptLen = 100) array 게시판 최신글 배열 반환
 
매개변수 타입 기본값 설명
$boardKey string (필수) 게시판 키 (board_key)
$limit int 5 가져올 글 수
$withNotice bool false true면 공지글 포함
$titleLen int 0 제목 최대 길이 (0이면 전체)
$excerptLen int 100 본문 발췌 길이 (0이면 발췌 없음)

반환 배열 각 항목의 키 목록입니다.
 
타입 예시 값 설명
'id' int 42 게시글 ID
'title' string '공지사항입니다' 제목 ($titleLen 적용)
'author' string '홍길동' 작성자 이름
'date' string '2026-05-01' 날짜 (Y-m-d)
'date_short' string '05.01' 짧은 날짜 (m.d)
'url' string 'https://...view/42' 게시글 URL
'board_key' string 'notice' 게시판 키
'view_count' int 127 조회수
'comment_count' int 5 댓글 수
'excerpt' string '내용 요약...' 본문 발췌 ($excerptLen 적용)
 
// 홈 페이지 파일 (pages/home.php)에서 활용
$posts = dx_board_posts('notice', 5);

foreach ($posts as $post) {
    echo '<li>';
    echo '  <a href="' . dx_esc($post['url']) . '">';
    echo '    ' . dx_esc($post['title']);
    echo '  </a>';
    echo '  <span>' . $post['date_short'] . '</span>';
    echo '</li>';
}

// 본문 발췌 포함 (카드형 레이아웃)
$posts = dx_board_posts('free', 6, false, 30, 80);
foreach ($posts as $post) {
    echo '<div class="card">';
    echo '  <h3>' . dx_esc($post['title']) . '</h3>';
    echo '  <p>' . dx_esc($post['excerpt']) . '</p>';
    echo '  <span>' . dx_time_ago($post['date']) . '</span>';
    echo '</div>';
}


15.2 dx_board_latest($boardKey, $limit, $skin, $title, $icon, $titleLen, $showExcerpt)

dx_board_latest(string $boardKey, int $limit = 5, string $skin = 'list', ...) void 스킨 파일로 최신글 목록 출력
 
매개변수 타입 기본값 설명
$boardKey string (필수) 게시판 키
$limit int 5 표시할 글 수
$skin string 'list' 스킨명: 'list', 'card', 'simple' 등
$title string '' 섹션 제목 (없으면 게시판 이름 자동 사용)
$icon string '' 아이콘 이모지
$titleLen int 0 제목 최대 길이
$showExcerpt bool false 본문 발췌 표시 여부

스킨 파일은 DxTheme 폴백 체인으로 결정됩니다. 스킨 파일 내에서 사용 가능한 변수 목록입니다.
 
변수 타입 설명
$board_key string 게시판 키 (board_key)
$board_name string 게시판 이름 (DB)
$title string 표시 제목 ($title 파라미터 또는 $board_name)
$icon string 아이콘 이모지
$more_url string 더보기 URL (게시판 목록 URL)
$posts array dx_board_posts() 반환 배열
$show_excerpt bool 발췌 표시 여부
 
// 스킨 폴백 체인
// 1. themes/{현재테마}/board_latest/{스킨명}.php  ← 최우선
// 2. themes/default/board_latest/{스킨명}.php    ← 폴백
// 3. themes/default/board_latest/list.php        ← 최종 폴백

// 홈 페이지에서 사용 예
dx_board_latest('notice', 5, 'list', '공지사항', '📢');
dx_board_latest('free',   6, 'card', '자유게시판', '💬');
dx_board_latest('gallery', 8, 'grid', '갤러리', '🖼️', 0, false);


16. 테마 자산 함수

모든 테마에 공통으로 필요한 폰트, 아이콘, CSS 엔진을 자동으로 로드합니다. 테마 파일의 <head> 영역에서 한 번만 호출하면 됩니다.


16.1 dx_head_assets($opts)

dx_head_assets(array $opts = array()) void Pretendard + Space Grotesk + Font Awesome 6 + dxb-css 로드
 
매개변수 타입 기본값 설명
opts['fa'] bool true Font Awesome 6 CSS 로드 여부
opts['pretend'] bool true Pretendard 폰트 로드 여부
opts['grotesk'] bool true Space Grotesk 폰트 로드 여부
opts['dxb'] bool true dxb-css.js 로드 여부
 
<!-- themes/{테마}/layout/main.php -->
<head>
    <meta charset="UTF-8">
    <title><?php echo dx_esc(dx_config('site_name')); ?></title>
    <?php dx_head_assets(); ?>
    <!-- 위 한 줄로 아래 항목 모두 자동 로드:
         - Pretendard 폰트 (CDN)
         - Space Grotesk 폰트 (Google Fonts)
         - Font Awesome 6 (CDN)
         - dxb-css.js (DXB 유틸리티 CSS 엔진) -->
    <link rel="stylesheet" href="<?php echo dx_static_url('themes/default/assets/style.css'); ?>">
</head>

// 개별 옵션 비활성화
<?php dx_head_assets(array('fa' => false)); ?>  // Font Awesome 제외
<?php dx_head_assets(array('dxb' => false)); ?> // dxb-css.js 제외


16.2 dx_font_css()

dx_font_css() void 공통 body font CSS 인라인 출력

CSS 변수로 폰트 패밀리를 정의하고 기본 리셋을 적용합니다. 테마 파일의 <style> 블록 직전에 호출합니다.
 
<!-- 출력되는 CSS 요약 -->
<style>
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0 }
  html { scroll-behavior: smooth }
  :root {
    --font-body: 'Pretendard', -apple-system, ..., sans-serif;
    --font-head: 'Space Grotesk', 'Pretendard', sans-serif;
    --font-mono: 'JetBrains Mono', 'Fira Code', ..., monospace;
  }
  body { font-family: var(--font-body); -webkit-font-smoothing: antialiased; }
  a { text-decoration: none; color: inherit }
  img { max-width: 100%; height: auto; display: block }
  /* 스크롤바, 텍스트 셀렉션 스타일 등 */  
  .sg { font-family: var(--font-head) }   /* Space Grotesk 단축 클래스 */
</style>


17. 내부 전용 함수 (_dx_ 접두사)

앞에 언더스코어(_)가 붙은 함수는 내부 용도로, 직접 호출하지 않는 것을 권장합니다. 대신 공개 함수를 통해 간접 사용합니다.


17.1 _dx_resolve_base()

dx_base_url(), dx_static_url()이 내부적으로 사용하는 베이스 URL 계산 함수입니다. static 캐싱으로 요청당 1회만 계산합니다. (4장 URL 생성 섹션에 상세 설명)


17.2 _dx_inject_dxb_css()

load_plugins() 호출 시 자동으로 실행되어 DXB CSS 엔진(dxb-css.js)을 모든 페이지에 자동 주입합니다. dx_head 훅과 dx_body_bottom 훅에 콜백을 등록하는 방식으로 동작합니다.
 
// load_plugins() 내에서 자동 호출
_dx_inject_dxb_css();

// 내부: 두 개의 훅 등록
dx_add_hook('dx_head', function() {
    // dxb-css.js 파일 존재 시에만 로드
    // 중복 로드 방지 (data-dxb 속성 확인)
}, 1);  // priority=1: 다른 dx_head 훅보다 먼저 실행

dx_add_hook('dx_body_bottom', function() {
    // 1차 rescan() 즉시 실행
    // 2차: requestAnimationFrame으로 브라우저 렌더링 직전 스캔
}, 1);

// 직접 호출하지 않아야 하는 이유:
// • load_plugins()가 이미 호출하므로 중복 등록 발생
// • dx_head, dx_body_bottom 훅이 2배로 등록됨


17.3 _dx_register_point_hooks()

포인트•경험치 시스템의 훅을 등록합니다. index.php에서 초기화 완료 후 1회 호출됩니다.
 
훅 이름 이벤트 처리 내용
dx_after_write 게시글 작성 DxPoint::add + DxPoint::addExp (write 타입)
dx_after_comment 댓글 작성 DxPoint::add + DxPoint::addExp (comment 타입)
dx_after_login 로그인 오늘 첫 로그인인지 확인 후 출석 포인트 지급
dx_after_register 회원 가입 가입 축하 포인트 + 경험치 지급
dx_after_like 좋아요 받음 작성자에게 like_recv 포인트 + 경험치


17.4 _dxAvaColor($name)

회원 이름을 해시하여 아바타 배경색을 결정합니다. 같은 이름은 항상 같은 색이 나옵니다.
 
// 사용 예 (테마 파일에서)
$color = _dxAvaColor($user['name']);
echo '<div style="background:' . $color . ';...">';
echo $user['name'][0]; // 이름 첫 글자
echo '</div>';

// 8가지 색상 중 해시로 선택
// #ef4444 (빨강), #f97316 (주황), #eab308 (노랑), #22c55e (초록)
// #06b6d4 (청록), #3b82f6 (파랑), #8b5cf6 (보라), #ec4899 (핑크)


18. 실전 활용 패턴 모음


18.1 게시판 핸들러 패턴

// boards/handler.php 또는 pages/*.php에서

// ① GET 파라미터 안전하게 읽기
$page    = dx_get('page', 1, 'int');
$keyword = dx_get('q', '');
$postId  = dx_get('id', 0, 'bigint');

// ② POST 처리
if (dx_method('POST')) {
    dx_csrf_check();
    $title   = dx_post('title');
    $content = dx_post('content');
    if (empty($title)) {
        dx_json(array('error' => '제목을 입력해주세요.'), 422);
    }
    // ... DB 저장 ...
    dx_json(array('success' => true, 'id' => $newId));
}

// ③ 리다이렉트
dx_set_flash('게시글이 등록되었습니다.', 'success');
dx_redirect(dx_base_url($boardKey . '/view/' . $newId));


18.2 API 핸들러 패턴

// core/api/my_api.php
if (!defined('DX_CMS')) exit;

// 인증 확인
if (!Auth::getInstance()->isLoggedIn()) {
    dx_json(array('error' => '로그인이 필요합니다.'), 401);
}

// AJAX 여부 확인
if (!dx_is_ajax()) {
    dx_json(array('error' => 'AJAX 요청만 허용됩니다.'), 400);
}

// CSRF 검증
dx_csrf_check();

// 요청 처리
$targetId = dx_post('id', 0, 'bigint');
if (!ctype_digit($targetId) || $targetId === '0') {
    dx_json(array('error' => '잘못된 요청입니다.'), 400);
}

// ... 처리 로직 ...
dx_json(array('success' => true, 'message' => '처리되었습니다.'));


18.3 extend/ 파일 패턴

// extend/middle/02_my_feature.php
if (!defined('DX_CMS')) exit;

// 라우트 정보 (middle에서만 사용 가능)
$route = isset($GLOBALS['dx_route']) ? $GLOBALS['dx_route'] : array();
$type  = isset($route['type'])  ? $route['type']  : '';
$slug  = isset($route['slug'])  ? $route['slug']  : '';

// admin/api 제외
if (in_array($type, array('admin', 'api'))) return;

// 설정 읽기
$enabled = dx_config('my_feature_enabled', '0');
if ($enabled !== '1') return;

// IP 기반 처리
$ip = dx_ip();
$device = dx_device();

// 응답 후 처리
register_shutdown_function(function() use ($ip, $type, $slug) {
    if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();
    $db = Database::getInstance();
    $db->query("INSERT INTO my_log...", array($ip, $type, $slug));
});


18.4 테마 파일 패턴

<!-- themes/{테마}/layout/main.php -->
<?php if (!defined('DX_CMS')) exit; ?>
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?php echo dx_esc(dx_config('site_name')); ?></title>
    <?php dx_head_assets(); ?>
    <?php dx_font_css(); ?>
</head>
<body>

<?php
// 플래시 메시지 표시
$flash = dx_get_flash();
if ($flash) {
    echo '<div class="flash flash-' . dx_esc($flash['type']) . '">'
       . dx_esc($flash['message']) . '</div>';
}
?>

<main>
    <?php echo $dx_content; ?>
</main>

</body>
</html>


19. 전체 함수 빠른 참조 (Quick Reference)

함수 반환 설명
dx_random_bytes($length) string 암호화 안전 난수 바이트
dx_random_hex($length=32) string 16진수 난수 문자열
dx_realpath($path) string|false 백슬래시 정규화 realpath
dx_path_inside($path, $base) bool 경로 탈출 방어 검증
dx_is_https() bool HTTPS 감지 (CF·IIS 포함)
dx_base_url($path='') string 내부 절대 URL 생성
dx_static_url($path='') string 정적 자산 URL
dx_request_uri() string 이중슬래시 정규화 URI
dx_current_url() string 현재 전체 URL
dx_redirect($url, $code=302) void/exit 리다이렉트 후 exit
dx_redirect_back($fallback='/') void/exit 이전 페이지로 리다이렉트
dx_get($key, $default='', $type='string') mixed $_GET 안전 읽기
dx_post($key, $default='', $type='string') mixed $_POST 안전 읽기
dx_request($key, $default='', $type='string') mixed $_REQUEST 안전 읽기
dx_method($method) bool HTTP 메서드 확인
dx_cast($val, $type) mixed 타입 캐스팅
dx_ip() string 클라이언트 IP
dx_ua() string User-Agent 문자열
dx_device() string 디바이스: 'mobile'|'tablet'|'pc'
dx_os() string OS 정보
dx_browser() string 브라우저 정보
dx_config($key, $default='') mixed 설정값 조회
dx_set_config($key, $value) void 런타임 설정 변경
dx_config_set($key, $value) void dx_set_config 별칭
dx_esc($str) string HTML 특수문자 이스케이프
dx_safe_url($url) string XSS 방어 URL
dx_csrf_token() string CSRF 토큰 반환/생성
dx_csrf_field() string CSRF hidden 필드 HTML
dx_csrf_check() void CSRF 검증 (실패 시 403)
dx_error($msg, $code=500) void/exit HTTP 에러 응답
dx_json($data, $code=200) void/exit JSON 응답
dx_set_flash($msg, $type='success') void 플래시 메시지 저장
dx_flash($msg, $type='success') void dx_set_flash 별칭
dx_get_flash() array|null 플래시 메시지 읽기(1회)
dx_substr($str, $len, $suffix='...') string 멀티바이트 안전 자르기
dx_mb_substr($str, $start, $len=null) string mb_substr 폴백 래퍼
dx_date($datetime, $format='Y-m-d') string 날짜 포맷 변환
dx_upload_url($path='') string data/uploads/ URL
dx_upload_exists($path='') bool 업로드 파일 존재 확인
dx_array_column($arr, $col, $idx=null) array array_column PHP 5.4 폴백
dx_time_ago($datetime) string 'N분 전' 등 상대 시간
dx_filesize($bytes) string '1.5MB' 등 파일 크기
dx_pagination($total, $per, $cur, $url, $range=5) string 페이지네이션 HTML
dx_log($msg, $level='info') void data/error.log 기록
dx_is_ajax() bool AJAX 요청 감지
dx_board_posts($key, $limit=5, ...) array 게시판 최신글 배열
dx_board_latest($key, $limit=5, $skin, ...) void 스킨으로 최신글 출력
dx_head_assets($opts) void 공통 폰트/아이콘 로드
dx_font_css() void 공통 body font CSS 출력

핵심 원칙 요약
1. functions.php는 가장 먼저 로드된다. 클래스를 직접 생성하지 않고 함수 정의만 담는다.
2. $_GET/$_POST 직접 접근 금지. dx_get(), dx_post()를 항상 사용한다.
3. 사용자 입력 출력 시 dx_esc()로 필수 이스케이프. URL은 dx_safe_url().
4. POST 처리 시 dx_csrf_check()를 맨 먼저 호출한다.
5. 리다이렉트는 dx_redirect(). JSON은 dx_json(). 에러는 dx_error().
6. IP는 dx_ip()로만 읽는다. Cloudflare•프록시 환경에서도 실제 IP 보장.
7. 설정은 dx_config(). 런타임 변경은 dx_set_config(). DB 변경 아님.
8. 로그는 dx_log($msg, 'warning') 또는 dx_log($msg, 'error'). info는 기록 안 됨.

댓글0

로그인 후 댓글을 작성할 수 있습니다.
5. 관리자 기능 사용법 회원 랭킹 2026.04.21 5. 관리자 기능 사용법 포인트샵 2026.04.21 5. 관리자 기능 사용법 레벨 관리 2026.04.21 5. 관리자 기능 사용법 포인트 관리 2026.04.21 5. 관리자 기능 사용법 문자 서비스 2026.04.21 5. 관리자 기능 사용법 메일 보내기 2026.04.21 5. 관리자 기능 사용법 회원 관리 2026.04.21 5. 관리자 기능 사용법 메뉴 관리 2026.04.21 5. 관리자 기능 사용법 인기글 2026.04.21 5. 관리자 기능 사용법 카테고리 2026.04.21 5. 관리자 기능 사용법 게시판 그룹 2026.04.21 5. 관리자 기능 사용법 페이지 관리 2026.04.21 5. 관리자 기능 사용법 전체 공지 2026.04.21 5. 관리자 기능 사용법 팝업 관리 2026.04.21 5. 관리자 기능 사용법 게시판 관리 2026.04.21 4.2 관리자 시스템 구조 관리자 UI 구조 2026.04.21 4.2 관리자 시스템 구조 관리자 라우팅 2026.04.21 4.1 CMS 아키텍처 데이터 흐름 연결 2026.04.21 4.1 CMS 아키텍처 DX 위에 CMS가 올라가는 구조 2026.04.21 3.10 모듈 로딩 구조 자동 로딩 구조 2026.04.21
31
전체 회원
503
전체 게시글
770
전체 댓글
442
오늘 방문
33,173
전체 방문
3
현재 접속
인기글 7일 이내
최신글
최신댓글
목록