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

Hook 개념

A Administrator
2026.04.21 00:58(수정됨) 98 0

1. Hook 시스템이란?

Hook(훅) 시스템은 CMS 핵심 코드를 수정하지 않고도 동작에 끼어들거나 값을 변형할 수 있게 해주는 확장 메커니즘입니다. DXCMS는 WordPress의 Action/Filter 개념을 PHP 5.6+ 호환으로 경량화하여 내장하고 있습니다.

핵심 개념 한 줄 요약
  • 훅 포인트 = CMS가 "여기서 끼어들어도 돼" 하고 열어 둔 자리
  • 콜백 등록 = 개발자가 "나 여기서 이 코드 실행할게" 하고 예약하는 것
  • 훅 실행 = CMS가 그 자리에 도달하면 등록된 콜백을 순서대로 호출하는 것


1.1 Hook의 두 가지 종류

종류 설명
Action Hook (dx_run_hook) CMS의 특정 시점에 코드를 실행합니다. 반환값 없음. "이 시점에 내 코드를 실행하라"는 명령입니다.
Filter Hook (dx_apply_filter) 값이 출력되기 전 가로채어 변형합니다. 반환값이 있음. "이 값을 내가 원하는 형태로 바꿔라"는 명령입니다.


1.2 Hook vs extend/ 폴더 비교

DXCMS에는 Hook 외에 extend/ 폴더라는 또 다른 확장 수단이 있습니다. 두 방식의 차이를 명확히 이해해야 합니다.
 
구분 Hook (dx_add_hook) extend/ 폴더
등록 방법 코드에서 함수 호출로 등록 파일을 폴더에 넣으면 자동 실행
실행 시점 훅 이름으로 정밀 제어 top / middle / bottom 3단계만
우선 순위 priority 숫자로 세밀 조정 파일명 오름차순으로만 제어
대상 개발자 (플러그인 제작자) 누구나 (설정 파일, 간단한 코드)
격리 try-catch로 자체 관리 필요 파일별 자동 에러 격리


2. HookManager 클래스 구조

HookManager는 싱글턴(Singleton) 패턴으로 구현되어 있으며 core/hook/HookManager.php에 위치합니다. 개발자는 직접 클래스를 사용하지 않고 전역 헬퍼 함수를 사용합니다.


2.1 핵심 메서드

메서드 / 함수 설명
dx_add_hook($name, $callback, $priority) 훅 포인트에 콜백 등록. priority가 낮을수록 먼저 실행 (기본값 10)
dx_run_hook($name, $args) Action 훅 실행. 등록된 콜백을 순서대로 호출. 반환값 없음
dx_apply_filter($name, $value, $args) Filter 훅 실행. 값을 콜백 체인에 통과시켜 변형된 값 반환
dx_add_filter($name, $callback, $priority) dx_add_hook의 별칭. Filter 의미를 명확히 하기 위해 사용
dx_remove_hook($name, $callback) 등록된 특정 콜백 제거. callback 생략 시 해당 훅 전체 제거
dx_has_hook($name) 해당 훅에 콜백이 등록되어 있는지 확인. boolean 반환


2.2 priority(우선순위) 동작 방식

priority는 정수값으로, 숫자가 작을수록 먼저 실행됩니다. 같은 priority면 등록 순서대로 실행됩니다.
 
// priority 1 → 가장 먼저 실행
dx_add_hook('dx_head', function() { echo '<!-- 먼저 -->'; }, 1);
 
// priority 10 → 기본값, 두 번째 실행
dx_add_hook('dx_head', function() { echo '<!-- 기본 -->'; });
 
// priority 999 → 가장 나중에 실행
dx_add_hook('dx_head', function() { echo '<!-- 마지막 -->'; }, 999);


3. 표준 훅 포인트 전체 목록

DXCMS v8.1.0은 아래 훅 포인트를 기본 제공합니다. 플러그인 개발 시 이 목록에서 필요한 훅을 선택해 사용합니다.


