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

실행 타이밍

A Administrator
2026.04.21 00:59(수정됨) 96 0

1. 전체 실행 흐름 개요

DXCMS의 모든 요청은 index.php 단일 진입점을 통해 처리됩니다. 요청이 들어온 순간부터 응답이 완료될 때까지 5단계를 거치며, 각 단계마다 훅 포인트가 배치되어 있습니다.

핵심 원칙

  • 훅은 "CMS가 특정 작업을 완료한 직후" 또는 "특정 작업을 시작하기 직전"에 실행됩니다.
  • 훅 등록(dx_add_hook)은 STEP 4 load_plugins() 시점에 완료되어야 합니다.
  • 훅 실행(dx_run_hook)은 반드시 등록 이후에 이루어집니다. 등록 전 실행은 무시됩니다.


1.1 5단계 실행 구조 (index.php 기준)


1. STEP 1 — 클래스•함수 로드

functions.php, DxCache, Secure, Database, HookManager, PluginRegistry, Auth, DxExtend, Dispatcher 등 모든 클래스 파일을 require_once로 로드합니다. 이 단계는 정의만 하며 실행은 없습니다.


2. STEP 2 — 보안 초기화

세션 설정 → 세션 시작 → 보안 헤더 발행 → CSRF 토큰 발급 순서로 실행됩니다. 훅 없음.


3. STEP 3 — DB 연결 + 설정 로드

data/config.php를 실행해 DB 연결, dx_config 설정, DX_SECRET_KEY 주입을 완료합니다. 훅 없음.


4. STEP 4 — 초기화 + 훅 등록

HookManager, PluginRegistry, load_plugins(), DxSite, DxTheme, Auth 순서로 초기화. 플러그인이 여기서 dx_add_hook을 실행합니다.
dx_body_bottom (팝업)  dx_after_login (회원모니터)  dx_after_logout (회원모니터)


5. STEP 5 — 라우팅 + 디스패치 + 렌더링

routes/ 로드 → DxRouter → Dispatcher::dispatch() 순서. extend/top → 라우팅 → extend/middle → 핸들러 → 렌더링 → extend/bottom.


1.2 extend/ 폴더와 Hook의 실행 시점 비교

실행 방식 실행 시점 (index.php 기준) 주요 용도
extend/top/ STEP 4 완료 직후 (플러그인•Auth•DxSite•DxTheme 모두 완료) 점검 모드, IP 차단, 전역 변수 주입
extend/middle/ Dispatcher::dispatch() 내부 라우트 확정 직후, 핸들러 실행 전    방문자 로그(01_visit_tracker.php), 접근 제어
extend/bottom/ Dispatcher 완료 직후 ob_end_flush() 직전 캐시 저장, 성능 로그, 정리 작업
dx_head 훅 테마 main.php의 </head> 바로 앞 CSS•JS•메타태그 삽입
dx_top 훅 테마 main.php의 <body> 시작 직후 헤더 네비게이션 아래 공지 배너, 팝업 트리거
dx_middle 훅 테마 main.php의 <main> 태그 안 실제 콘텐츠 바로 앞 사이드 위젯, 보조 콘텐츠
dx_bottom 훅 테마 main.php의 </footer> 직후 스크립트 블록 앞 플로팅 버튼, 하단 위젯
dx_footer_scripts 훅 테마 main.php의 모든 JS 다음 추가 스크립트 태그 삽입
dx_body_bottom 훅 맨 마지막 (dx_footer_scripts 바로 다음) 팝업 렌더링, 전역 JS 실행


2. index.php 단계별 실행 타이밍


2.1 STEP 4 — 초기화 완료 후 훅 등록

