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

컨트롤러 구조 • 데이터 전달 • 실행 방식 • 역할

A Administrator
2026.04.21 00:53(수정됨) 93 0

1. 개요 — DX 컨트롤러 구조의 설계 철학

DXCMS는 두 개의 컨트롤러 레이어를 병행 운영합니다. 하나는 파일 기반의 전통적인 핸들러 방식이고, 다른 하나는 v6.2.0에서 추가된 라라벨 스타일의 클래스 기반 컨트롤러입니다. 두 방식은 서로 대체 관계가 아니라 폴백(Fallback) 관계로 공존합니다.

핵심 원칙: DxRouter(클래스 기반)에 매칭된 라우트가 있으면 해당 컨트롤러를 실행하고, 없으면 Dispatcher(파일 기반) 방식으로 자동 폴백합니다. 기존 코드를 전혀 건드리지 않아도 됩니다.


1.1 두 가지 컨트롤러 방식 비교

구분 파일 기반 핸들러 클래스 기반 컨트롤러
도입 버전 CMS 최초 버전 v6.2.0
진입점 Dispatcher::dispatch() DxRouter::dispatch()
파일 위치 boards/handler.php, core/auth/*.php 등 controllers/{이름}.php
라우트 정의 URL 세그먼트 자동 파싱 routes/*.php에 명시적 선언
미들웨어 없음 (접근제어는 Dispatcher 내부) auth, admin, csrf, json, throttle
의존성 주입 없음 (싱글턴 직접 호출) DxContainer가 자동 주입
URL 파라미터 $_GET, $GLOBALS 직접 참조 {id}, {slug} 자동 추출 후 $params 전달
적합한 용도 게시판, 관리자, 인증 등 CMS 핵심 기능 커스텀 페이지, API, 플러그인 확장


2. 컨트롤러 실행 흐름 전체 구조

index.php에서 라우팅이 시작되는 순간부터 컨트롤러가 실행되고 렌더링이 완료될 때까지의 전체 흐름입니다.
 
HTTP 요청
    │
    ▼
index.php  [STEP 5]
    │
    ├── routes/*.php 자동 로드 (DxRouter에 라우트 등록)
    │
    ├── DxRouter::dispatch()  ← 클래스 기반 라우터 우선 시도
    │       │
    │       ├── URI + METHOD 매칭
    │       ├── {id}, {slug} 파라미터 추출 → $params[]
    │       ├── 미들웨어 실행 (auth, csrf, json ...)
    │       ├── DxContainer::call("Controller@method", $params)
    │       │       └── loadController() → require controllers/{클래스}.php
    │       │       └── build($class) → 생성자 의존성 자동 주입
    │       │       └── $controller->method($params)
    │       └── return true  ← 매칭 완료, 이하 실행 안 함
    │
    └── (매칭 없음) Dispatcher(Router)::dispatch()  ← 파일 기반 폴백
                │
                ├── extend/middle/ 실행
                ├── switch(라우트 타입)
                │       board  → boards/handler.php
                │       admin  → admin/index.php
                │       auth   → core/auth/{action}.php
                │       api    → core/api/{action}.php
                │       page   → pages/{slug}.php + layout
                │       home   → theme/page/home.php
                │       404    → theme/page/404.php
                └── 렌더링 완료


3. 클래스 기반 컨트롤러 — DxRouter + DxContainer


3.1 라우트 등록 — routes/ 폴더

index.php 실행 시 routes/ 폴더의 PHP 파일을 알파벳 순으로 자동 로드합니다. 이 파일 안에서 DxRouter의 정적 메서드로 라우트를 선언합니다.
 
// routes/web.php

// 기본 라우트 — GET
DxRouter::get('/mypage/dashboard', 'MemberController@dashboard')
         ->middleware('auth');

// POST + 다중 미들웨어
DxRouter::post('/api/member/update', 'MemberController@update')
         ->middleware(array('auth', 'csrf'));

// URL 파라미터 — {id} 자동 추출
DxRouter::get('/product/{id}', 'ShopController@show');

// 라우트 그룹 — prefix + 미들웨어 공통 적용
DxRouter::group(array('prefix' => '/shop', 'middleware' => 'auth'), function() {
    DxRouter::get('/cart',  'ShopController@cart');
    DxRouter::post('/order', 'ShopController@order')->middleware('csrf');
});

// 클로저 라우트 — 컨트롤러 없이 바로 처리
DxRouter::get('/health', function() {
    header('Content-Type: application/json');
    echo json_encode(array('status' => 'ok', 'version' => DX_VERSION));
    exit;
});

// REST 리소스 — index/show/store/update/destroy 자동 등록
DxRouter::resource('/posts', 'PostController');


3.2 지원하는 HTTP 메서드

DxRouter::get() GET 요청 전용
DxRouter::post() POST 요청 전용
DxRouter::put() PUT 요청 전용
DxRouter::patch() PATCH 요청 전용
DxRouter::delete() DELETE 요청 전용
DxRouter::any() GET + POST 동시 등록
DxRouter::resource() index/show/store/update/destroy 6개 라우트 자동 등록


3.3 URI 파라미터 추출

라우트 패턴에 {변수명}을 사용하면 실제 요청 URI에서 값을 자동 추출합니다. 추출된 파라미터는 연관 배열($params)로 컨트롤러 메서드에 전달됩니다.
 
// 라우트 선언
DxRouter::get('/product/{id}',      'ShopController@show');
DxRouter::get('/board/{slug}/{num}', 'BoardController@view');

// 실제 요청: GET /product/42
// 컨트롤러에 전달: $params = array('id' => '42')

// 실제 요청: GET /board/notice/5
// 컨트롤러에 전달: $params = array('slug' => 'notice', 'num' => '5')


3.4 미들웨어

라우트에 체이닝 방식으로 미들웨어를 적용합니다. 미들웨어는 컨트롤러 실행 전에 순서대로 실행됩니다.
 
미들웨어 처리 조건 처리 내용
auth 비로그인 요청 로그인 페이지로 리다이렉트 (redirect 파라미터 포함)
admin 비관리자 요청 403 Forbidden 응답
guest 로그인 상태 요청 홈으로 리다이렉트
csrf CSRF 토큰 불일치 403 + JSON 에러 응답
json 모든 요청 Content-Type: application/json 헤더 자동 설정
throttle 향후 확장 분당 요청 횟수 제한 (구현 예정)
커스텀 클로저 또는 Class@handle DxContainer를 통해 실행


4. 컨트롤러 클래스 구조와 역할


4.1 컨트롤러 파일 위치

일반 컨트롤러 controllers/{클래스명}.php
소문자 파일명 controllers/{소문자}.php  (Controller 접미사 제거)
코어 컨트롤러 core/controllers/{클래스명}.php
플러그인 컨트롤러 plugins/{플러그인명}/controllers/{클래스명}.php

컨트롤러 자동 로드: DxContainer::loadController()가 위 경로를 순서대로 탐색합니다. 직접 require를 작성할 필요가 없습니다.


4.2 컨트롤러 클래스 기본 구조

컨트롤러는 일반 PHP 클래스입니다. 특별한 부모 클래스를 상속할 필요가 없으며, 각 퍼블릭 메서드가 하나의 라우트 액션에 대응합니다.
 
<?php
// controllers/MemberController.php
if (!defined('DX_CMS')) exit('Direct access not allowed.');

class MemberController
{
    // 생성자 — DxContainer가 의존성을 자동 주입
    public function __construct()
    {
        // 초기화 (필요한 경우)
    }

    /**
     * GET /mypage/dashboard
     * middleware: auth
     *
     * @param array $params  URL 파라미터 (이 라우트는 없음)
     */
    public function dashboard(array $params = array())
    {
        // 1. 서비스 인스턴스 획득
        $auth = Auth::getInstance();
        $user = $auth->user();

        // 2. 데이터 조회
        $recentPosts = dx_db('posts')
            ->where('member_id', $user['id'])
            ->where('status', 1)
            ->orderBy('id', 'desc')
            ->limit(5)
            ->get();

        // 3. 뷰 렌더링
        ob_start();
        extract(compact('user', 'recentPosts'));
        include DX_ROOT . '/pages/mypage/dashboard.php';
        $dx_content = ob_get_clean();

        // 4. 레이아웃 적용
        $layoutFile = DxTheme::getInstance()->resolve('layout/main.php');
        if ($layoutFile) include $layoutFile;
        else echo $dx_content;
        exit;
    }

    /**
     * GET /product/{id}
     *
     * @param array $params  array('id' => '42')
     */
    public function show(array $params = array())
    {
        $id = isset($params['id']) ? (int)$params['id'] : 0;
        if (!$id) { http_response_code(404); exit; }

        $product = dx_db('products')
            ->where('id', $id)
            ->where('status', 1)
            ->first();

        if (!$product) { http_response_code(404); exit; }

        ob_start();
        extract(compact('product'));
        include DX_ROOT . '/pages/shop/product.php';
        $dx_content = ob_get_clean();

        $layoutFile = DxTheme::getInstance()->resolve('layout/main.php');
        if ($layoutFile) include $layoutFile;
        else echo $dx_content;
        exit;
    }
}