3.1 레이아웃 훅 (Layout Hooks)

테마 main.php에서 실행되는 훅입니다. HTML 출력에 직접 개입합니다.
 
훅 이름 실행 위치 / 용도
dx_head <head> 태그 내부. CSS•JS 링크, 메타 태그 추가
dx_top <body> 시작 직후. 전역 배너, 공지, 분석 스크립트
dx_middle 콘텐츠 영역 내부. 페이지별 맞춤 콘텐츠 삽입
dx_bottom </body> 직전. 하단 공통 위젯, 팝업
dx_footer_scripts body 닫기 직전 스크립트 영역. JS 초기화 코드
dx_body_bottom 최종 출력 직전. 전역 JS 실행 (priority 1 = 최우선)

페이지 타입별 세분화 훅도 자동 생성됩니다. context의 type 값에 따라 다음과 같이 추가 실행됩니다.
 
// type='board' 게시판 페이지라면 아래 훅들이 추가로 실행됩니다:
dx_board_top     // dx_{type}_top
dx_board_middle  // dx_{type}_middle
dx_board_bottom  // dx_{type}_bottom
 
// slug='notice' 페이지라면:
dx_page_notice_top
dx_page_notice_middle
dx_page_notice_bottom


3.2 인증 훅 (Auth Hooks)

훅 이름 전달 인자 / 용도
dx_after_login array('user' => $user) — 로그인 성공 후. 포인트 지급, 알림 발송
dx_after_logout array('user' => $user) — 로그아웃 후. 세션 정리, 로그 기록
dx_after_register array('user_id' => $id, 'data' => ...) — 회원가입 완료 후. 환영 메일 발송


3.3 게시판 훅 (Board Hooks)

훅 이름 전달 인자 / 용도
dx_board_before board, action, skin, id — 게시판 처리 시작 전. 접근 제어
dx_board_before_save data(참조), board, action — 저장 직전 데이터 변형 가능
dx_board_after_save post_id, board, redirect(참조) — 저장 완료 후. 알림, 포인트
dx_after_write post_id, board, data — 글 작성 완료 후. 전역 처리
dx_board_before_delete post, board_key — 삭제 직전. 관련 파일 정리
dx_board_after_delete post_id, board_key — 삭제 완료 후
dx_board_list_context context(참조), board — 목록 컨텍스트 변형
dx_board_view_context context(참조), board, post — 상세 컨텍스트 변형


3.4 커뮤니티 활동 훅

훅 이름 전달 인자 / 용도
dx_after_comment post_id, board_key, comment_id, member_id — 댓글 작성 후
dx_after_like post_id, owner_id — 좋아요 클릭 후. 포인트, 알림
dx_add_friend member_id, target_id — 친구 추가 완료 후
dx_after_point member_id, type, point, balance — 포인트 변동 후
dx_levelup member_id, old_level, new_level — 레벨 상승 시


3.5 플러그인•드라이버 훅

훅 이름 용도
dx_editor_render 에디터 HTML 렌더링. CKEditor4 등 커스텀 에디터 적용
dx_editor_init 에디터 초기화 JS 코드 삽입
dx_payment_request 결제 처리. 토스•카카오•이니시스 등 PG사 플러그인이 등록
dx_shop_after_purchase 구매 완료 후. 디지털 상품 배포, 쿠폰 발급
dx_mailer_drivers 메일 드라이버 등록. SMTP•SendGrid 등 커스텀 드라이버
dx_sms_drivers SMS 드라이버 등록. CoolSMS•NCP 등 커스텀 드라이버
dx_captcha_drivers Captcha 드라이버 등록. hCaptcha•Turnstile 등


3.6 관리자 훅

