회원가입 | 고객센터 |
DESIGNONEX
dxcms.kr
로그인 회원가입
고객센터
3.6 데이터 처리 구조

공통 함수 활용

D DX
2026.04.21 00:58(수정됨) 132 0

1. 공통함수 라이브러리 개요


1.1 파일 위치 및 역할

core/functions.php는 DXCMS 프레임워크 전체에서 공통으로 사용되는 순수 함수(Pure Function)들의 집합입니다.
이 파일은 HookManager, PluginRegistry, Database 클래스보다 먼저 로드되며, 클래스 인스턴스를 생성하지 않고 함수 정의만 담습니다. PHP 5.6부터 최신 PHP까지, Windows•Linux•IIS•Apache•Nginx, HTTP•HTTPS 환경을 완전히 지원합니다.

📌 로드 순서 원칙
core/functions.php 는 index.php 부팅 가장 초기에 로드됩니다.
이 파일 안에서는 Database::getInstance(), Auth::getInstance() 등 클래스 인스턴스를 직접 호출하지 않습니다.
단, dx_board_posts() 처럼 내부적으로 Database를 사용하는 함수는 반드시 클래스 로드 이후에 호출하세요.


1.2 함수 카테고리 전체 구조

카테고리 주요 함수 설명
보안 난수 dx_random_bytes(),
dx_random_hex()
PHP 버전별 자동 폴백 보안 난수 생성
경로 유틸 dx_realpath(),
dx_path_inside()
크로스 플랫폼 파일 경로 처리
HTTPS 감지 dx_is_https() 프록시•CDN 환경 포함 HTTPS 정확 감지
URL 생성 dx_base_url(),
dx_static_url()
서브디렉토리•URL Rewrite 자동 처리
요청 처리 dx_get(),
dx_post(),
dx_request()
타입 캐스팅 포함 안전 입력 처리
IP / UA dx_ip(),
dx_device(),
dx_os()
Cloudflare 포함 실제 IP•디바이스 감지
설정 관리 dx_config(),
dx_set_config()
전역 설정값 읽기/쓰기
보안 dx_esc(),
dx_safe_url(),
dx_csrf_*()
XSS 방어•CSRF 토큰 처리
응답 dx_error(),
dx_json()
HTTP 오류•JSON API 응답
플래시 메시지 dx_flash(),
dx_get_flash()
세션 기반 일회성 메시지
문자열 dx_substr(),
dx_time_ago()
UTF-8 안전 문자열•상대 시간
날짜 dx_date() 날짜 포맷 통일
파일 업로드 dx_upload_url(),
dx_upload_exists()
업로드 URL/존재 여부 확인
페이지네이션 dx_pagination() HTML 페이지 버튼 자동 생성
게시판 헬퍼 dx_board_posts(),
dx_board_latest()
홈/위젯에서 게시글 출력
에셋 출력 dx_head_assets(),
dx_font_css()
폰트•아이콘 CDN 자동 삽입
로그 dx_log() 에러/경고 파일 로그 기록
플러그인 load_plugins() 플러그인 자동 로드


2. 보안 난수 생성


2.1 dx_random_bytes($length)

PHP 버전과 서버 환경에 따라 가장 안전한 방법을 자동으로 선택하여 보안 난수 바이트를 반환합니다.
 
// PHP 7.0+ → random_bytes() 사용 (가장 안전)
// PHP 5.x + OpenSSL → openssl_random_pseudo_bytes()
// PHP 5.x + mcrypt → mcrypt_create_iv()
// 최후 폴백 → mt_rand() (저가형 호스팅 대응)

$bytes = dx_random_bytes(16);   // 16바이트 보안 난수 반환


2.2 dx_random_hex($length = 32)

보안 난수를 16진수 문자열로 반환합니다. CSRF 토큰, 세션키, 임시 비밀번호 생성에 사용합니다.
 
$token   = dx_random_hex(32);  // 32자리 16진수 토큰 생성
$tempPwd = dx_random_hex(16);  // 16자리 임시 비밀번호

// 내부적으로 dx_random_bytes((int)ceil($length/2))를 호출 후 bin2hex() 적용


3. 경로 및 URL 처리


3.1 경로 유틸리티

dx_realpath($path)

Windows 백슬래시를 슬래시로 정규화한 실제 절대 경로를 반환합니다. realpath() 실패 시 false를 반환합니다.
 
$real = dx_realpath('../data/uploads');
// 결과: '/var/www/html/data/uploads'  (\를 /로 자동 변환)


dx_path_inside($path, $base)

$path가 $base 디렉토리 내부에 있는지 확인합니다. 경로 탈출(Path Traversal) 공격 방어에 사용합니다.
 
$base = DX_DATA . '/uploads';
$file = dx_realpath($userInput);

