회원가입 | 고객센터 |
DESIGNONEX
디자인원엑스
About
Service
Q&A
PR리그
자유게시판N
갤러리
포인트게임
공지사항N
통계
로그인 회원가입
고객센터
3.9 공통 함수 / 유틸

전역 함수 구조

A Administrator
2026.04.21 01:01(수정됨) 99 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

로그인 후 댓글을 작성할 수 있습니다.
3.9 공통 함수 / 유틸 재사용 방식 2026.04.21 3.9 공통 함수 / 유틸 공통 클래스 구조 2026.04.21 3.9 공통 함수 / 유틸 전역 함수 구조 2026.04.21
30
전체 회원
269
전체 게시글
144
전체 댓글
181
오늘 방문
28,530
전체 방문
1
현재 접속
인기글 7일 이내
최신글
최신댓글
목록