훅 이름 용도
dx_admin_top 관리자 패널 상단. 공지, 긴급 알림
dx_admin_bottom 관리자 패널 하단
dx_admin_dashboard_widgets 대시보드 위젯 추가 (dx-socket 플러그인 등에서 활용)


4. Action Hook 실전 예제


4.1 페이지 하단에 성능 표시 (디버그)

example-plugin/plugin.php 참고. 모든 페이지 우측 하단에 실행 시간과 DB 쿼리 수를 출력합니다.
 
if (defined('DX_DEBUG') && DX_DEBUG) {
    dx_add_hook('dx_bottom', function($context) {
        $time = round((microtime(true) - DX_START_TIME) * 1000, 2);
        $db   = Database::getInstance();
        echo '<div style="position:fixed;bottom:10px;right:10px;
              background:rgba(0,0,0,.7);color:#fff;padding:6px 12px;
              border-radius:8px;font-size:11px;z-index:9999">';
        echo '⚡ ' . $time . 'ms | DB: ' . $db->getQueryCount() . '쿼리';
        echo '</div>';
    }, 999);  // priority 999 = 가장 마지막에 실행
}


4.2 로그인 후 환영 메시지 표시

dx_add_hook('dx_after_login', function($args) {
    $user = $args['user'];
    $_SESSION['welcome_msg'] = $user['nickname'] . '님, 환영합니다!';
});
 
// 이후 dx_top 훅에서 세션 메시지를 화면에 표시
dx_add_hook('dx_top', function() {
    if (!empty($_SESSION['welcome_msg'])) {
        echo '<div class="dx-alert dx-alert-success">'
           . htmlspecialchars($_SESSION['welcome_msg'])
           . '</div>';
        unset($_SESSION['welcome_msg']);
    }
});


4.3 게시글 저장 전 데이터 보정

dx_board_before_save 훅의 data 인자는 참조(&)로 전달됩니다. 값을 수정하면 실제 저장 데이터에 반영됩니다.
 
dx_add_hook('dx_board_before_save', function($args) {
    $data  = &$args['data'];   // 참조 전달 — 수정하면 저장에 반영됨
    $board = $args['board'];
 
    // 제목이 비어 있으면 내용 앞 20자를 자동 제목으로
    if (empty($data['title']) && !empty($data['content'])) {
        $data['title'] = mb_substr(strip_tags($data['content']), 0, 20) . '...';
    }
 
    // 특정 게시판에서 금지어 필터
    if ($board['board_key'] === 'free') {
        $data['content'] = str_replace(['광고', '스팸'], '***', $data['content']);
    }
});


4.4 회원가입 후 환영 메일 발송

dx_add_hook('dx_after_register', function($args) {
    $userId = $args['user_id'];
    $data   = $args['data'];
 
    try {
        $mailer = DxMailer::getInstance();
        $mailer->send(
            $data['email'],
            '회원가입을 환영합니다!',
            '<h2>안녕하세요 ' . htmlspecialchars($data['nickname']) . '님</h2>'
            . '<p>DXCMS에 가입해 주셔서 감사합니다.</p>'
        );
    } catch (Exception $e) {
        dx_log('[welcome-mail] ' . $e->getMessage(), 'warning');
    }
});


4.5 결제 플러그인 연동 (dx_payment_request)

결제 플러그인들은 dx_payment_request 훅에 자신을 등록합니다. 실제 tosspay-payment/plugin.php 구조입니다.
 
// plugins/my-payment/plugin.php
dx_add_hook('dx_payment_request', function($data) {
    // $data['payment'] 에 결제 수단 ID가 담겨 있음
    if ($data['payment'] !== 'my_payment') return;  // 내 플러그인만 처리
 
    // 결제 처리 로직
    $amount  = (int)$data['amount'];
    $orderId = $data['order_id'];
 
    // 결제 API 호출...
    header('Content-Type: application/json');
    echo json_encode(array('success' => true, 'redirect' => '/payment/complete'));
    exit;
});