5. DxContainer — 의존성 주입(DI) 컨테이너

DxContainer는 라라벨의 서비스 컨테이너와 동일한 철학으로 설계된 경량 DI 컨테이너입니다. 기존의 싱글턴 패턴(getInstance())을 완전히 대체하는 것이 아니라, 그 위에 포장하는 방식으로 100% 하위 호환성을 유지합니다.


5.1 핵심 등록 방식

bind() 팩토리 바인딩. make() 호출마다 새 인스턴스 생성
singleton() 싱글턴 바인딩. 첫 make() 이후 동일 인스턴스 반환
instance() 이미 생성된 인스턴스를 직접 등록 (항상 싱글턴)
alias() 별칭 등록. dx_app()->make('db')로 Database 반환


5.2 기본 등록된 서비스 (registerCoreServices)

CMS 초기화 완료 후 자동으로 핵심 서비스들이 컨테이너에 등록됩니다.
 
'db' / 'database' Database::getInstance() — PDO 래퍼
'auth' Auth::getInstance() — 세션 기반 인증
'secure' Secure::getInstance() — 보안 전담
'cache' 'DxCache' (클래스명 문자열) — 파일/APCu 캐시
'hook' / 'hooks' HookManager::getInstance() — 훅 시스템
'seo' 'DxSeo' (클래스명 문자열) — SEO 헬퍼
'site' DxSite::getInstance() — 멀티사이트
'theme' DxTheme::getInstance() — 테마 엔진


