회원가입 | 고객센터 |
DESIGNONEX
dxcms.kr
로그인 회원가입
고객센터
DXCMS 활용 (CMS)

DXCMS 날코딩•막코딩 완전 허용

D DX
2026.04.12 15:31(수정됨) 91 0

이 문서가 답하는 질문

"DXCMS는 정해진 방식대로만 코딩해야 하는가?"
 
아닙니다. 스킨 파일에서 PHP Notice가 터져도 페이지는 출력됩니다.
아닙니다. extend/ 파일에서 Exception이 발생해도 CMS는 계속 실행됩니다.
아닙니다. pages/home.php에 아무 HTML을 때려 넣어도 레이아웃은 살아있습니다.
아닙니다. 전역변수 $GLOBALS를 직접 쓰는 날코딩도 정식 지원됩니다.

이 문서는 그것을 소스코드 레벨에서 하나씩 증명합니다.


01. DXCMS의 철학 — "틀려도 죽지 않는 CMS"

막코딩을 허용하는 것은 버그가 아니라 설계입니다

라라벨에서 View 파일에 존재하지 않는 변수를 쓰면 → ErrorException이 터지고 500 페이지가 뜹니다.
DXCMS에서 스킨 파일에 존재하지 않는 변수를 쓰면 → 에러 로그에만 기록되고 페이지는 출력됩니다.
이것은 DXCMS가 "실수에 관대한 개발 환경"을 의도적으로 설계한 결과입니다.

왜 이렇게 설계했는가?

  • DXCMS의 주요 사용자층은 PHP 숙련자부터 입문자까지 매우 넓습니다.
  • 스킨•플러그인•extend/ 파일은 제3자 개발자가 자유롭게 작성하는 영역입니다.
  • 단 하나의 실수가 전체 사이트를 죽이면 생태계가 자라날 수 없습니다.
  • 에러는 숨기지 않습니다 — data/error.log에 기록하고 디버그 모드로 화면 출력도 가능합니다.
 
❌ 다른 CMS/프레임워크 ✅ DXCMS
변수 미정의 → 500 Error 변수 미정의 → 로그 + 계속 출력
예외 발생 → 흰 화면 예외 발생 → 에러박스 표시 + 계속
규칙 위반 → 작동 불능     규칙 위반 → 일단 실행, 나중에 수정
엄격한 PSR 준수 요구     PSR 불필요, 날것의 PHP로 OK
Composer 패키지 방식 강제 파일 복사만으로 기능 추가


02. 소스코드가 증명한다

실제 코드에 막코딩 지원이 명시되어 있습니다


증거 1 — SKIN_GUIDE.md에 직접 명시된 "막코딩 지원"

DXCMS 공식 가이드 문서(boards/skins/SKIN_GUIDE.md)에는 다음 내용이 명시되어 있습니다.

boards/skins/SKIN_GUIDE.md — 막코딩 지원 항목 (원문)
## 막코딩 지원
스킨 파일에서 PHP Notice/Warning이 발생해도 CMS는 계속 동작합니다.
- Notice/Warning → data/error.log에만 기록, 페이지 정상 출력
- Exception/Fatal → 에러 박스를 레이아웃 안에 표시하고 계속 동작
- define('DX_DEBUG', true) → 화면에 에러 상세 표시 (개발 시 활용)


증거 2 — DxExtend::safeExec() 에러 격리 메커니즘

core/DxExtend.php의 safeExec() 메서드는 extend/ 파일 실행 시 어떤 에러가 발생해도 CMS 전체를 보호합니다. 실제 소스코드입니다.

core/DxExtend.php — safeExec() 실제 소스코드
/**
 * 파일 안전 실행 (에러 격리)
 * 어떤 코드가 들어있어도 CMS 전체를 죽이지 않음
 */