4.6 head에 CSS•JS 삽입

dx_head 훅은 <head> 태그 내부에서 실행됩니다. 플러그인 전용 스타일시트나 스크립트를 삽입할 때 사용합니다.
 
dx_add_hook('dx_head', function() {
    $base = rtrim(dx_base_url(''), '/');
    echo '<link rel="stylesheet" href="' . $base . '/plugins/my-plugin/style.css">';
    echo '<script src="' . $base . '/plugins/my-plugin/script.js"></script>';
});
 
// body 닫기 직전에 JS 실행이 필요할 때
dx_add_hook('dx_body_bottom', function() {
    echo '<script>
        document.addEventListener("DOMContentLoaded", function() {
            MyPlugin.init();
        });
    </script>';
});


5. Filter Hook 실전 예제

Filter 훅은 값을 가로채어 변형한 뒤 돌려줍니다. 반드시 변형된 값을 return 해야 합니다. DXCMS에서는 dx_apply_filter()와 dx_add_filter()를 사용합니다.

Filter 훅 사용 원칙
콜백에서 반드시 $value를 return 해야 합니다. return을 빠뜨리면 이후 콜백에 null이 전달됩니다.
여러 콜백이 체인처럼 연결됩니다. 앞 콜백의 반환값이 다음 콜백의 $value가 됩니다.
원본 값의 타입을 유지하는 것이 좋습니다. 문자열로 받았으면 문자열을 반환하세요.


5.1 기본 Filter 훅 등록과 실행 구조

// ① CMS 내부 코드 (훅 포인트 정의)
$content = dx_apply_filter('dx_post_content', $rawContent, array('post_id' => $id));
echo $content;  // 필터링된 내용 출력
 
// ② 플러그인/확장 코드 (필터 등록)
dx_add_filter('dx_post_content', function($value, $args) {
    // URL을 자동으로 링크로 변환
    $value = preg_replace(
        '/(https?:\/\/[^\s<>"]+)/',
        '<a href="$1" target="_blank">$1</a>',
        $value
    );
    return $value;  // 반드시 return!
});


5.2 다중 Filter 체인

같은 훅에 여러 필터를 등록하면 priority 순으로 체인을 형성합니다.
 
// 필터 1: HTML 태그 허용 목록 적용 (priority 5)
dx_add_filter('dx_post_content', function($value) {
    $allowed = '<p><br><strong><em><ul><ol><li><a><img>';
    return strip_tags($value, $allowed);
}, 5);
 
// 필터 2: 이모지 변환 (priority 10 = 기본, 1 다음에 실행)
dx_add_filter('dx_post_content', function($value) {
    return str_replace(':)', '😊', $value);
}, 10);
 
// 필터 3: 광고 문구 차단 (priority 20, 가장 나중)
dx_add_filter('dx_post_content', function($value) {
    return preg_replace('/광고[^\n]*/u', '[광고 차단]', $value);
}, 20);


6. 플러그인에서 Hook 등록하기

DXCMS 플러그인은 plugins/{plugin-name}/plugin.php 파일에 훅을 등록합니다. 이 파일은 CMS 초기화 완료 후 자동으로 include됩니다.


6.1 플러그인 파일 기본 구조

<?php
/**
 * My Plugin - 플러그인 설명
 */
if (!defined('DX_CMS')) exit('Direct access not allowed.');
 
// ① 페이지 상단에 안내 배너 표시
dx_add_hook('dx_top', function($context) {
    if (!isset($context['type'])) return;
    echo '<div class="my-notice">공지: 서비스 점검 예정 안내</div>';
}, 1);
 
// ② 레벨업 시 알림 발송
dx_add_hook('dx_levelup', function($args) {
    $memberId = $args['member_id'];
    $newLevel = $args['new_level'];
    // 알림 DB 저장 등 처리
    DxNotification::send($memberId, '레벨이 ' . $newLevel . '로 상승했습니다!');
});
 