5.3 사용 예시 — 플러그인에서 서비스 등록

// plugins/my-plugin/plugin.php

// 팩토리 바인딩 (매번 새 인스턴스)
dx_app()->bind('mailer', function() {
    return new MyMailer(dx_config('smtp_host'));
});

// 싱글턴 바인딩
dx_app()->singleton('sms', function() {
    return new AlimtalkSMS(dx_config('alimtalk_key'));
});

// 컨트롤러에서 꺼내 쓰기
$mailer = dx_app()->make('mailer');  // 또는 dx_make('mailer')
$mailer->send('to@example.com', '제목', '내용');


5.4 컨트롤러 자동 로드 및 실행 흐름

DxContainer::call("클래스@메서드", $params) 호출 시 내부에서 다음 순서로 처리합니다.
 
// DxContainer::call() 내부 처리 순서

// 1. 클래스 존재 확인
if (!class_exists($class)) {
    $this->loadController($class);  // 파일 자동 탐색
}

// 2. loadController() 탐색 순서
// controllers/MemberController.php
// controllers/member.php
// core/controllers/MemberController.php
// plugins/*/controllers/MemberController.php

// 3. 인스턴스 생성 (build)
// ReflectionClass로 생성자 파라미터 분석
// 타입힌트 있으면 컨테이너에서 자동 주입
// 옵셔널 파라미터는 기본값 사용