STEP 4는 훅 시스템이 활성화되는 핵심 단계입니다. load_plugins() 호출 시 plugins/ 폴더의 plugin.php 파일들이 실행되며, 각 플러그인이 dx_add_hook으로 콜백을 등록합니다.
 
 
HookManager::getInstance();     // 훅 매니저 싱글턴 생성
PluginRegistry::getInstance();  // 플러그인 레지스트리 생성
load_plugins();                 // ← plugins/*/plugin.php 실행
                                //   이 시점에 dx_add_hook이 모두 등록됨
_dx_register_point_hooks();    // 포인트 시스템 훅 등록
 
DxSite::getInstance();          // 멀티사이트 도메인 설정 적용
DxTheme::getInstance();         // 테마 결정 (DxSite 이후)
Auth::getInstance();            // 세션 기반 인증 초기화
DxContainer::getInstance()->registerCoreServices();
 
// CMS가 직접 등록하는 훅들
dx_add_hook('dx_body_bottom', function() {
    DxPopup::render();           // 팝업 자동 출력 (priority 50)
}, 50);
 
dx_add_hook('dx_after_login', function($args) {
    DxMemberMonitor::onLogin((int)$args["user"]["id"]);
}, 20);
 
// extend/top/ 실행 — 모든 초기화 완료 직후
DxExtend::getInstance()->runTop(array(
    'version' => DX_VERSION,
    'path'    => dx_request_uri(),
));

주의: 훅 등록 가능 최초 시점
dx_add_hook()은 HookManager가 생성된 이후부터 호출 가능합니다.
STEP 1의 함수 파일 로드 시점에는 훅을 등록할 수 없습니다 (HookManager 미생성).
플러그인의 plugin.php는 load_plugins() 내부에서 include 되므로 STEP 4가 안전한 등록 시점입니다.


2.2 STEP 5 — 라우팅과 extend/middle 실행

// index.php STEP 5 — Dispatcher 실행 전후
 
// ① routes/ 폴더 자동 로드 (DxRouter 기반 라우트)
$_dxRouteFiles = glob(DX_ROOT . "/routes/*.php");
foreach ($_dxRouteFiles as $f) require_once $f;
 
// ② 라우팅 + 디스패치
if (!DxRouter::dispatch()) {
    $dispatcher = new Dispatcher(new Router());
    $dispatcher->dispatch();
    // dispatch() 내부에서:
    //   1) Router::resolve() — 라우트 확정
    //   2) extend/middle/ 실행  ← 이 시점에 $GLOBALS["dx_route"] 사용 가능
    //   3) switch(type) — 핸들러 실행
}
 
// ③ 렌더링 완료 후 extend/bottom/ 실행
DxExtend::getInstance()->runBottom(array(
    'elapsed' => round((microtime(true) - DX_START) * 1000, 2),
));
 
// ④ 출력 버퍼 flush
if (ob_get_level() > 0) ob_end_flush();


3. 테마 레이아웃 훅 실행 타이밍

테마의 themes/default/layout/main.php가 렌더링될 때 6개의 훅이 HTML 구조 내 특정 위치에서 실행됩니다. 각 훅이 HTML의 어느 위치에 있는지 정확히 파악해야 올바른 훅을 선택할 수 있습니다.


3.1 main.php HTML 구조와 훅 위치

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>...</title>
  <style>...</style>   <!-- CMS 기본 스타일 -->
 
  ★ [훅 1] dx_head  ← 289번 줄 (</head> 바로 앞)
  <!-- Google Analytics, 웹마스터 메타태그 추가 위치 -->
  <script src="jquery.min.js"></script>
</head>
 
<body>
  <!-- 헤더 네비게이션, 드로어 메뉴 -->
 
  ★ [훅 2] dx_top  ← 649번 줄 (헤더 아래, 상단 유틸바 위)
 
  <div class="dx-topbar">  <!-- 상단 유틸바 --></div>
  <header>...</header>     <!-- GNB 메뉴 --></header>
 
  <main id="dx-main">
    ★ [훅 3] dx_middle  ← 1135번 줄 (콘텐츠 바로 앞)
 
    <!-- 실제 페이지 콘텐츠 ($dx_content) -->
    <?php echo $dx_content; ?>
  </main>
 
  <footer>...</footer>
 
  ★ [훅 4] dx_bottom  ← 1854번 줄 (</footer> 직후)
 
  <script>/* CMS 기본 JS 코드 */</script>
 
  ★ [훅 5] dx_footer_scripts  ← 2129번 줄
  ★ [훅 6] dx_body_bottom     ← 2130번 줄