// ③ 관리자 대시보드 위젯 추가
dx_add_hook('dx_admin_dashboard_widgets', function() {
    echo '<div class="dx-card"><h3>My Plugin 통계</h3>.....</div>';
});


6.2 훅 제거 (우선순위로 교체)

기존에 등록된 훅을 제거하거나 새 콜백으로 교체할 수 있습니다.
 
// 특정 콜백 제거
$myCallback = function($args) { /* ... */ };
dx_add_hook('dx_top', $myCallback);
 
// 나중에 제거
dx_remove_hook('dx_top', $myCallback);
 
// 해당 훅 전체 콜백 제거 (callback 생략)
dx_remove_hook('dx_top');

6.3 훅 등록 여부 확인

if (dx_has_hook('dx_payment_request')) {
    // 결제 플러그인이 설치되어 있을 때만 UI 표시
    echo '<button>결제하기</button>';
} else {
    echo '<p>결제 플러그인을 설치해 주세요.</p>';
}


7. context 인자 활용

dx_hook_top / dx_hook_middle / dx_hook_bottom으로 실행되는 레이아웃 훅에는 context 배열이 전달됩니다. 이 값으로 현재 페이지 종류를 판단할 수 있습니다.


7.1 context 주요 키

설명 / 예시
type 페이지 유형. 'board', 'page', 'auth', 'admin', 'search' 등
slug 페이지 슬러그. board_key 또는 page slug. 예: 'free', 'notice'
action 현재 액션. 'list', 'view', 'write', 'edit' 등
board_key 게시판 키값 (type=board일 때). 예: 'free'


7.2 특정 페이지에서만 실행

dx_add_hook('dx_bottom', function($context) {
 
    // 게시판 목록 페이지에서만 실행
    if (($context['type'] ?? '') !== 'board') return;
    if (($context['action'] ?? '') !== 'list') return;
 
    echo '<script>console.log("게시판 목록 페이지");</script>';
});
 
dx_add_hook('dx_middle', function($context) {
 
    // 'free' 게시판에서만 사이드 위젯 표시
    if (($context['board_key'] ?? '') !== 'free') return;
 
    echo '<aside class="free-board-widget">.....</aside>';
});


8. 커스텀 훅 포인트 만들기

플러그인이나 테마에서 직접 훅 포인트를 정의하면, 다른 플러그인이 해당 시점에 끼어들 수 있습니다. 오픈 구조를 만들어 확장성을 높이는 방법입니다.

8.1 Action 훅 포인트 정의

// 내 플러그인 A: 처리 완료 후 훅 포인트 제공
function my_plugin_process($data) {
    // 처리 로직...
    $result = do_something($data);
 
    // 다른 플러그인이 끼어들 수 있는 포인트 제공
    dx_run_hook('my_plugin_after_process', array(
        'data'   => $data,
        'result' => $result,
    ));
 
    return $result;
}
 
// 플러그인 B: A의 훅 포인트를 활용
dx_add_hook('my_plugin_after_process', function($args) {
    dx_log('[플러그인B] 처리 완료: ' . json_encode($args['result']), 'info');
});


8.2 Filter 훅 포인트 정의

// 플러그인 A: 출력 전 필터 포인트 제공
function my_plugin_render($html) {
    // 다른 플러그인이 HTML을 변형할 수 있게 허용
    $html = dx_apply_filter('my_plugin_render', $html);
    echo $html;
}
 
// 플러그인 C: A의 HTML에 광고 삽입
dx_add_filter('my_plugin_render', function($html) {
    return $html . '<div class="ad">광고</div>';
});


9. 디버그 & 트러블슈팅


9.1 실행된 훅 목록 확인

HookManager::getExecuted()로 현재 요청에서 실행된 훅 목록을 확인할 수 있습니다.
 