// 4. 메서드 실행
call_user_func_array(array($controller, $method), $params);


6. 파일 기반 핸들러 — Dispatcher

DxRouter에 매칭된 라우트가 없을 때 실행되는 전통적인 컨트롤러 방식입니다. URL 세그먼트를 분석하여 적절한 핸들러 파일을 자동으로 찾아 실행합니다.


6.1 라우트 타입별 핸들러 파일

타입 핸들러 파일 역할 및 특이사항
home themes/{테마}/page/home.php → pages/home.php 우선순위 폴백 체인으로 홈 렌더링
board boards/handler.php $GLOBALS에 board, action, id 주입 후 실행
admin admin/index.php 관리자 권한 확인 후 실행. $GLOBALS에 action, sub 주입
auth core/auth/{action}.php 소셜 콜백은 ob_start 버퍼 초기화 후 독립 실행
api core/api/{action}.php JSON Content-Type 자동 설정 (일부 제외)
page pages/{slug}.php + layout/main.php 에러 격리 모드로 실행. standalone 모드 지원
search core/search/handler.php 통합 검색 처리
404 themes/{테마}/page/404.php HTTP 404 코드 설정 후 렌더링


6.2 데이터 전달 방식 — $GLOBALS 주입

파일 기반 핸들러에서는 라우트 정보를 $GLOBALS를 통해 핸들러 파일에 전달합니다.
 
// Dispatcher::dispatchBoard() 내부
$GLOBALS['dx_board']      = $board;       // 게시판 정보 (DB row)
$GLOBALS['dx_action']     = $action;      // 액션 (list/view/write 등)
$GLOBALS['dx_board_skin'] = $boardSkin;   // 스킨명

// boards/handler.php에서 수신
$board     = $GLOBALS['dx_board'];
$action    = $GLOBALS['dx_action'];
$boardSkin = $GLOBALS['dx_board_skin'];

// 관리자 핸들러
$GLOBALS['dx_admin_action'] = $action;    // 관리 메뉴 (dashboard, boards 등)
$GLOBALS['dx_admin_sub']    = $sub;       // 서브 메뉴


6.3 게시판 핸들러의 스킨 폴백 체인

boards/handler.php는 다음 우선순위로 뷰 파일을 탐색합니다.
 
// 스킨 독립 핸들러가 있으면 완전 위임
// 1. boards/skins/{스킨}/{액션}/handler.php

// 없으면 표준 뷰 탐색 (_brd_render 함수)
// 2. boards/skins/{스킨}/{액션}.php
// 3. themes/{테마}/board/{스킨}/{액션}.php
// 4. themes/{테마}/board/{액션}.php
// 5. themes/default/board/{스킨}/{액션}.php
// 6. themes/default/board/{액션}.php  ← 최종 폴백


7. 데이터 전달 구조 상세

DXCMS에서 컨트롤러(핸들러)와 뷰 파일 사이에 데이터를 전달하는 방법은 세 가지가 있습니다. 각 방식은 사용 맥락에 따라 선택합니다.


7.1 방식 1 — extract()를 이용한 로컬 변수 주입

가장 일반적인 방식입니다. Dispatcher의 renderWithLayout()과 renderPageWithLayout()이 이 방식을 사용합니다.
 
// 핸들러(컨트롤러)에서
$user      = Auth::getInstance()->user();
$posts     = dx_db('posts')->where('status', 1)->get();
$context   = array('user' => $user, 'posts' => $posts);

// Dispatcher::renderWithLayout() 내부
ob_start();
extract($context);          // $user, $posts 변수로 풀림
require $contentFile;       // 뷰 파일 실행
$dx_content = ob_get_clean();

extract($context);
require $layoutFile;        // layout/main.php에서 $dx_content, $user 사용 가능


7.2 방식 2 — $GLOBALS를 통한 전역 공유