</body>
</html>


3.2 레이아웃 훅 상세 설명

훅 이름 HTML 문맥 주요 용도
dx_head <head> 내부 </head> 바로 앞 외부 CSS 파일 링크 구글 폰트 로드 favicon 추가 커스텀 메타태그
dx_top <body> 내부 헤더 메뉴 아래 전체 페이지 공지 배너 점검 안내 띠 A/B 테스트 overlay
dx_middle <main> 내부 $dx_content 앞 콘텐츠 앞 보조 안내 사이드 패널 맞춤형 위젯
dx_bottom </footer> 직후 <script> 블록 앞 플로팅 버튼(채팅, 상단이동) 하단 위젯 쿠키 동의 배너
dx_footer_scripts CMS JS 다음 </body> 직전 추가 <script> 태그 외부 라이브러리 로드
dx_body_bottom 맨 마지막 (dx_footer_scripts 다음) 팝업 렌더링(priority 50) 전역 JS 초기화 디버그 도구


3.3 dx_top의 세분화 — 페이지 타입별 자동 훅

dx_hook_top(), dx_hook_middle(), dx_hook_bottom() 함수는 context의 type과 slug 값에 따라 추가 훅을 자동으로 실행합니다. 이를 통해 특정 페이지 타입에서만 실행되는 훅을 만들 수 있습니다.
 
// HookManager.php의 dx_hook_top() 내부 구현
function dx_hook_top($context = array()) {
    dx_run_hook('dx_top', $context);            // ① 전체 페이지 공통
 
    if (isset($context['type'])) {
        dx_run_hook('dx_' . $context['type'] . '_top', $context);
        // 예: type='board' → dx_board_top
        // 예: type='page' → dx_page_top
        // 예: type='admin' → dx_admin_top  ← 관리자 전용!
    }
 
    if (isset($context['slug'])) {
        dx_run_hook('dx_page_' . $context['slug'] . '_top', $context);
        // 예: slug='notice' → dx_page_notice_top
        // 예: slug='free'   → dx_page_free_top
    }
}
 
// ─────────────────────────────────────────────
// 실제 활용 예시: 게시판에서만 실행
dx_add_hook('dx_board_top', function($context) {
    echo '<div class="board-notice">게시판 이용 규칙...</div>';
});
 
// notice 게시판 목록에서만 실행
dx_add_hook('dx_page_notice_top', function($context) {
    echo '<div class="notice-banner">공지사항 배너</div>';
});


4. 게시판 핸들러 훅 실행 타이밍

게시판 요청(boards/handler.php)은 자체적인 훅 포인트를 가집니다. 핸들러 진입부터 렌더링 완료까지 총 8개의 훅 포인트가 배치되어 있습니다.


4.1 boards/handler.php 실행 흐름

// boards/handler.php 실행 순서 (실제 소스 기반)
 
// ─── 공통 진입 훅 ─────────────────────────────────
dx_run_hook('dx_board_before', array(
    'board'  => $board,
    'action' => $action,   // list | view | write | edit | reply | delete
    'skin'   => $boardSkin,
    'id'     => $id,
));
 