if (!dx_path_inside($file, $base)) {
    dx_error('허용되지 않은 경로입니다.', 403);
}
// Windows 환경: 대소문자 구분 없이 stripos() 사용 (자동 처리)


3.2 HTTPS 감지

dx_is_https()

Cloudflare, AWS ALB, nginx 리버스 프록시 등 다양한 환경에서 정확하게 HTTPS를 감지합니다.
 
// 다음 헤더를 순서대로 확인:
// 1. $_SERVER['HTTPS']
// 2. HTTP_X_FORWARDED_PROTO (nginx 프록시)
// 3. HTTP_X_FORWARDED_SSL
// 4. HTTP_FRONT_END_HTTPS
// 5. SERVER_PORT === 443

if (dx_is_https()) {
    // SSL 환경에서만 실행할 코드
}


3.3 URL 생성 함수

dx_base_url($path = '')

사이트 절대 URL을 생성합니다. 내부적으로 _dx_resolve_base() 헬퍼를 통해 site_url 설정값을 확인하고, 도메인 불일치 시 자동 감지 모드로 전환합니다. 이중 슬래시(//)를 자동 제거하며 URL Rewrite 미적용 환경(IIS 등)도 지원합니다.
 
echo dx_base_url();                    // https://example.com/
echo dx_base_url('board/notice');      // https://example.com/board/notice
echo dx_base_url('admin/settings');    // https://example.com/admin/settings

// URL Rewrite 비활성화 환경 (dx_config('url_rewrite') === '0')
echo dx_base_url('board/notice');
// https://example.com/index.php?_url=/board/notice


dx_static_url($path = '')

CSS, JS, 이미지 등 정적 자산의 URL을 생성합니다. dx_base_url()과 동일한 베이스를 사용하되, URL Rewrite 영향을 받지 않습니다.
 
echo dx_static_url('assets/css/dx.css');
// https://example.com/assets/css/dx.css

echo dx_static_url('assets/js/dx.js');
// https://example.com/assets/js/dx.js


dx_request_uri() / dx_current_url()

// REQUEST_URI의 이중 슬래시(//) 정규화
$uri = dx_request_uri();       // '/board/notice/1'

// 현재 페이지 완전 URL 반환
$url = dx_current_url();
// 'https://example.com/board/notice/1?page=2'


dx_redirect($url, $code = 302) / dx_redirect_back($fallback = '/')

// 지정 URL로 리다이렉트 (기본 302)
dx_redirect(dx_base_url('member/login'));
dx_redirect(dx_base_url('board/notice'), 301);  // 영구 이동

// 이전 페이지로 돌아가기 (HTTP_REFERER 없으면 fallback)
dx_redirect_back('/board/notice');


4. 요청 데이터 처리


4.1 안전 입력 수신 함수

GET/POST/REQUEST 데이터를 수신할 때 반드시 dx_get() • dx_post() • dx_request() 를 사용합니다. 세 함수 모두 내부적으로 dx_cast() 를 거쳐 타입 변환 및 trim을 적용합니다.
 

함수 시그니처

dx_get($key, $default='', $type='string')
dx_post($key, $default='', $type='string')
dx_request($key, $default='', $type='string')


$type 파라미터 종류

$type 값 변환 방식 사용 예시
string (기본) trim() 처리된 문자열 dx_get('keyword')
int (int) 캐스팅 dx_get('page', 1, 'int')
float (float) 캐스팅 dx_post('price', 0.0, 'float')
bool (bool) 캐스팅 dx_post('agree', false, 'bool')
array (array) 캐스팅 dx_post('ids', [], 'array')
bigint 32bit PHP 오버플로우 방지 정수 문자열 dx_post('post_id', '0', 'bigint')


실전 사용 예

// 게시판 목록 파라미터
$page    = dx_get('page', 1, 'int');       // GET page, 기본값 1, 정수 변환
$keyword = dx_get('keyword', '');          // GET keyword, 기본값 빈문자열
$catId   = dx_get('cat', 0, 'int');        // GET cat, 기본값 0

// 게시글 작성 POST 데이터
$title   = dx_post('title');                // POST title, string+trim
$content = dx_post('content');              // POST content
$postId  = dx_post('post_id', '0', 'bigint'); // 큰 ID 안전 처리

// HTTP 메서드 확인
if (dx_method('POST')) {
    // POST 요청 처리
}


4.2 dx_cast($val, $type)

dx_cast()는 dx_get/post/request 내부에서 호출되지만, 직접 사용할 수도 있습니다.
$raw  = '  123  ';
$num  = dx_cast($raw, 'int');     // → 123
$str  = dx_cast($raw, 'string'); // → '123' (trim 적용)
$big  = dx_cast('9999999999', 'bigint'); // 32bit PHP에서도 안전한 정수 문자열


5. IP 주소 및 디바이스 감지


5.1 dx_ip()

Cloudflare(CF-Connecting-IP), X-Real-IP, X-Forwarded-For 등 프록시 헤더를 순서대로 확인하여 실제 클라이언트 IP를 반환합니다.
 
// 확인 우선순위:
// 1. HTTP_CF_CONNECTING_IP  (Cloudflare)
// 2. HTTP_X_REAL_IP         (nginx)
// 3. HTTP_X_FORWARDED_FOR   (일반 프록시)
// 4. HTTP_CLIENT_IP
// 5. REMOTE_ADDR            (직접 연결)

$ip = dx_ip();   // '203.0.113.45'

// 활용: 접속 로그 기록
$db->query(
    "INSERT INTO `{$db->table('access_log')}` (ip, created_at) VALUES (?, NOW())",
    [dx_ip()]
);


5.2 디바이스•OS•브라우저 감지

// 디바이스 타입: 'mobile' | 'tablet' | 'pc'
$device = dx_device();

// OS 정보: 'Windows 10/11', 'Android 13', 'iOS 17.0' 등
$os = dx_os();

// 브라우저: 'Chrome 124', 'Firefox 125', 'Edge', 'Safari' 등
$browser = dx_browser();

// 활용: 모바일 전용 레이아웃 분기
if (dx_device() === 'mobile') {
    include $skinDir . '/mobile/list.php';
} else {
    include $skinDir . '/list.php';
}

// 활용: 회원 접속 통계 저장
$db->query(
    "INSERT INTO `{$db->table('member_access')}` (member_id, device, os, browser, ip)
     VALUES (?, ?, ?, ?, ?)",
    [$memberId, dx_device(), dx_os(), dx_browser(), dx_ip()]
);


6. 설정 관리


6.1 dx_config($key, $default = '')

config.php 가 로드한 전역 $dx_config 배열에서 설정값을 읽습니다. 키가 없으면 $default를 반환합니다.
 
// 기본 읽기
$siteName = dx_config('site_name', 'DXCMS');
$adminMail = dx_config('admin_email');
$siteUrl   = dx_config('site_url', '');

// 활용: 기능 on/off 플래그 확인
if (dx_config('use_sms') === '1') {
    // SMS 기능 사용 중
}

// 활용: URL Rewrite 여부 확인
if (dx_config('url_rewrite', '1') === '0') {
    // index.php?_url= 방식 사용
}


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

런타임에 설정값을 동적으로 변경합니다. dx_config_set()은 dx_set_config()의 별칭입니다.
 
// 런타임 설정 변경 (DB에는 저장되지 않음, 현재 요청에서만 유효)
dx_set_config('site_name', '테스트 사이트');
dx_config_set('debug_mode', '1');

// 플러그인에서 임시 설정 주입
dx_set_config('theme', 'dark');


7. 보안 관련 함수


7.1 dx_esc($str) — XSS 방어 출력

HTML 특수문자를 엔티티로 변환합니다. 모든 사용자 입력값은 반드시 이 함수를 거쳐 출력하세요. htmlspecialchars(ENT_QUOTES, 'UTF-8') 의 래퍼입니다.
 
// 잘못된 출력 (XSS 취약)
echo $_GET['name'];                    // 위험!

// 올바른 출력
echo dx_esc($_GET['name']);             // 안전

// 게시글 제목 출력
echo dx_esc($row['title']);

// input value 출력
echo '<input type="text" value="' . dx_esc($value) . '">';


7.2 dx_safe_url($url) — URL XSS 방어

blocked:, blocked:, data: 프로토콜을 차단하고 안전한 URL을 반환합니다. 메뉴•링크 href 출력에 사용합니다. 상대경로(/path) 는 자동으로 dx_base_url()로 변환됩니다.
 
echo dx_safe_url($menu['url']);              // 메뉴 링크 안전 출력

// 위험 URL 처리
echo dx_safe_url('blocked:alert(1)');     // → '#'  (차단)
echo dx_safe_url('data:text/html,<h1>');     // → '#'  (차단)

// 상대경로 자동 변환
echo dx_safe_url('/board/notice');
// → 'https://example.com/board/notice'

// 링크 태그 사용 예
echo '<a href="' . dx_safe_url($link) . '">' . dx_esc($label) . '</a>';


7.3 CSRF 보호 함수

내부적으로 Secure 클래스에 위임합니다. Secure 미로드 환경에서는 세션 기반 폴백으로 동작합니다.
 
// 1) 폼에 CSRF 토큰 필드 삽입
echo dx_csrf_field();
// → <input type="hidden" name="_csrf" value="abc123...">

// 2) 토큰 값만 가져오기 (Ajax 요청 헤더 등에 사용)
$token = dx_csrf_token();

// 3) POST 처리 시 검증 (실패 시 403 반환 후 exit)
dx_csrf_check();

// 폼 전체 예시
// <form method="POST" action="...">
//   <?php echo dx_csrf_field(); ?>
//   ...
// </form>

// POST 핸들러 예시
// dx_csrf_check();  // 반드시 첫 줄에 호출
// $title = dx_post('title');
// ...

⚠️ CSRF 보호 필수 적용
POST 요청을 처리하는 모든 핸들러 파일(게시글 작성/수정/삭제, 회원정보 변경 등) 상단에 반드시 dx_csrf_check()를 호출하세요.
AJAX 요청의 경우 요청 헤더에 X-CSRF-Token: <?php echo dx_csrf_token(); ?> 를 포함시켜야 합니다.


8. 응답 처리 함수


8.1 dx_error($msg, $code = 500)

HTTP 상태코드를 설정하고 오류 화면을 출력한 후 종료합니다. DX_DEBUG 상수가 true면 상세 오류 메시지를, false면 사용자 친화적 메시지를 출력합니다.
 
// 403 접근 거부
dx_error('권한이 없습니다.', 403);

// 404 not found
dx_error('페이지를 찾을 수 없습니다.', 404);

// 500 서버 오류 (기본)
dx_error('오류가 발생했습니다.');

// 권한 체크 패턴
if (!Auth::getInstance()->isLoggedIn()) {
    dx_redirect(dx_base_url('member/login'));
}
if ($post['member_id'] !== $loginUser['id']) {
    dx_error('본인 게시글만 수정할 수 있습니다.', 403);
}


8.2 dx_json($data, $code = 200) — API/Ajax 응답

Content-Type: application/json; charset=utf-8 헤더를 설정하고 데이터를 JSON으로 출력 후 종료합니다. 한글 등 유니코드를 이스케이프 없이 출력합니다.
 
// 성공 응답
dx_json(['success' => true, 'message' => '저장되었습니다.']);

// 데이터 포함 응답
dx_json([
    'success' => true,
    'data'    => ['id' => $postId, 'title' => $title],
]);

// 실패 응답 (422 Unprocessable Entity)
dx_json(['success' => false, 'message' => '제목을 입력하세요.'], 422);

// 인증 실패 (401)
if (!Auth::getInstance()->isLoggedIn()) {
    dx_json(['success' => false, 'message' => '로그인이 필요합니다.'], 401);
}


8.3 플래시 메시지 함수

세션에 일회성 메시지를 저장하고, 다음 페이지 로드 시 꺼내어 사용합니다. 리다이렉트 후 알림 표시에 활용합니다.
 
// 메시지 저장 (리다이렉트 전)
dx_flash('게시글이 저장되었습니다.', 'success');
dx_flash('삭제되었습니다.', 'warning');
dx_flash('오류가 발생했습니다.', 'error');

// dx_set_flash()는 dx_flash()의 동일한 기능 (별칭)
dx_set_flash('처리되었습니다.', 'success');

// 메시지 읽기 (읽으면 세션에서 자동 삭제)
$flash = dx_get_flash();
if ($flash) {
    echo '<div class="alert alert-' . dx_esc($flash['type']) . '">';
    echo dx_esc($flash['message']);
    echo '</div>';
}


9. 문자열•날짜•파일 유틸리티


9.1 문자열 처리

dx_substr($str, $length, $suffix = '...')

UTF-8 한글을 안전하게 지정 길이로 자르고 말줄임 문자를 붙입니다. mb_string 미설치 환경도 지원합니다.
 
$title   = '안녕하세요 반갑습니다 DXCMS 입니다';
$short   = dx_substr($title, 10);          // '안녕하세요 반갑습니다...'
$custom  = dx_substr($title, 10, ' [더보기]');  // 커스텀 suffix

// 게시판 목록 제목 자르기
echo dx_esc(dx_substr($row['title'], 30));


dx_time_ago($datetime)

DB에 저장된 datetime 문자열을 "방금 전", "5분 전", "3시간 전", "2일 전" 형태로 반환합니다.
 
echo dx_time_ago($row['created_at']);
// 결과 예: '방금 전'  |  '15분 전'  |  '3시간 전'  |  '2일 전'  |  '04/15'

// 기준: 7일 미만이면 상대시간, 이상이면 'm/d' 형식 날짜 반환


dx_array_column($arr, $columnKey, $indexKey = null)

PHP 5.4 이하 환경에서도 array_column() 기능을 사용할 수 있는 폴백 함수입니다.
 
$posts   = $db->rows("SELECT id, title FROM `{$db->table('posts')}`");
$titles  = dx_array_column($posts, 'title');         // 제목 배열 추출
$indexed = dx_array_column($posts, 'title', 'id');   // id를 키로 사용


9.2 날짜 처리

dx_date($datetime, $format = 'Y-m-d')

timestamp 또는 날짜 문자열을 지정 포맷으로 반환합니다. 유효하지 않은 값이면 빈 문자열을 반환합니다.
 
echo dx_date($row['created_at']);              // '2025-04-24'
echo dx_date($row['created_at'], 'Y년 m월 d일'); // '2025년 04월 24일'
echo dx_date($row['created_at'], 'Y-m-d H:i'); // '2025-04-24 14:30'

// timestamp도 처리 가능
echo dx_date(time(), 'H:i:s');                // '14:30:45'

// null/빈값은 빈 문자열 반환
echo dx_date(null);   // ''
echo dx_date('');     // ''


9.3 파일 크기 및 업로드

dx_filesize($bytes)

echo dx_filesize(512);         // '512B'
echo dx_filesize(2048);        // '2KB'
echo dx_filesize(1536000);     // '1.5MB'
echo dx_filesize(2147483648);  // '2GB'


dx_upload_url($path) / dx_upload_exists($path)

// 업로드 파일 URL 생성
echo dx_upload_url('2025/04/image.jpg');
// 'https://example.com/data/uploads/2025/04/image.jpg'

// 업로드 파일 존재 확인
if (dx_upload_exists('2025/04/image.jpg')) {
    echo '<img src="' . dx_upload_url('2025/04/image.jpg') . '">';
}


10. 페이지네이션


10.1 dx_pagination($total, $perPage, $current, $urlPattern, $range = 5)

전체 데이터 수, 페이지당 수, 현재 페이지, URL 패턴을 받아 HTML 페이지 버튼 문자열을 반환합니다.
 
파라미터 타입 설명
$total int 전체 데이터 수
$perPage int 페이지당 표시 수
$current int 현재 페이지 번호 (1부터 시작)
$urlPattern string {page} 플레이스홀더를 포함한 URL 패턴
$range int 표시할 페이지 버튼 수 (기본 5)
 
$total   = 253;   // 전체 게시글 수
$perPage = 20;    // 페이지당 20개
$page    = dx_get('page', 1, 'int');

// {page} 가 페이지 번호로 치환됨
$pager = dx_pagination(
    $total,
    $perPage,
    $page,
    dx_base_url('board/notice?page={page}'),
    5   // 페이지 버튼 5개 표시
);

echo '<div class="dx-pagination">' . $pager . '</div>';

// 출력 결과 HTML 예시:
// <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=3" class="dx-page-btn">›</a>


11. 게시판 데이터 헬퍼


11.1 dx_board_posts($boardKey, ...) — 게시글 배열 반환

홈페이지 메인 위젯•사이드바에서 특정 게시판의 최신 게시글을 배열로 가져옵니다.
 

파라미터

파라미터 기본값 설명
$boardKey (필수) 게시판 고유 키 (예: notice, free, gallery)
$limit 5 가져올 게시글 수
$withNotice false true이면 공지글 포함
$titleLen 0 제목 최대 글자수 (0이면 자르지 않음)
$excerptLen 100 본문 요약 글자수 (0이면 요약 없음)


반환 배열 구조

// 반환 배열 각 항목
$post = [
    'id'            => 123,           // 게시글 ID
    'title'         => '게시글 제목',   // 제목 (titleLen 적용)
    'author'        => '홍길동',        // 작성자 (익명이면 '익명')
    'date'          => '2025-04-24',   // 작성일 (Y-m-d)
    'date_short'    => '04.24',        // 작성일 짧게 (m.d)
    'url'           => 'https://...', // 게시글 상세 URL
    'board_key'     => 'notice',       // 게시판 키
    'view_count'    => 42,             // 조회수
    'comment_count' => 5,              // 댓글수
    'excerpt'       => '본문 요약...',  // 요약 (excerptLen > 0 이면)
];


사용 예

// 공지사항 최신 5개
$notices = dx_board_posts('notice');

// 자유게시판 10개 (공지 포함, 제목 30자, 요약 150자)
$posts = dx_board_posts('free', 10, true, 30, 150);

// 갤러리 6개
$gallery = dx_board_posts('gallery', 6);

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


11.2 dx_board_latest($boardKey, ...) — 스킨으로 직접 출력

게시글을 가져와 지정한 스킨 파일로 바로 렌더링합니다. dx_board_posts()와 달리 HTML을 직접 echo합니다.


스킨 파일 탐색 순서

  • 1순위: themes/{현재테마}/board_latest/{스킨명}.php
  • 2순위: themes/default/board_latest/{스킨명}.php
  • 3순위: themes/default/board_latest/list.php  (최종 폴백)


스킨 파일 내부에서 사용 가능한 변수

변수명 설명
$board_key 게시판 키 (예: notice)
$board_name DB에 저장된 게시판 이름
$title 섹션 표시 제목 (지정 없으면 board_name)
$icon 아이콘 이모지
$more_url 더보기 링크 URL
$posts dx_board_posts() 반환 배열
$show_excerpt 요약 표시 여부 (bool)


사용 예

// 기본 list 스킨으로 공지사항 5개 출력
dx_board_latest('notice');

// card 스킨, 섹션 제목과 아이콘 지정
dx_board_latest('free', 5, 'card', '자유게시판', '💬');

// 제목 25자 자르기 + 요약 포함
dx_board_latest('gallery', 6, 'simple', '갤러리', '🖼️', 25, true);

// 테마 파일(themes/default/home.php) 안에서 사용
// <?php dx_board_latest('notice', 5, 'list', '공지사항', '📢'); ?>


12. 에셋 및 폰트 출력 함수


12.1 dx_head_assets($opts = [])

모든 레이아웃 <head> 에서 호출하면 Pretendard, Space Grotesk, Font Awesome 6, dxb-css.js를 자동 삽입합니다.
 
// 기본 (모두 로드)
dx_head_assets();

// 선택적 로드
dx_head_assets([
    'pretend' => true,   // Pretendard 폰트
    'grotesk' => true,   // Space Grotesk 폰트
    'fa'      => true,   // Font Awesome 6
    'dxb'     => true,   // dxb-css.js (DXB 유틸리티 CSS 엔진)
]);

// Font Awesome 제외
dx_head_assets(['fa' => false]);

// 레이아웃 파일 head 섹션 예시
// <head>
//   <meta charset="UTF-8">
//   <?php dx_head_assets(); ?>
//   <?php dx_font_css(); ?>
// </head>


12.2 dx_font_css()

CSS 리셋 + Pretendard/Space Grotesk 폰트 변수 정의 + 스크롤바 스타일을 <style> 태그로 출력합니다.
 
// 레이아웃 <head> 또는 <style> 바로 다음에 호출
dx_font_css();

// 출력 내용:
// * 박스 모델 리셋 (box-sizing: border-box)
// * :root CSS 변수: --font-body, --font-head, --font-mono
// * body font-family 설정
// * a 기본 스타일 제거
// * img 반응형 설정
// * 스크롤바 스타일 (webkit)
// * 텍스트 선택 색상
// * .sg 클래스 (Space Grotesk 단축)


12.3 _dxAvaColor($name) — 아바타 색상 헬퍼

회원 이름을 해싱하여 8가지 고정 색상 중 하나를 반환합니다. 이미지 없는 회원의 텍스트 아바타 배경색 생성에 사용합니다.
 
$color = _dxAvaColor($member['name']);
// → '#3b82f6' (이름에 따라 항상 동일한 색상 반환)

// 아바타 출력 예시
$bg    = _dxAvaColor($member['name']);
$initial = mb_substr($member['name'], 0, 1, 'UTF-8');
echo '<div class="avatar" style="background:' . $bg . '">' . dx_esc($initial) . '</div>';


13. 로그•Ajax•플러그인 로더


13.1 dx_log($message, $level = 'info')

에러와 경고만 data/error.log 에 기록합니다. info/debug 레벨은 기록하지 않습니다. 호출 파일과 라인 번호가 자동 기록됩니다.
 
// 에러 기록 (기록됨)
dx_log('DB 쿼리 실패: ' . $e->getMessage(), 'error');

// 경고 기록 (기록됨)
dx_log('파일 업로드 실패: ' . $filename, 'warning');

// info/debug는 기록 안 됨 (성능 보호)
dx_log('게시글 조회: ' . $postId, 'info');   // 기록 안 됨

// 로그 파일 형식:
// [2025-04-24 14:30:45][ERROR][boards/handler.php:123] DB 쿼리 실패: ...


13.2 dx_is_ajax()

현재 요청이 XMLHttpRequest(Ajax) 인지 확인합니다. X-Requested-With: XMLHttpRequest 헤더를 기준으로 판단합니다.
 
if (dx_is_ajax()) {
    // Ajax 요청 → JSON 응답
    dx_json(['success' => true, 'html' => $html]);
} else {
    // 일반 요청 → 페이지 리다이렉트
    dx_redirect_back();
}


13.3 load_plugins()

DX_PLUGINS 디렉토리에서 각 플러그인의 plugin.php를 자동으로 require_once합니다. 플러그인 로드 후 DXB CSS 엔진 주입과 에디터 훅 브릿지를 자동 설정합니다.
 
// index.php 부팅 시 자동 호출됨 (직접 호출 불필요)
load_plugins();

// 플러그인 디렉토리 구조:
// plugins/
//   my_plugin/
//     plugin.php   ← 이 파일이 자동 로드됨
//   another_plugin/
//     plugin.php

// DXB CSS 엔진 자동 주입 (테마 수정 불필요):
// - dx_head 훅 (priority=1): dxb-css.js 로드 + 1차 rescan
// - dx_body_bottom 훅 (priority=1): 2차 rescan (rAF 포함)
// - dx_admin_top 훅 (priority=1): 관리자 영역 rescan


14. 포인트•경험치 훅 자동 등록


14.1 _dx_register_point_hooks()

게시글 작성, 댓글, 로그인, 가입, 좋아요 등의 이벤트에 포인트/경험치를 자동 부여하는 훅을 등록합니다. DxPoint 클래스 로드 후 자동 실행됩니다.
 
훅 이름 발동 시점 지급 포인트 종류
dx_after_write 게시글 작성 완료 write (글쓰기 포인트 + 경험치)
dx_after_comment 댓글 작성 완료 comment (댓글 포인트 + 경험치)
dx_after_login 로그인 성공 (일 1회) login (출석 포인트 + 경험치)
dx_after_register 신규 회원 가입 signup (가입 축하 포인트 + 경험치)
dx_after_like 좋아요 받음 like_recv (좋아요 받기 포인트 + 경험치)

포인트 설정
실제 지급 포인트 수량은 관리자 > 포인트 설정에서 각 항목별로 설정합니다.
함수 내 두 번째 인자 0은 설정값을 DB에서 자동으로 읽어오기 위한 플레이스홀더입니다.
로그인 포인트는 오늘 이미 받은 경우 point_log 테이블을 확인하여 중복 지급을 방지합니다.


15. 실전 통합 활용 패턴


15.1 게시글 목록 페이지 전체 패턴

<?php
// 1. 입력값 안전 수신
$page    = dx_get('page', 1, 'int');
$keyword = dx_get('keyword');
$catId   = dx_get('cat', 0, 'int');
$perPage = 20;

// 2. 데이터 조회
$db    = Database::getInstance();
$total = (int)$db->value(
    "SELECT COUNT(*) FROM posts WHERE board_id=? AND status=1",
    [$boardId]
);
$offset = ($page - 1) * $perPage;
$rows   = $db->rows(
    "SELECT * FROM posts WHERE board_id=? AND status=1
     ORDER BY id DESC LIMIT {$perPage} OFFSET {$offset}",
    [$boardId]
);

// 3. 페이지네이션 생성
$pager = dx_pagination(
    $total, $perPage, $page,
    dx_base_url($boardKey . '?page={page}&keyword=' . urlencode($keyword))
);

// 4. 날짜•제목 포맷
foreach ($rows as &$row) {
    $row['date_f']    = dx_date($row['created_at'], 'Y-m-d');
    $row['date_ago']  = dx_time_ago($row['created_at']);
    $row['title_s']   = dx_substr($row['title'], 30);
}

// 5. 출력 (스킨 파일에서)
foreach ($rows as $row) {
    echo '<a href="' . dx_safe_url(dx_base_url($boardKey.'/'.$row['id'])) . '">';
    echo dx_esc($row['title_s']);
    echo '</a> ' . dx_esc($row['date_ago']);
}
echo '<div class="pagination">' . $pager . '</div>';


15.2 Ajax POST 처리 핸들러 패턴

<?php
// Ajax 전용 핸들러 파일

// 1. CSRF 검증 (가장 먼저)
dx_csrf_check();

// 2. 로그인 확인
if (!Auth::getInstance()->isLoggedIn()) {
    dx_json(['success' => false, 'message' => '로그인이 필요합니다.'], 401);
}

// 3. 입력값 수신
$postId  = dx_post('post_id', '0', 'bigint');
$comment = dx_post('comment');

// 4. 유효성 검사
if (empty($comment)) {
    dx_json(['success' => false, 'message' => '댓글 내용을 입력하세요.'], 422);
}

// 5. DB 처리
try {
    $db = Database::getInstance();
    $id = $db->insert($db->table('comments'), [
        'post_id'    => $postId,
        'member_id'  => Auth::getInstance()->user()['id'],
        'content'    => DxSanitizer::cleanText($comment),
        'ip'         => dx_ip(),
        'created_at' => date('Y-m-d H:i:s'),
    ]);
    dx_json(['success' => true, 'comment_id' => $id]);
} catch (Exception $e) {
    dx_log('댓글 저장 실패: ' . $e->getMessage(), 'error');
    dx_json(['success' => false, 'message' => '저장 중 오류가 발생했습니다.'], 500);
}


15.3 홈페이지 메인 위젯 패턴

<?php // themes/default/home.php ?>

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <?php dx_head_assets(); ?>
  <?php dx_font_css(); ?>
</head>
<body>

<!-- 공지사항 위젯 (list 스킨) -->
<?php dx_board_latest('notice', 5, 'list', '공지사항', '📢'); ?>

<!-- 자유게시판 위젯 (card 스킨, 제목 25자, 요약 포함) -->
<?php dx_board_latest('free', 6, 'card', '자유게시판', '💬', 25, true); ?>

<!-- PHP 배열로 받아서 직접 처리 -->
<?php
$gallery = dx_board_posts('gallery', 8, false, 20);
foreach ($gallery as $p) {
    echo '<div class="card">';
    echo '  <a href="' . dx_safe_url($p['url']) . '">' . dx_esc($p['title']) . '</a>';
    echo '  <span>' . dx_esc($p['date_short']) . '</span>';
    echo '</div>';
}
?>

</body>
</html>


16. 전체 공통함수 빠른 참조표

함수명 카테고리 반환 타입 비고
dx_random_bytes($length) 보안 난수 string (bytes) PHP 5.6+ 자동 폴백
dx_random_hex($length=32) 보안 난수 string 16진수 토큰 생성
dx_realpath($path) 경로 string|false 역슬래시 → 슬래시
dx_path_inside($path, $base) 경로 bool 경로 탈출 방어
dx_is_https() HTTPS bool 프록시/CDN 포함
dx_base_url($path='') URL string URL Rewrite 자동
dx_static_url($path='') URL string 정적 자산 URL
dx_request_uri() URL string 이중슬래시 정규화
dx_current_url() URL string 현재 완전 URL
dx_redirect($url, $code=302) URL void+exit Location 헤더
dx_redirect_back($fallback='/') URL void+exit Referer 기반
dx_get($key, $default, $type) 요청 mixed $_GET 안전 수신
dx_post($key, $default, $type) 요청 mixed $_POST 안전 수신
dx_request($key, $default, $type) 요청 mixed $_REQUEST 안전 수신
dx_method($method) 요청 bool HTTP 메서드 비교
dx_cast($val, $type) 타입 mixed 타입 변환 헬퍼
dx_ip() IP string Cloudflare 포함
dx_ua() UA string UA 문자열 (500자)
dx_device() 디바이스 string mobile|tablet|pc
dx_os() 디바이스 string OS 이름
dx_browser() 디바이스 string 브라우저 이름
dx_config($key, $default) 설정 mixed $dx_config 읽기
dx_set_config($key, $value) 설정 void 런타임 설정 변경
dx_config_set($key, $value) 설정 void dx_set_config 별칭
dx_esc($str) 보안 string XSS 방어 출력
dx_safe_url($url) 보안 string URL XSS 방어
dx_csrf_token() 보안 string CSRF 토큰 반환
dx_csrf_field() 보안 string hidden input 반환
dx_csrf_check() 보안 void 검증 실패 시 403
dx_error($msg, $code=500) 응답 void+exit HTTP 오류 출력
dx_json($data, $code=200) 응답 void+exit JSON API 응답
dx_flash($msg, $type) 플래시 void 세션 메시지 저장
dx_set_flash($msg, $type) 플래시 void dx_flash 별칭
dx_get_flash() 플래시 array|null 읽고 세션 삭제
dx_substr($str, $len, $suffix) 문자열 string UTF-8 안전
dx_mb_substr($str, $start, $len) 문자열 string mb_substr 폴백
dx_time_ago($datetime) 날짜 string '방금 전' 형식
dx_date($datetime, $format) 날짜 string 날짜 포맷
dx_filesize($bytes) 파일 string B/KB/MB/GB 표시
dx_upload_url($path) 파일 string uploads URL
dx_upload_exists($path) 파일 bool 파일 존재 확인
dx_array_column($arr, $key, $idx) 배열 array PHP 5.4 폴백
dx_pagination(...) 페이지 string (HTML) 페이지 버튼 HTML
dx_board_posts($key, ...) 게시판 array 게시글 배열 반환
dx_board_latest($key, ...) 게시판 void (echo) 스킨으로 직접 출력
dx_head_assets($opts) 에셋 void (echo) CDN 폰트/아이콘
dx_font_css() 에셋 void (echo) CSS 리셋+폰트 변수
_dxAvaColor($name) 에셋 string (#hex) 아바타 색상
dx_log($message, $level) 로그 void error/warning만 기록
dx_is_ajax() Ajax bool XHR 요청 감지
load_plugins() 플러그인 void plugin.php 자동 로드
 
 
 
 

댓글0

로그인 후 댓글을 작성할 수 있습니다.
1. DX 철학 / 개념 왜 DXCMS를 만들었는가 2026.04.20 1. DX 철학 / 개념 DXCMS란 무엇인가 2026.04.20 DXCMS 활용 (CMS) DXCMS 날코딩•막코딩 완전 허용 2026.04.12
31
전체 회원
503
전체 게시글
770
전체 댓글
441
오늘 방문
33,173
전체 방문
2
현재 접속
인기글 7일 이내
최신글
최신댓글
목록