Dispatcher가 핸들러 파일에 데이터를 전달할 때, 또는 핸들러 내부에서 뷰 파일이 참조할 수 있도록 공유 데이터를 등록할 때 사용합니다.
 
// Dispatcher가 주입
$GLOBALS['dx_route']         = $route;   // 현재 라우트 정보
$GLOBALS['dx_board']         = $board;   // 게시판 row
$GLOBALS['dx_action']        = $action;  // 현재 액션
$GLOBALS['dx_board_skin']    = $skin;    // 스킨명
$GLOBALS['dx_handler_context'] = array(...); // 스킨 독립 핸들러 컨텍스트

// 뷰 파일에서 참조
$board = $GLOBALS['dx_board'];
$route = $GLOBALS['dx_route'];


7.3 방식 3 — ob_start/ob_get_clean 버퍼링

컨트롤러에서 뷰 파일의 출력을 캡처하여 $dx_content 변수에 저장하고, 레이아웃 파일(layout/main.php)에서 이를 출력하는 패턴입니다. DXCMS의 모든 페이지 렌더링이 이 구조를 따릅니다.
 
// 컨트롤러(클래스 기반)
ob_start();
extract(compact('user', 'posts'));
include DX_ROOT . '/pages/mypage.php';  // 뷰 파일 실행 → 버퍼에 쌓임
$dx_content = ob_get_clean();            // 버퍼 내용을 변수로 획득

// 레이아웃 파일 실행
$layoutFile = DxTheme::getInstance()->resolve('layout/main.php');
if ($layoutFile) {
    include $layoutFile;  // layout/main.php 안에서 echo $dx_content; 호출
}
exit;

// layout/main.php 구조
<!DOCTYPE html>
<html>
<head>...</head>
<body>
    <?php include 'header.php'; ?>
    <main>
        <?php echo $dx_content; ?>  // 뷰 파일 출력 결과
    </main>
    <?php include 'footer.php'; ?>
    <?php dx_do_hook('dx_body_bottom'); ?>  // 팝업, 스크립트 등
</body>
</html>


7.4 QueryBuilder — 데이터 조회 인터페이스

컨트롤러에서 DB 데이터를 조회할 때는 dx_db() 헬퍼 함수 또는 Database::getInstance()->table()을 통해 QueryBuilder를 사용합니다.
 
// dx_db() 헬퍼 — QueryBuilder 즉시 반환
$posts = dx_db('posts')
    ->select(array('id', 'title', 'created_at'))
    ->where('status', 1)
    ->where('member_id', '>=', 1)
    ->orderBy('id', 'desc')
    ->limit(10)
    ->get();           // array[] 반환

$post = dx_db('posts')
    ->where('id', $id)
    ->first();         // 단일 row 반환 (없으면 null)

// JOIN
$result = dx_db('posts')
    ->leftJoin('members', 'posts.member_id', '=', 'members.id')
    ->select(array('posts.*', 'members.nickname'))
    ->where('posts.status', 1)
    ->get();

// 페이지네이션
$result = dx_db('posts')
    ->where('status', 1)
    ->paginate(20);    // array('data' => [], 'total' => N, 'per_page' => 20, ...)


8. extend/middle/ — 핸들러 실행 전 미들웨어 포인트

파일 기반 라우팅에서는 DxRouter의 미들웨어 대신 extend/middle/ 폴더를 통해 모든 요청에 대한 공통 처리를 수행합니다. 라우트 타입이 확정된 직후, 실제 핸들러가 실행되기 직전에 실행됩니다.

extend/middle/은 파일 기반 핸들러와 클래스 기반 컨트롤러 모두에 적용됩니다. DxRouter::dispatch()가 true를 반환하면 Dispatcher::dispatch()는 실행되지 않으므로, extend/middle/은 Dispatcher 내에서만 실행됩니다.


8.1 사용 예시

// extend/middle/01_visit_tracker.php
// $context 변수 사용 가능: type, route

$type  = isset($type)  ? $type  : '';
$route = isset($route) ? $route : array();