// ─── 액션별 분기 (switch) ─────────────────────────
switch ($action) {
 
  case 'list':
    // 목록 컨텍스트 준비 후:
    dx_run_hook('dx_board_list_context',
        array('context' => &$ctx, 'board' => $board));  // 참조 전달
    // 렌더링...
    break;
 
  case 'view':
    // 상세 컨텍스트 준비 후:
    dx_run_hook('dx_board_view_context',
        array('context' => &$ctx, 'board' => $board, 'post' => $post));
    // 렌더링...
    break;
 
  case 'write':
  case 'edit':
    dx_run_hook('dx_board_write_context',
        array('context' => &$ctx, 'board' => $board));
    // 저장 처리 시:
    dx_run_hook('dx_board_before_save',
        array('data' => &$data, 'board' => $board, 'action' => $action));
    // DB 저장 완료 후:
    dx_run_hook('dx_after_write',
        array('post_id' => $postId, 'board' => $board, 'data' => $data));
    dx_run_hook('dx_board_after_save',
        array('post_id' => $postId, 'board' => $board, 'redirect' => &$redirect));
    break;
 
  case 'delete':
    dx_run_hook('dx_board_before_delete',
        array('post' => $post, 'board' => $board));
    // 삭제 처리...
    dx_run_hook('dx_board_after_delete',
        array('post_id' => $id, 'board' => $board));
    break;
}
 
// ─── 공통 종료 훅 ─────────────────────────────────
dx_run_hook('dx_board_after', array(
    'board'  => $board,
    'action' => $action,
    'skin'   => $boardSkin,
));


4.2 게시판 훅 타이밍 표

훅 이름 실행 시점 인자 특이사항
dx_board_before 핸들러 진입 즉시 모든 처리 전 일반 전달. 여기서 exit()하면 게시판 전체 차단 가능
dx_board_list_context 목록 데이터 조회 완료 렌더링 직전 context를 참조(&)로 전달. 콘텐츠를 변형하면 목록에 반영됨
dx_board_view_context 게시글 조회 완료 렌더링 직전 context, post 참조 전달
dx_board_write_context 작성 폼 렌더링 직전 context 참조 전달
dx_board_before_save DB 저장 직전 (write•edit•reply 공통) data를 참조(&)로 전달. 값을 수정하면 실제 저장 데이터에 반영됨
dx_after_write DB 저장 완료 직후 포인트, 알림, 통계 처리에 사용
dx_board_after_save dx_after_write 바로 다음 redirect를 참조로 전달. 저장 후 이동 URL 변경 가능
dx_board_before_delete 삭제 처리 직전 연관 파일•댓글 정리에 사용
dx_board_after_delete 삭제 완료 직후 캐시 무효화, 통계 갱신에 사용
dx_board_after 모든 처리 완료 handler.php 맨 끝 로그, 정리 작업


5. 인증•커뮤니티 훅 실행 타이밍


5.1 로그인 관련 훅

// core/auth/Auth.php 내부 — login() 메서드
 
// DB에서 사용자 조회 → 비밀번호 검증 → 세션 저장 완료 후:
dx_run_hook('dx_after_login', array('user' => $user));
// ↑ 이 훅이 실행되는 시점에는 세션에 사용자 정보가 이미 저장되어 있음
// ↑ Auth::getInstance()->isLoggedIn() === true
 
// core/auth/Auth.php 내부 — logout() 메서드
// 세션에서 사용자 정보를 제거하기 전:
dx_run_hook('dx_after_logout', array('user' => $user));
// ↑ 이 훅 실행 시점에는 아직 세션이 살아있음
 
// core/auth/Auth.php 내부 — register() 메서드
// DB에 회원 INSERT 완료 후:
dx_run_hook('dx_after_register', array(
    'user_id' => $id,
    'data'    => $safeData,  // 저장된 회원 데이터
));


5.2 커뮤니티 활동 훅 타이밍