if (defined('DX_DEBUG') && DX_DEBUG) {
    dx_add_hook('dx_bottom', function() {
        $executed = HookManager::getInstance()->getExecuted();
        echo '<pre style="font-size:11px;background:#f5f5f5;padding:10px">';
        echo '실행된 훅 (' . count($executed) . '개):\n';
        print_r($executed);
        echo '</pre>';
    }, 9999);
}


9.2 등록된 훅 전체 목록 확인

$allHooks = HookManager::getInstance()->getAll();
// 반환 예시:
// ['dx_head', 'dx_body_bottom', 'dx_after_login', 'dx_payment_request', ...]
 
// 특정 훅에 몇 개의 콜백이 등록되어 있는지 확인
$cnt = HookManager::getInstance()->count('dx_payment_request');
// 예: 2 (tosspay + kakaopay 두 개 플러그인이 등록된 경우)


9.3 자주 발생하는 실수

실수 올바른 방법
Filter 훅에서 return을 빠뜨림 콜백 내에서 반드시 return $value; 작성
참조 인자를 복사로 받음 data(참조)는 &$args['data']로 받아야 수정 반영됨
훅 이름 오타 dx_has_hook()으로 훅 이름 존재 여부 먼저 확인
훅 등록 위치가 늦음 플러그인 파일 최상단에서 등록해야 실행 전에 예약됨
priority 충돌 코어 훅은 1~99, 플러그인은 100+, 디버그는 999로 규칙 통일


10. 훅 개발 가이드라인


10.1 priority 사용 규칙 권장

범위 용도
1 ~ 9 최우선 실행 필요 (보안 체크, 인증 확인, 필수 데이터 주입)
10 (기본) 일반 플러그인 로직 (기본값 유지 권장)
11 ~ 99 코어에 의존하는 로직 (코어 콜백 실행 후 처리)
100 ~ 998 서드파티 플러그인 간 충돌 방지용 범위
999 디버그, 로깅, 정리 작업 (가장 마지막에 실행)


10.2 안전한 훅 콜백 작성

dx_add_hook('dx_after_write', function($args) {
    // ① 필수 인자 확인
    if (empty($args['post_id'])) return;
 
    // ② try-catch로 에러 격리 (훅 실패가 CMS 전체를 멈추면 안 됨)
    try {
        $postId = (int)$args['post_id'];
        // 처리 로직...
    } catch (Exception $e) {
        dx_log('[my-plugin][dx_after_write] ' . $e->getMessage(), 'error');
    }
});


10.3 훅 이름 네이밍 규칙

  • DXCMS 내장 훅: dx_ 접두사 (예: dx_after_login)
  • 플러그인 자체 훅: {plugin-name}_ 접두사 (예: tosspay_after_payment)
  • 테마 훅: theme_ 접두사 (예: theme_header_end)
  • 충돌 방지를 위해 고유한 접두사를 반드시 사용하세요.

최종 정리
Hook은 CMS 코드를 수정하지 않고 동작을 확장하는 핵심 메커니즘입니다.
Action 훅(dx_run_hook)은 실행, Filter 훅(dx_apply_filter)은 값 변형에 사용합니다.
priority로 실행 순서를 제어하며, 낮은 숫자가 먼저 실행됩니다.
모든 콜백은 try-catch로 에러를 격리하여 CMS 안정성을 보장하세요.
커스텀 훅 포인트를 정의하면 내 플러그인도 다른 플러그인이 확장할 수 있습니다.

 

댓글0

로그인 후 댓글을 작성할 수 있습니다.
3.7 Hook 시스템 Hook 시스템 활용 사례 2026.04.21 3.7 Hook 시스템 실행 타이밍 2026.04.21 3.7 Hook 시스템 Hook 개념 2026.04.21
30
전체 회원
269
전체 게시글
144
전체 댓글
181
오늘 방문
28,530
전체 방문
1
현재 접속
인기글 7일 이내
최신글
최신댓글
목록