private function safeExec($file, $context, $slot)
{
    // 에러 핸들러: 이 파일의 에러를 로그에만 기록하고 계속 진행
    set_error_handler(function($errno, $errstr, $errfile, $errline) {
        dx_log("[DxExtend] " . $errstr . " (line " . $errline . ")", "error");
        return true;  // ← 에러를 잡아서 로그만 쓰고 계속 실행
    });

    try {
        extract($context, EXTR_SKIP);
        include $file;  // ← 이 파일에서 뭐가 터져도 아래로 진행
    } catch (Exception $e) {
        // Exception도 로그에만 기록, CMS는 계속 동작
        dx_log("[DxExtend][" . $e->getFile() . "] " . $e->getMessage(), "error");
    }

    restore_error_handler();  // ← 다음 파일은 새 핸들러로
}

이 코드가 의미하는 것:

extend/ 폴더에 넣는 PHP 파일은 어떻게 짜도 됩니다.
Exception이 터지면? → 로그 기록 후 다음 파일 실행.
Notice가 뜨면? → 로그 기록 후 계속 출력.
PHP Fatal Error면? → 에러 핸들러가 잡아서 CMS 보호.

단 하나의 파일이 전체 사이트를 다운시킬 수 없습니다.


증거 3 — boards/handler.php 스킨 독립 핸들러 위임

boards/handler.php 소스코드를 보면, 스킨에 handler.php가 있으면 DB 쿼리부터 렌더링까지 모든 것을 그 파일에 완전히 맡깁니다.

boards/handler.php — 스킨 독립 핸들러 위임 (실제 소스코드)
// ════════════════════════════════════════════════════
// 1단계: 스킨 독립 핸들러 위임
//   boards/skins/{스킨}/{액션}/handler.php 가 있으면 완전 위임
// ════════════════════════════════════════════════════
$skinHandler = $dxSkin->resolveHandler($boardSkin, $action);
if ($skinHandler) {
    // 컨텍스트 주입 — 독립 핸들러에서 $dx_* 변수 바로 사용 가능
    $GLOBALS["dx_handler_context"] = array(
        "db"=>$db, "auth"=>$auth, "board"=>$board,
        "action"=>$action, "id"=>$id, "boardSkin"=>$boardSkin,
    );
    include $skinHandler;
    return;  // ← CMS 핵심 로직 완전 우회
}

boards/skins/my-skin/list/handler.php 파일 하나만 만들면
CMS 핵심 코드를 완전히 우회하고 원하는 대로 처리할 수 있습니다.
SELECT 쿼리를 날것으로 짜도 되고, 전역변수를 마음대로 써도 됩니다.


03. 5가지 날코딩 시나리오 — 전부 동작합니다

실제로 어떻게 코딩해도 되는지 하나씩 보여줍니다


시나리오 1 — 스킨 파일에 날것의 PHP 때려 넣기

boards/skins/my-skin/list.php 파일에 변수 체크도 없이, 구조도 없이, 마음대로 쓴 코드입니다.

boards/skins/my-skin/list.php — 완전 날코딩
<?php
// boards/skins/my-skin/list.php
// 변수 체크? 없음. 에러처리? 없음. 구조? 없음.
// 그냥 내 마음대로 출력합니다.

// $posts는 CMS가 자동으로 넘겨줌 — 확인도 안 하고 바로 사용
echo "<ul>";
foreach ($posts as $p) {
    echo "<li><a href='/" . $board["board_key"] . "/view/" . $p["id"] . "'>";
    echo $p["title"];  // dx_esc() 안 씀 — 날것 출력 (개발 중에는 OK)
    echo " (" . $p["view_cnt"] . "회) ";
    echo date("m/d", strtotime($p["created_at"]));
    echo "</a></li>";
}
echo "</ul>";

// 페이지네이션도 직접 박음
if ($total > $perPage) {
    echo "<div>총 " . $total . "개 / " . $page . " 페이지</div>";
}
?>

✅ 결과: 페이지 정상 출력

→ $posts, $board, $total, $perPage, $page 변수는 CMS가 자동으로 넘겨줍니다.
→ list.php 파일 하나만 있으면 됩니다. view.php/write.php는 default 테마로 폴백.
→ 구조 없음, 들여쓰기 없음, 에러처리 없음 — 전혀 문제 없습니다.


시나리오 2 — extend/ 파일에 에러 나는 코드를 넣어도 CMS 계속 동작