훅 이름 위치 파일 실행 시점
dx_after_comment core/api/comment.php DB에 댓글 INSERT 완료 직후 알림 발송, 포인트 지급에 활용
dx_after_like core/api/like.php 좋아요 DB 처리 완료 직후 게시물 소유자에게 알림 발송
dx_add_friend core/DxFriend.php 친구 관계 DB 저장 완료 직후
dx_after_point core/DxPoint.php 포인트 지급/차감 DB 저장 후 레벨업 체크 전
dx_levelup core/DxPoint.php 레벨업 조건 충족 확인 후 DB 레벨 업데이트 완료 직후
dx_shop_after_purchase core/DxShop.php 결제 완료 + 주문 DB 저장 후 디지털 상품 배포에 활용


6. 훅 등록과 실행의 순서 관계


6.1 등록 → 실행 타이밍 원칙

핵심 규칙
dx_add_hook()은 반드시 dx_run_hook() 호출 이전에 실행되어야 합니다.
STEP 4의 load_plugins()에서 plugin.php가 실행되므로, 플러그인의 dx_add_hook은 STEP 5보다 항상 앞에 있습니다.
예외: extend/top/ 파일에서 dx_add_hook을 등록하면, 해당 파일은 load_plugins() 이후에 실행되므로 테마 훅(dx_head 등)에 등록 가능합니다.


6.2 훅 등록 가능 위치 정리