// 게시판 페이지 방문만 통계 기록
if ($type === 'board') {
    $boardKey = isset($route['slug']) ? $route['slug'] : '';
    // 방문자 로그 기록 ...
}

// 점검 모드 (extend/top/에 넣어도 됨)
// if (dx_config('maintenance')) {
//     http_response_code(503);
//     exit('점검 중입니다.');
// }


9. 훅 시스템 — HookManager

컨트롤러와 뷰 사이, 또는 플러그인과 CMS 코어 사이의 확장 포인트는 훅(Hook) 시스템을 통해 제공됩니다. WordPress의 add_action/do_action과 동일한 개념입니다.


9.1 주요 훅 목록

dx_after_login 로그인 완료 직후. $args['user'] = 사용자 row
dx_after_logout 로그아웃 직후. $args['user'] = 로그아웃된 사용자 row
dx_body_bottom 레이아웃 </body> 직전. 팝업, 스크립트 주입
dx_board_before 게시판 핸들러 진입 직후. board, action, skin, id
dx_extend_top extend/top/ 실행 완료 후
dx_extend_middle extend/middle/ 실행 완료 후
dx_extend_bottom extend/bottom/ 실행 완료 후


9.2 훅 등록 및 실행

// 훅 등록 (plugins/my-plugin/plugin.php 등에서)
dx_add_hook('dx_body_bottom', function() {
    echo '<script>console.log("loaded");</script>';
}, 20);  // 20 = 우선순위 (낮을수록 먼저 실행)

// 훅 실행 (레이아웃 파일에서)
dx_do_hook('dx_body_bottom');

// 데이터 전달이 필요한 경우
dx_add_hook('dx_after_login', function($args) {
    $user = $args['user'];
    // 로그인 이력 기록 ...
}, 10);

dx_run_hook('dx_after_login', array('user' => $user));


10. 컨트롤러의 역할 — 무엇을 해야 하고, 무엇을 하면 안 되는가


10.1 컨트롤러가 해야 하는 일

  • 요청 파라미터 수신 및 유효성 검사 ($params[], $_GET, $_POST)
  • Auth::getInstance()로 인증/권한 확인
  • QueryBuilder / Database로 데이터 조회 및 조작
  • 비즈니스 로직 실행 (또는 서비스 클래스에 위임)
  • 뷰 파일에 전달할 데이터 준비 (compact, extract)
  • ob_start → include 뷰 → ob_get_clean → include 레이아웃 패턴으로 렌더링
  • JSON API라면 header(Content-Type) 설정 후 json_encode 출력
  • 리다이렉트: dx_redirect() 또는 header(Location:)


10.2 컨트롤러가 하면 안 되는 일

  • HTML 마크업을 컨트롤러 파일 안에 직접 출력 — 반드시 뷰 파일로 분리
  • 복잡한 비즈니스 로직을 컨트롤러에 모두 구현 — 서비스 클래스로 분리 권장
  • 세션을 직접 조작 — Auth 클래스를 통해 처리
  • 보안 헤더나 CSRF 검증을 직접 구현 — Secure 클래스 또는 미들웨어 사용
  • exit 없이 컨트롤러 종료 — 렌더링 후 반드시 exit 또는 return으로 종료

권장 패턴: 컨트롤러는 얇게(thin controller) 유지하고, 복잡한 로직은 별도의 서비스 클래스나 헬퍼 함수로 분리합니다. 컨트롤러는 요청 수신 → 데이터 준비 → 뷰 렌더링 세 가지만 담당하는 것이 이상적입니다.


10.3 컨트롤러 유형별 역할 요약