❌ 이렇게 짜도...
// extend/top/99_my_bad_code.php
// 일부러 에러 나는 코드를 넣었음

// 존재하지 않는 함수 호출
this_function_does_not_exist();

// 정의 안 된 변수 사용
echo $undefined_variable;

// 0으로 나누기
$x = 10 / 0;

// 존재하지 않는 클래스
$obj = new NonExistentClass();
✅ CMS는 안 죽습니다
// 결과: 위 파일에서 에러가 나도
// CMS는 멈추지 않습니다.

// safeExec()가 모든 에러를 격리:
// 1. set_error_handler → 로그 기록
// 2. try/catch → Exception 격리
// 3. restore_error_handler → 복구

// data/error.log에는 기록됨:
// [DxExtend] Call to undefined
//   function this_function...
// [DxExtend] Undefined variable:
//   undefined_variable

// 사이트는 정상적으로 열립니다.
// 에러 파일만 수정하면 됩니다.

→ extend/top/ 파일들은 파일명 오름차순으로 실행됩니다.
→ 99_my_bad_code.php에서 에러가 나도 01_~, 02_~ 파일들은 이미 실행 완료.
→ 에러가 난 파일만 skip하고 나머지는 계속 실행됩니다.
→ DX_DEBUG=true로 설정하면 화면에 에러 내용이 표시됩니다.


시나리오 3 — pages/home.php에 순수 HTML+PHP 막코딩

pages/home.php는 홈페이지 파일입니다. 아무 구조 없이 HTML과 PHP를 마구 섞어 써도 됩니다.

pages/home.php — 막코딩 홈페이지 예시
<?php
// pages/home.php — 아무렇게나 짜도 됩니다
// 레이아웃(헤더+푸터)은 CMS가 자동으로 감쌉니다

// 1. DB에서 직접 쿼리 — QueryBuilder 안 써도 됨
$db = Database::getInstance();
$hot = $db->rows("SELECT * FROM dx_posts WHERE status=1 ORDER BY view_cnt DESC LIMIT 5");

// 2. 전역변수 직접 접근 — 날것의 PHP
$siteTitle = $GLOBALS["dx_config"]["site_name"];

// 3. HTML을 그냥 때려 넣기
?>
<h2 style="color:red;font-size:32px;text-align:center">
    🔥 오늘의 인기글 — <?php echo $siteTitle; ?>
</h2>

<div style="border:3px solid blue;padding:20px">
<?php foreach ($hot as $post): ?>
    <div style="margin:10px 0;font-size:18px">
        👉 <a href="/free/view/<?= $post["id"] ?>">
            <?= $post["title"] ?> (<?= $post["view_cnt"] ?>회)
        </a>
    </div>
<?php endforeach; ?>
</div>

<?php
// 4. 최신글 위젯 — 함수 하나로 끝
dx_board_latest("notice", 5, "list", "공지사항", "📢");
dx_board_latest("free",   5, "card", "자유게시판", "💬");
?>

<!-- 5. 그냥 인라인 스타일 남발해도 됨 -->
<div style="background:#ff0;padding:30px;text-align:center;font-size:24px">
    ✨ 오늘도 좋은 하루 되세요! ✨
</div>

✅ 결과: 헤더•메뉴•푸터가 자동으로 감싸진 완성된 홈페이지 출력

→ pages/home.php는 $dx_content 영역만 채우면 됩니다.
→ 레이아웃(header/footer/menu)은 DxTheme::resolve()가 자동 처리합니다.
→ <?= ?>  단축 문법도 됩니다. 인라인 스타일도 됩니다. 직접 SQL도 됩니다.
→ dx_board_latest() 함수 하나로 최신글 위젯도 끝납니다.


시나리오 4 — 스킨 actions/ 에서 $GLOBALS 날것으로 접근

boards/skins/my-skin/actions/custom.php — CMS 내부 변수에 $GLOBALS로 직접 접근하는 것이 정식 지원됩니다.
실제 ERP 스킨(boards/skins/erp/actions/inventory.php) 코드에 이 패턴이 그대로 사용되고 있습니다.