등록 위치 실행 시점 등록 가능한 훅 범위
plugins/*/plugin.php STEP 4 — load_plugins() 모든 훅 (가장 권장)
extend/top/*.php STEP 4 완료 직후 테마 훅(dx_head, dx_top ...) 포함 모든 훅
extend/middle/*.php Dispatcher::dispatch() 내부 dx_middle, dx_bottom 등 렌더링 훅만
extend/bottom/*.php 렌더링 완료 후 훅 등록 불가 (이미 모든 훅 실행 완료)
data/config.php STEP 3 HookManager 미생성 → 훅 등록 불가


6.3 등록 타이밍 오류 예시

// ❌ 잘못된 예: extend/bottom에서 dx_top 훅 등록
// extend/bottom/bad_hook.php
dx_add_hook('dx_top', function($ctx) {
    echo '<div>이 코드는 절대 실행되지 않습니다</div>';
});
// → dx_top은 렌더링 중(STEP 5)에 이미 실행됨
// → extend/bottom은 렌더링 완료 후 실행됨
// → 등록 시점이 실행 시점보다 늦어 콜백이 무시됨
 
// ✅ 올바른 예: plugin.php에서 dx_top 훅 등록
// plugins/my-plugin/plugin.php
dx_add_hook('dx_top', function($ctx) {
    echo '<div>이 코드는 정상 실행됩니다</div>';
});
// → plugin.php는 STEP 4에서 실행됨
// → dx_top은 STEP 5 렌더링 중에 실행됨
// → 등록이 실행보다 먼저 완료됨 → 정상 작동


7. 전체 훅 실행 순서 다이어그램

아래는 HTTP 요청이 들어온 순간부터 응답이 완료될 때까지 모든 훅이 실행되는 순서를 나타낸 다이어그램입니다.
 
HTTP 요청 수신
  ↓
STEP 1: 클래스•함수 로드 (실행 없음)
functions.php → DxCache → Secure → Database →
HookManager → PluginRegistry → Auth → DxExtend → ...
  ↓
STEP 2: 보안 초기화 (세션, 보안 헤더, CSRF)
  ↓
STEP 3: DB 연결 + data/config.php 실행
  ↓
STEP 4: 초기화 + 훅 등록
load_plugins()  ← 플러그인 dx_add_hook 등록 완료
DxSite → DxTheme → Auth
CMS 내부 dx_add_hook 등록
★ extend/top/ 실행  ← 최초 extend 실행 지점
  ↓
STEP 5: 라우팅 + 디스패치

Router::resolve() → 라우트 확정
★ extend/middle/ 실행  ← dx_route 확정 직후

switch(type) → 핸들러 실행
├── 게시판: dx_board_before
│           dx_board_{action}_context
│           dx_board_before_save (저장 시)
│           dx_after_write / dx_board_after_save
│           dx_board_after
└── 기타: 페이지, 관리자, API 처리...

테마 layout/main.php 렌더링
├── <head> 안  → ★ dx_head
├── 헤더 아래  → ★ dx_top (+ dx_{type}_top)
├── <main> 안  → ★ dx_middle (+ dx_{type}_middle)
├── 푸터 아래  → ★ dx_bottom (+ dx_{type}_bottom)
├── 스크립트 후 → ★ dx_footer_scripts
└── 맨 마지막  → ★ dx_body_bottom (팝업 포함)

★ extend/bottom/ 실행  ← 렌더링 완료 후
  ↓
ob_end_flush() → HTTP 응답 전송 완료
(register_shutdown_function 콜백 실행)
★ visit_logs INSERT ← 응답 후 비동기 처리


8. 실전 타이밍 선택 가이드

목적에 맞는 훅을 빠르게 선택하기 위한 의사결정 가이드입니다.


8.1 목적별 훅 선택

하고 싶은 것 선택할 훅 이유
모든 페이지 <head>에 CSS 추가 dx_head HTML head 내부에서 실행됨
모든 페이지 상단에 배너 표시 dx_top 헤더 아래, 콘텐츠 위에서 실행됨
게시판 목록 상단에만 안내 추가 dx_board_top type=board일 때만 실행됨
notice 게시판에만 추가 UI dx_page_notice_top slug=notice일 때만 실행됨
페이지 하단 플로팅 버튼 dx_bottom 푸터 아래, 스크립트 앞에서 실행됨
외부 JS 라이브러리 추가 dx_footer_scripts 모든 CMS JS 로드 후 실행됨
팝업 / 전역 초기화 JS dx_body_bottom 맨 마지막 실행 (DOM 완성 후)
로그인 후 포인트 지급 dx_after_login 세션 저장 완료 후 실행됨
글 저장 전 내용 검사•수정 dx_board_before_save data 참조(&)로 전달, 수정 반영됨
글 저장 후 알림 발송 dx_after_write DB 저장 완료 후 실행됨
점검 모드 구현 extend/top/ 모든 초기화 완료 후 가장 먼저 실행
방문자 IP 로깅 extend/middle/ 라우트 확정 후, 응답 후 비동기 처리


8.2 타이밍 오류 체크리스트

  • 훅이 실행되지 않는다면: plugin.php에서 dx_add_hook을 등록했는지 확인 (config.php나 extend/bottom은 불가)
  • 레이아웃 훅(dx_head 등)이 실행되지 않는다면: API, 단독 페이지(is_standalone) 요청인지 확인 — 이 경우 main.php가 로드되지 않음
  • dx_board_before_save에서 데이터 수정이 반영 안 된다면: &$args['data'] 참조로 받아야 함
  • dx_after_login 훅이 실행 안 된다면: 소셜 로그인 콜백(_callback) 파일에서 Auth::login()이 호출되는지 확인
  • extend/middle에서 등록한 훅이 실행 안 된다면: 이미 지나간 훅에 등록한 것 — plugin.php로 이동 필요


최종 요약

훅 등록은 STEP 4 load_plugins()에서 완료되어야 합니다. plugin.php가 가장 안전합니다.
레이아웃 훅(dx_head~dx_body_bottom)은 테마 main.php 렌더링 중에 실행됩니다.
extend/top/은 모든 초기화 완료 후 라우팅 전, extend/middle/은 라우트 확정 후, extend/bottom/은 렌더링 완료 후입니다.
게시판 훅은 boards/handler.php 내에서, 인증 훅은 Auth.php 내에서 실행됩니다.
dx_body_bottom은 테마 렌더링의 맨 마지막이며, 팝업(priority 50)보다 작은 priority를 주면 팝업보다 먼저 실행됩니다.

 

댓글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일 이내
최신글
최신댓글
목록