컨트롤러 유형 대표 파일 핵심 역할
게시판 핸들러 boards/handler.php 게시글 CRUD, 댓글, 좋아요, 파일 다운로드. 스킨 폴백 체인으로 뷰 결정
관리자 핸들러 admin/index.php 관리자 권한 확인, 설정 저장, 회원/게시판/플러그인 관리
인증 핸들러 core/auth/*.php 로그인/로그아웃/회원가입/마이페이지/소셜 로그인
API 핸들러 core/api/*.php JSON 응답 전용. 댓글 CRUD, 파일 업로드, 좋아요, 알림 등
페이지 핸들러 Dispatcher::dispatchPage() 접근 권한 확인, 뷰 파일 실행, 에러 격리
클래스 컨트롤러 controllers/*.php 커스텀 기능. DI 컨테이너 활용, 미들웨어 지원


11. 에러 격리 — 컨트롤러의 방어 설계

DXCMS는 "막코딩 완전 지원"을 설계 목표 중 하나로 삼고 있습니다. 뷰 파일이나 페이지 파일에서 PHP 오류가 발생해도 레이아웃이 깨지지 않고 CMS 전체가 계속 동작하도록 에러 격리 메커니즘을 적용합니다.

11.1 에러 격리 동작 원리

// Dispatcher::renderPageWithLayout() 내부 에러 격리

$pageErrors = array();

// 에러 핸들러 등록 — 뷰 파일의 notice/warning을 로그에만 기록
set_error_handler(function($errno, $errstr, $errfile, $errline) use (&$pageErrors) {
    dx_log('[Page] ' . $errstr, 'error');  // data/error.log에 기록
    if (defined('DX_DEBUG') && DX_DEBUG) {
        $pageErrors[] = '[' . $level . '] ' . $errstr;
    }
    return true;  // PHP 기본 에러 핸들러 실행 방지
});

ob_start();
try {
    extract($context, EXTR_SKIP);
    include $contentFile;           // 뷰 파일 실행
} catch (Exception $e) {
    // Exception은 에러 박스로 표시
    echo '<div class="dx-error">페이지 오류: ' . htmlspecialchars($e->getMessage()) . '</div>';
}
$dx_content = ob_get_clean();
restore_error_handler();

// 레이아웃은 정상 출력
require $layoutFile;

extend/middle/, extend/top/, extend/bottom/ 모두 동일한 에러 격리(set_error_handler + try-catch)를 적용합니다. 어떤 파일에서 오류가 발생해도 다른 파일의 실행은 계속됩니다.


12. 전체 구조 요약

+---------------------------------------------------------+
|                    HTTP 요청                            |
+---------------------------------+-----------------------+
                                  |
                                  v
+---------------------------------------------------------+
|  index.php  [STEP 5]  라우팅 시작                       |
|                                                         |
|  routes/*.php 자동 로드 → DxRouter 라우트 등록         |
+---------------------------------+-----------------------+
                                  |
          +-----------------------+--------------------+
          v                                            v
  DxRouter::dispatch()                    Dispatcher::dispatch()
  (클래스 기반, 우선)                      (파일 기반, 폴백)
          |                                            |
  URI + METHOD 매칭                       Router::resolve()
  파라미터 추출 $params                   라우트 타입 결정
  미들웨어 실행                            extend/middle 실행
  DxContainer::call()                     switch(type)
          |                                  |
  loadController()                    핸들러 파일 require
  build() — 의존성 주입               boards/handler.php 등
  $controller->method($params)               |
          |                                  |
          +--------------+-------------------+
                         |
                    데이터 조회
              (QueryBuilder / Database)
                         |
                    뷰 파일 실행
               ob_start → include → ob_get_clean
                         |
                  $dx_content 생성
                         |
              layout/main.php 실행
          (echo $dx_content + 헤더/푸터)
                         |
                  dx_body_bottom 훅
               (팝업, 스크립트 등)
                         |
+------------------------v--------------------------------+
|                  HTTP 응답 전송                         |
+---------------------------------------------------------+
 

댓글0

로그인 후 댓글을 작성할 수 있습니다.
3.5 컨트롤러 구조 컨트롤러 구조 • 데이터 전달 • 실행 방식 • 역할 2026.04.21
30
전체 회원
269
전체 게시글
144
전체 댓글
181
오늘 방문
28,530
전체 방문
1
현재 접속
인기글 7일 이내
최신글
최신댓글
목록