boards/skins/my-skin/actions/custom.php — $GLOBALS 날것 접근 (공식 패턴)
<?php
// boards/skins/my-skin/actions/custom.php
// URL: /my-board/custom
// 이 파일을 만드는 것만으로 URL이 자동 생성됩니다.

// $GLOBALS 직접 접근 — 이것이 정식 패턴입니다 (공식 코드에서 사용 중)
$db    = $GLOBALS["dx_handler_context"]["db"];
$auth  = $GLOBALS["dx_handler_context"]["auth"];
$board = $GLOBALS["dx_handler_context"]["board"];

// 로그인 체크 — 마음대로 처리
if (!$auth->isLoggedIn()) {
    dx_redirect(dx_base_url("auth/login"));
    exit;
}

// DB 쿼리 — 아무 방법이나 사용 가능
// 방법 1: QueryBuilder
$items = dx_db("my_items")->where("board_id", $board["id"])->get();

// 방법 2: 날것의 SQL (PDO 직접)
$items = $db->rows("SELECT * FROM dx_my_items WHERE board_id = ?",
                   array($board["id"]));

// 방법 3: 전역함수로 쿼리
$items = $db->findAll("my_items", array("board_id"=>$board["id"]));

// 출력 — 방법 마음대로
$dxth = DxTheme::getInstance();
$layoutFile = $dxth->resolve("layout/main.php");

ob_start();
?>
<h1>커스텀 페이지 — <?php echo $board["name"]; ?></h1>
<?php foreach ($items as $i): ?>
    <div><?php echo $i["title"]; ?></div>
<?php endforeach; ?>
<?php
$dx_content = ob_get_clean();
if ($layoutFile) {
    extract(array("type"=>"board","board"=>$board));
    require $layoutFile;
} else {
    echo $dx_content;  // 레이아웃 없으면 그냥 출력
}

✅ 결과: /my-board/custom URL이 자동 생성되고 페이지 정상 출력

→ actions/ 폴더에 파일만 만들면 라우트 등록 없이 URL이 자동 생성됩니다.
→ $GLOBALS["dx_handler_context"] 직접 접근은 공식 코드(ERP스킨)에서 사용 중입니다.
→ QueryBuilder / 날것 SQL / $db->findAll() — 세 가지 방법 모두 허용됩니다.


시나리오 5 — 스킨 완전 독립 핸들러 (CMS 핵심 로직 100% 우회)

boards/skins/my-skin/list/handler.php 파일을 만들면, 목록 조회부터 출력까지 모든 것을 자기 코드로 완전히 대체할 수 있습니다.

boards/skins/my-skin/list/handler.php — CMS 핵심 로직 완전 우회 
<?php
// boards/skins/my-skin/list/handler.php
// 이 파일이 있으면 CMS의 list 처리 로직을 100% 우회합니다.

$db    = $GLOBALS["dx_handler_context"]["db"];
$board = $GLOBALS["dx_handler_context"]["board"];
$auth  = $GLOBALS["dx_handler_context"]["auth"];

// DB를 내 마음대로 쿼리
$page    = max(1, (int)dx_get("page", 1));
$perPage = 10;
$offset  = ($page - 1) * $perPage;

// 날것 SQL이든 QueryBuilder든 원하는 대로
$posts = $db->rows(
    "SELECT p.*, m.nickname FROM dx_posts p",
    "LEFT JOIN dx_members m ON p.member_id = m.id",
    "WHERE p.board_id = ? AND p.status = 1",
    "ORDER BY p.is_notice DESC, p.id DESC",
    "LIMIT ? OFFSET ?",
    array($board["id"], $perPage, $offset)
);

// 레이아웃 감싸기
$dxth = DxTheme::getInstance();
$layoutFile = $dxth->resolve("layout/main.php");

ob_start();
?>
<!— 아무렇게나 HTML 짜기 —>
<div class="my-list">
    <h2 style="font-size:28px;font-weight:900"><?= $board["name"] ?></h2>
    <?php foreach ($posts as $p): ?>
        <div style="padding:12px;border-bottom:1px solid #eee">
            <a href="/<?= $board["board_key"] ?>/view/<?= $p["id"] ?>">
                <?= htmlspecialchars($p["title"]) ?>
            </a>
            <small style="color:gray"> — <?= $p["nickname"] ?></small>
        </div>
    <?php endforeach; ?>
</div>
<?php
$dx_content = ob_get_clean();
if ($layoutFile) require $layoutFile; else echo $dx_content;
✅ 결과: CMS의 list 처리를 완전히 대체한 커스텀 목록 페이지 출력

→ CMS 코어 코드를 한 줄도 수정하지 않고 게시판 목록 로직을 완전히 교체했습니다.
→ 날것 SQL, 즉석 JOIN, 인라인 스타일 — 전부 허용됩니다.
→ 이 파일이 exit을 안 해도 handler.php가 return으로 CMS 흐름을 막아줍니다


04. routes/ + controllers/ 날코딩

규칙 없이 원하는 대로 라우트와 컨트롤러 작성

routes/ — 클로저 라우트로 모든 로직을 한 파일에
routes/ 폴더의 PHP 파일들은 자동으로 로드됩니다. 컨트롤러 없이 클로저 하나에 모든 로직을 넣어도 됩니다.

routes/my_routes.php — 클로저 날코딩 예시
<?php
// routes/my_routes.php — 이 파일만 있으면 됩니다
if (!defined("DX_CMS")) exit;

// 클로저에 모든 로직을 때려 넣기 — 완전 허용
DxRouter::get("/my-page", function() {
    $auth = Auth::getInstance();
    if (!$auth->isLoggedIn()) {
        header("Location: /auth/login"); exit;
    }

    // DB 직접 쿼리 — 어떤 방법이든 OK
    $user  = $auth->user();
    $posts = dx_db("posts")
        ->where("member_id", $user["id"])
        ->orderBy("id", "desc")
        ->limit(10)
        ->get();

    // 날것 HTML 출력
    echo "<h1>내 글 목록 — " . htmlspecialchars($user["nickname"]) . "</h1>";
    foreach ($posts as $p) {
        echo "<p>" . htmlspecialchars($p["title"]) . "</p>";
    }
    exit;
});

// API 엔드포인트도 클로저로 — 구조 없이
DxRouter::post("/api/my-data", function() {
    // CSRF는 미들웨어로 처리
    dx_json(array("status"=>"ok", "time"=>time()));
})->middleware(array("auth", "csrf", "json"));

// 관리자 전용 페이지도 한 줄
DxRouter::get("/secret-page", function() {
    echo "<h1>관리자만 봄</h1>";
    exit;
})->middleware("admin");
controllers/ — 라라벨 문법 필요 없음, PHP 그대로

controllers/MyController.php — 자유로운 컨트롤러
<?php
// controllers/MyController.php
// 클래스 이름만 맞추면 됩니다. 나머지는 자유.
if (!defined("DX_CMS")) exit;

class MyController
{
    // 파라미터는 그냥 배열로 옵니다
    public function index($params = array())
    {
        // 아무렇게나 코딩
        $page   = isset($_GET["page"]) ? (int)$_GET["page"] : 1;
        $search = isset($_GET["q"]) ? trim($_GET["q"]) : "";

        // DB 쿼리 — 스타일 통일 안 해도 됨
        $db = Database::getInstance();
        if ($search) {
            $list = dx_db("posts")
                ->where("title", "LIKE", "%" . $search . "%")
                ->paginate(20);
        } else {
            // 날것 SQL도 OK
            $list["data"] = $db->rows(
                "SELECT * FROM dx_posts WHERE status=1",
                "ORDER BY id DESC LIMIT 20"
            );
        }

        // 레이아웃 포함해서 출력
        ob_start();
        include DX_ROOT . "/pages/my-page.php";
        $dx_content = ob_get_clean();

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

    // POST 처리도 자유롭게
    public function store($params = array())
    {
        // CSRF는 미들웨어가 처리해줌
        $title = dx_post("title", "");
        $content = dx_post("content", "");

        if (!$title) {
            dx_json(array("success"=>false, "msg"=>"제목 필요"), 400);
        }

        $id = dx_db("my_table")->insert(array(
            "title"      => $title,
            "content"    => $content,
            "member_id"  => Auth::getInstance()->get("id"),
            "created_at" => date("Y-m-d H:i:s"),
        ));

        dx_json(array("success"=>true, "id"=>$id));
    }
}

→ 클래스 이름만 파일명과 맞추면 됩니다.
→ extends Controller 불필요. interface 구현 불필요.
→ $_GET/$_POST 직접 접근도 됩니다. dx_get()/dx_post() 안 써도 됩니다.
→ QueryBuilder와 날것 SQL을 같은 파일에 섞어 써도 됩니다.


05. 플러그인도 날코딩 허용

plugin.php에 코드를 마음대로 넣어도 됩니다

plugins/my-plugin/plugin.php — 날코딩 플러그인 예시
<?php
// plugins/my-plugin/plugin.php
// 복잡한 ServiceProvider 패턴? 필요 없습니다.
// 이 파일에 코드를 직접 쓰면 됩니다.
if (!defined("DX_CMS")) exit;

// 서비스 등록
dx_app()->singleton("slack", function() {
    return new SlackNotifier(dx_config("slack_webhook"));
});

// 훅 — 글이 저장될 때 슬랙 알림
dx_add_hook("after_post_save", function($data) {
    $post = isset($data["post"]) ? $data["post"] : array();
    if (empty($post)) return;

    // 날것의 HTTP 요청 — curl 직접 사용
    $payload = json_encode(array(
        "text" => "새 글: " . $post["title"] . " by " . $post["nickname"],
    ));
    $ch = curl_init(dx_config("slack_webhook"));
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_exec($ch);
    curl_close($ch);
}, 10);

// 하단에 광고 배너 삽입 — 훅 하나로
dx_add_hook("dx_bottom", function() {
?>
<div style="position:fixed;bottom:0;left:0;right:0;background:#1e293b;
            color:#fff;text-align:center;padding:10px;font-size:13px">
    🎉 My Plugin이 작동 중입니다!
</div>
<?php
}, 99);

// 커스텀 라우트도 여기서 추가 가능
DxRouter::get("/plugin-test", function() {
    echo "<h1>플러그인 테스트 페이지</h1>";
    exit;
});

✅ 결과: 플러그인 활성화만 하면 위 코드가 전부 동작합니다.

→ curl 직접 사용 허용. Guzzle 같은 패키지 없어도 됩니다.
→ 훅에서 inline HTML echo 허용. Blade 디렉티브 불필요.
→ plugin.php 한 파일에 서비스 등록 + 훅 + 라우트를 모두 넣어도 됩니다.


06. extend/ — 파일만 넣으면 실행, 코딩 스타일 무관

가장 자유로운 확장 방법

extend/ 폴더는 훅 등록도, 클래스도, 규칙도 없습니다. PHP 파일을 폴더에 넣으면 자동으로 실행됩니다.
파일 위치 실행 시점 & 가능한 것
extend/top/ 01_check.php DB•세션•Auth 완료 직후. 점검모드•IP차단•글로벌 변수 주입 가능. $_SERVER, $GLOBALS, Auth, Database 모두 사용 가능.
extend/middle/ 01_log.php 라우트 확정 직후. $GLOBALS["dx_route"]로 현재 라우트 정보 접근. 방문자 로그, 접근 제어, A/B 테스트 가능.
extend/bottom/ 01_perf.php 렌더링 완료 후. 성능 로그, 캐시 저장, 정리 작업 가능. 출력 버퍼가 아직 열려 있어 추가 출력도 가능.

extend/top/01_maintenance.php — 점검 모드 (날코딩)
<?php
// extend/top/01_maintenance.php
// 규칙 없이 그냥 PHP 코드를 씁니다.

$mode = dx_config("maintenance_mode", 0);

if ((int)$mode === 1) {
    $auth = Auth::getInstance();

    // 관리자는 그냥 통과
    if ($auth->isAdmin()) return;

    // IP 화이트리스트 — 배열을 그냥 하드코딩해도 됨
    $allowed_ips = array("127.0.0.1", "192.168.1.100");
    if (in_array($_SERVER["REMOTE_ADDR"], $allowed_ips)) return;

    // 점검 페이지를 날것의 HTML로 출력
    http_response_code(503);
    header("Retry-After: 3600");
?>
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>점검 중</title></head>
<body style="text-align:center;font-family:sans-serif;padding:100px">
    <h1 style="font-size:48px">🔧</h1>
    <h2>현재 사이트 점검 중입니다</h2>
    <p style="color:gray">잠시 후 다시 방문해주세요.</p>
</body></html>
<?php
    exit;
}

→ 이 파일 하나로 전체 사이트 점검 모드가 완성됩니다.
→ 훅 등록 불필요. 클래스 불필요. config 설정 하나 + 파일 하나.
→ HTML도 그냥 PHP 파일 안에 섞어 씁니다.


07. 날코딩•막코딩 허용 범위 완전 요약표

어디서, 어떻게 해도 되는지 한눈에
 
코딩 방식 CMS 동작 에러 처리
스킨 list.php — 변수 체크 없음 ✅ 페이지 정상 출력 Notice → 로그, 화면 출력 계속
스킨 list.php — dx_esc() 생략 ✅ 출력됨 (XSS 주의) 출력 되지만 보안 취약, 나중에 수정
extend/ 파일 — 예외 발생 ✅ CMS 계속 실행 safeExec()가 예외 격리, 로그 기록
extend/ 파일 — 정의 안 된 함수 ✅ CMS 계속 실행 set_error_handler가 잡아서 로그만
actions/ — $GLOBALS 직접 접근 ✅ 정식 지원 패턴 erp/actions/inventory.php에서 사용중
list/handler.php — CMS 핵심 우회 ✅ 완전 독립 처리 include 후 return으로 흐름 막음
routes/ — 클로저에 모든 로직 ✅ 정상 동작 클로저 라우트 공식 지원
plugin.php — curl 직접 사용 ✅ 정상 동작 Composer 패키지 없이 표준 PHP
pages/home.php — 인라인 스타일 ✅ 정상 출력 HTML 자유롭게 작성 가능
controllers/ — PSR 무시 ✅ 정상 동작 클래스 이름만 파일명과 일치하면 됨
날것 SQL — $db->rows() 직접 ✅ PDO 바인딩 자동 SQL Injection은 ? 플레이스홀더가 방어
QueryBuilder + 날것 SQL 혼용 ✅ 동작 같은 파일에 두 방법 혼용 허용

한 줄 결론

DXCMS에서 틀린 코딩 방식은 없습니다.
에러가 나면 로그에 기록하고, CMS는 계속 돌아갑니다.
나중에 에러 로그를 보면서 고치면 됩니다.

단 하나의 원칙만 지키면 됩니다:
→ 파일 첫 줄에 if (!defined("DX_CMS")) exit; 를 넣어서 직접 접근을 차단하세요.


08. 디버그 모드 — 에러를 화면에서 바로 보는 법

개발 중에는 숨기지 않고 다 보여줍니다

data/config.php — 디버그 모드 설정
<?php
// data/config.php 에 추가
define('DX_DEBUG', true);

// 이 설정 하나로:
// - 모든 PHP 에러가 화면에 표시됩니다.
// - 스킨 에러가 에러 박스 형태로 표시됩니다.
// - extend/ 파일 에러가 화면에 출력됩니다.
// - data/error.log에도 동시 기록됩니다.

// 운영 서버에서는 이 줄을 제거하거나 false로 설정하세요.
define('DX_DEBUG', false);

개발 흐름:

1. DX_DEBUG = true 설정
2. 마음대로 날코딩
3. 에러가 나면 화면에서 바로 확인
4. 고치면서 개선
5. 배포 전 DX_DEBUG = false 또는 제거

에러가 무서워서 조심스럽게 짤 필요가 없습니다.
일단 돌아가게 만들고, 에러를 보면서 다듬으면 됩니다.

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