회원가입 | 고객센터 |
DESIGNONEX
dxcms.kr
로그인 회원가입
고객센터
8. 플러그인

플러그인 구조

D DX
2026.05.01 01:39(수정됨) 132 0

1장. 플러그인 시스템 개요

DXCMS의 플러그인 시스템은 두 개의 핵심 컴포넌트로 구성됩니다. 첫째, HookManager — 액션(Action)과 필터(Filter) 기반의 이벤트 훅 시스템으로, 플러그인이 CMS의 특정 시점에 코드를 삽입합니다. 둘째, PluginRegistry — 에디터•결제•소켓 등 "타입"별 플러그인 등록소로, 관리자가 활성 플러그인을 선택하면 해당 기능이 자동으로 교체됩니다.


1.1 플러그인 시스템 핵심 설계 원칙

  • 파일 기반 자동 로딩 — plugins/ 폴더에 폴더를 만들고 plugin.php를 넣으면 자동으로 로드됨 (DB 활성화 불필요)
  • 훅 기반 삽입 — 플러그인은 CMS 코드를 직접 수정하지 않고 훅 포인트에 코드를 등록함
  • 타입 교체 구조 — 에디터•결제 등 타입은 관리자가 선택한 플러그인으로 언제든 교체 가능
  • 우선순위 제어 — 동일 훅에 여러 플러그인이 등록되면 priority 값으로 실행 순서 제어
  • PHP 5.6+ 완전 호환 — 클로저, 함수명 문자열, 배열 콜백 모두 지원
  • 안전한 오류 격리 — DX_DEBUG=false 시 플러그인 오류가 다른 플러그인에 영향 안 줌


1.2 플러그인 로딩 흐름

index.php의 STEP 3에서 load_plugins() 함수가 호출됩니다.
// index.php — STEP 3 (라우팅 전, 세션 시작 후)
load_plugins();

// core/functions.php — load_plugins() 구현
function load_plugins() {
    $dirs = glob(DX_PLUGINS . "/*", GLOB_ONLYDIR);
    foreach ($dirs as $dir) {
        $f = $dir . "/plugin.php";
        if (file_exists($f)) {
            require_once $f;  // 모든 폴더의 plugin.php 무조건 로드
        }
    }
}

⚠️ 중요: 모든 plugin.php는 항상 로드됩니다
plugins/ 폴더에 있는 모든 폴더의 plugin.php는 관리자 활성화 여부와 무관하게 항상 실행됩니다.
다만, 훅 등록•타입 등록 자체는 부하가 미미하므로 실용상 문제 없습니다.
무거운 초기화 작업은 훅 콜백 내부에서 처리해야 합니다 (조건부 실행).
실제 기능 실행은 해당 훅이 발동될 때만 동작합니다.


2장. 플러그인 파일 구조


2.1 전체 디렉토리 구조

plugins/
  my-plugin/                 ← 플러그인 폴더 (폴더명 = 플러그인 식별자)
  │  plugin.php              ← 메인 실행 파일 ★필수
  │  manifest.php            ← 메타 정보 (마켓 공유용) ★필수
  │
  │  admin/                  ← 관리자 전용 파일 (선택)
  │    settings.php          ← 플러그인 설정 페이지
  │
  │  assets/                 ← CSS/JS/이미지 (선택)
  │    plugin.css
  │    plugin.js
  │
  │  views/                  ← 렌더링 뷰 파일 (선택)
  │    widget.php
  │
  └─ README.md               ← 사용 설명서 (선택, 권장)


2.2 파일별 역할

파일 필수 여부 역할
plugin.php 필수 플러그인 진입점. dx_register_plugin() 호출 + 훅 등록. index.php가 자동 로드
manifest.php 필수 메타 정보 배열 return. 관리자 목록 표시 + 마켓 공유에 사용
admin/settings.php 선택 관리자 → 플러그인 설정 페이지 커스텀 UI
assets/ 선택 CSS/JS/이미지. dx_base_url("plugins/{폴더}/assets/") 로 URL 획득
README.md 권장 사용자 설명서. 관리자 화면에서 표시될 수 있음


2.3 manifest.php 전체 형식

<?php
// plugins/my-plugin/manifest.php
return array(
    "name"        => "My Plugin",           // 관리자 목록 표시 이름
    "version"     => "1.0.0",              // 버전
    "description" => "플러그인 기능 설명",    // 관리자 목록 설명
    "author"      => "개발자명",
    "author_url"  => "https://mysite.com",
    "type"        => "editor",             // editor|payment|socket|captcha|utility 등
    "min_version" => "8.0.0",              // 최소 DXCMS 버전
    "tags"        => "에디터,WYSIWYG",       // 쉼표 구분 태그
    "category"    => "utility",            // 카테고리
    "homepage"    => "https://...",
    "license"     => "MIT",
);


2.4 plugin.php 기본 구조

<?php
// plugins/my-plugin/plugin.php
// ⚠️ DX_CMS 상수가 없으면 직접 접근 차단 (보안 필수)
if (!defined("DX_CMS")) exit("Direct access not allowed.");

// 1. PluginRegistry에 타입 등록 (선택)
dx_register_plugin(array(
    "id"          => "my-plugin",     // 고유 ID (폴더명과 일치 권장)
    "type"        => "editor",        // 타입
    "name"        => "My Plugin",     // 관리자 표시 이름
    "version"     => "1.0.0",
    "description" => "설명",
    "author"      => "개발자명",
    "priority"    => 10,              // select 박스 정렬 순서
    "settings"    => array(           // 플러그인 전용 설정 필드
        "api_key" => array(
            "label"   => "API 키",
            "type"    => "text",
            "default" => "",
        ),
    ),
));

// 2. 훅 등록 — 실제 기능 구현
dx_add_hook("dx_head", function($ctx) {
    echo '<link rel="stylesheet" href="' . dx_base_url("plugins/my-plugin/assets/plugin.css") . '">';
}, 10);

dx_add_hook("dx_body_bottom", function() {
    echo '<script src="' . dx_base_url("plugins/my-plugin/assets/plugin.js") . '"></script>';
});


3장. PluginRegistry — 타입 등록 시스템

PluginRegistry는 "에디터를 교체하고 싶다", "결제 모듈을 바꾸고 싶다" 처럼 관리자가 특정 기능의 구현체를 선택할 수 있게 해주는 시스템입니다. 플러그인이 자신을 특정 타입으로 등록하면, 관리자 설정 화면의 드롭다운에 자동으로 나타납니다.


3.1 기본 제공 타입

타입 ID 설정 키 (DB) 설명 및 용도
editor active_editor 게시글·댓글 작성 WYSIWYG 에디터. 예: CKEditor4, TinyMCE
payment active_payment 결제 PG사 모듈. 예: 토스페이먼츠, KG이니시스, 카카오페이
captcha active_captcha 스팸 방지 CAPTCHA. 예: reCAPTCHA v3, hCaptcha
sms active_sms SMS 발송. 예: 알리고, 네이버 클라우드 SMS
social_login active_social_login 소셜 로그인. 카카오·네이버·구글·GitHub
socket active_socket WebSocket 실시간 기능. 접속자 추적·채팅·알림
(커스텀) active_{타입명} 개발자가 임의 타입 추가 가능. 관리자 설정 자동 표시


3.2 dx_register_plugin() 파라미터 상세

파라미터 필수 설명
id 필수 플러그인 고유 ID. 영문·숫자·하이픈만. 폴더명과 일치 권장
type 필수 등록할 타입. editor|payment|captcha|sms|socket 또는 커스텀
name 필수 관리자 select 박스에 표시되는 이름
version 선택 버전 표시 (기본 "1.0.0")
description 선택 플러그인 기능 설명
author 선택 제작자 이름
priority 선택 select 박스 정렬 순서. 낮을수록 위에 표시 (기본 10)
settings 선택 관리자 설정 폼에 자동 생성될 입력 필드 정의 배열
usage_guide 선택 관리자 화면에 표시될 사용 방법 안내 배열


3.3 settings 필드 타입 완전 가이드

settings 배열에 정의한 필드는 관리자 → 플러그인 → 해당 플러그인 설정 UI에 자동으로 표시됩니다.
 
type 관리자 UI 설명 및 저장 키
"text" 텍스트 입력란 plugin_{id}_{key} 형식으로 settings 테이블에 저장
"password" 비밀번호 입력란 값이 마스킹됨. API 키, 시크릿 키 입력용
"select" 드롭다운 "options" 배열로 선택지 정의. 예: {"1":"사용","0":"사용 안 함"}
"textarea" 여러 줄 텍스트 긴 설정값, JSON 형식 데이터 입력용
"checkbox_group" 체크박스 그룹 "options" 배열. 선택된 값이 쉼표 구분 문자열로 저장


settings 정의 예시

"settings" => array(
    "api_key" => array(
        "label"   => "API 키",
        "type"    => "text",
        "default" => "",
    ),
    "secret_key" => array(
        "label"   => "시크릿 키",
        "type"    => "password",
        "default" => "",
    ),
    "use_sandbox" => array(
        "label"   => "테스트 모드",
        "type"    => "select",
        "options" => array("1" => "테스트", "0" => "실제 결제"),
        "default" => "1",
    ),
    "support_methods" => array(
        "label"   => "지원 결제 수단",
        "type"    => "checkbox_group",
        "options" => array("card" => "카드", "bank" => "계좌이체", "phone" => "휴대폰"),
        "default" => "card,bank",
    ),
),


3.4 settings 값 읽기

저장된 settings 값은 dx_config()로 읽습니다. 키 형식은 plugin_{플러그인ID}_{설정키} 입니다.
// plugin.php 또는 훅 콜백 내에서
$apiKey    = dx_config("plugin_my-plugin_api_key", "");
$secretKey = dx_config("plugin_my-plugin_secret_key", "");
$sandbox   = dx_config("plugin_my-plugin_use_sandbox", "1") === "1";

// 체크박스 그룹은 쉼표 구분 문자열로 저장됨
$methods   = explode(",", dx_config("plugin_my-plugin_support_methods", "card"));
// 결과: ["card", "bank"]


3.5 활성 플러그인 확인

// 현재 활성 에디터 플러그인 ID 반환
$editorId = dx_active_plugin("editor");   // 예: "ckeditor4-editor"

// 현재 활성 결제 플러그인 정보 반환
$paymentInfo = dx_active_plugin_info("payment");
// 결과: ["id"=>"tosspay","name"=>"토스페이먼츠","version"=>"1.0.0",...]

// 특정 타입에 플러그인이 등록되어 있는지 확인
$hasEditor = PluginRegistry::getInstance()->hasPlugins("editor"); // bool


4장. DB 스키마 — dx_plugins 테이블

설치된 플러그인 목록과 공유 상태를 관리하는 테이블입니다. plugin.php는 자동 로드되지만, 관리자 화면에 표시되려면 DB에 등록되어야 합니다.
 
컬럼명 타입 설명
id INT UNSIGNED PK (AUTO_INCREMENT)
directory VARCHAR(100) 플러그인 폴더명. UNIQUE. 예: ckeditor4-editor
name VARCHAR(191) 표시 이름 (manifest.php에서 로드)
version VARCHAR(50) 버전
description TEXT 설명
author VARCHAR(191) 제작자 이름
author_url VARCHAR(500) 제작자 URL
status TINYINT(1) 1=활성, 0=비활성 (toggle_plugin 액션으로 변경)
is_shared TINYINT(1) 마켓 공유 여부. 1=공개, 0=비공개
share_key VARCHAR(64) 공유 고유 키 (32바이트 랜덤 hex). UNIQUE
share_desc TEXT 공유 시 표시할 설명 (마켓 카드용)
share_tags VARCHAR(500) 공유 태그 (쉼표 구분)
download_count INT 공유 다운로드 횟수
settings TEXT 플러그인 자체 설정 JSON (미사용 시 NULL)
sort_order SMALLINT 목록 정렬 순서
installed_at DATETIME 최초 등록일시
shared_at DATETIME 최초 공유 일시


5장. HookManager — 훅 시스템 완전 가이드

HookManager는 액션(Action)과 필터(Filter) 두 가지 훅 방식을 지원합니다. 액션은 특정 시점에 코드를 실행하는 것이고, 필터는 값을 받아 변형 후 반환하는 것입니다.


5.1 Action 훅 — 코드 삽입

// 훅 등록 — plugin.php에서
dx_add_hook(
    "훅이름",          // 훅 포인트 이름
    function($args) {  // 콜백 (PHP 5.6+: 클로저, 함수명, 배열 모두 가능)
        // 실행할 코드
        echo "훅에서 출력";
    },
    10                 // priority (낮을수록 먼저 실행, 기본 10)
);

// 훅 실행 — CMS 또는 테마에서
dx_run_hook("훅이름", $contextData);

// 훅이 등록되어 있는지 확인
$exists = dx_has_hook("훅이름"); // bool

// 특정 콜백만 제거
dx_remove_hook("훅이름", $specificCallback);

// 훅 전체 제거
dx_remove_hook("훅이름");


5.2 Filter 훅 — 값 변형

// 필터 등록 — 값을 받아 변형 후 반환
dx_add_filter("post_content", function($content, $args) {
    // 내용에 광고 삽입 예시
    return $content . "<div class=\"ad\">광고</div>";
}, 10);

// 필터 실행 — 변형된 값 반환
$filteredContent = dx_apply_filter("post_content", $originalContent, $extraArgs);


5.3 표준 훅 포인트 — 레이아웃 훅

layout/main.php에서 자동으로 실행되는 레이아웃 관련 훅 포인트입니다. 모든 페이지에서 발동됩니다
 
훅 이름 발동 위치 및 전달 인자
dx_head <head> 안쪽 — CSS, meta 태그, JSON-LD 삽입. $context 배열 전달
dx_top 레이아웃 body 최상단 (dx_hook_top() 내부). $context 전달
dx_{type}_top 페이지 타입별 top 훅. 예: dx_board_top, dx_home_top
dx_middle 콘텐츠 영역 중간 (dx_hook_middle() 내부). $context 전달
dx_{type}_middle 페이지 타입별 middle 훅. 예: dx_board_middle
dx_bottom 레이아웃 body 하단 (dx_hook_bottom() 내부). $context 전달
dx_{type}_bottom 페이지 타입별 bottom 훅
dx_footer_scripts </body> 직전 — JS 파일 삽입용
dx_body_bottom </body> 최하단 — 분석 코드, 채팅 위젯 등


5.4 표준 훅 포인트 — 게시판 훅

훅 이름 발동 시점 및 전달 인자
dx_board_before 핸들러 진입 직후. board, action, skin, id 전달
dx_board_list_context 목록 $ctx 확정 직전 — 참조(&)로 전달, 변경 가능
dx_board_view_context 상세 $ctx 확정 직전 — 참조(&)로 전달
dx_board_write_context 글쓰기 $ctx 확정 직전
dx_board_before_save 글 저장 직전 — $data 참조 전달. 커스텀 필드 추가 가능
dx_after_write 글 저장 완료 후 — post_id, board, data 전달
dx_board_after_save 저장 후 — redirect URL 변경 가능
dx_board_before_delete 글 삭제 직전 — post, board 전달
dx_board_after_delete 글 삭제 완료 후
dx_after_comment 댓글 등록 완료 후 — comment_id, post_id, board
dx_board_after 핸들러 처리 완료 후


5.5 표준 훅 포인트 — 에디터•결제 훅

훅 이름 발동 시점 및 용도
dx_editor_render 에디터 HTML 출력 시점. name, value, options, editor 전달
dx_editor_init dx_render_editor() 호출 시 내부 브릿지 훅. 에디터 플러그인 실행
dx_payment_request dx_request_payment() 호출 시. 결제창 HTML/JS 출력


5.6 표준 훅 포인트 — 회원 훅

훅 이름 발동 시점
dx_after_login 로그인 성공 후. user 배열 전달
dx_after_logout 로그아웃 후. user 배열 전달
dx_after_register 회원가입 완료 후. user_id, data 전달


5.7 priority(우선순위) 활용

같은 훅에 여러 플러그인이 등록된 경우 priority 값(낮을수록 먼저 실행)으로 순서를 제어합니다.
// 플러그인 A: priority=5 → 먼저 실행
dx_add_hook("dx_head", function($ctx) {
    echo '<link rel="stylesheet" href="plugin-a.css">';
}, 5);

// 플러그인 B: priority=10 (기본) → 나중에 실행
dx_add_hook("dx_head", function($ctx) {
    echo '<link rel="stylesheet" href="plugin-b.css">';
}, 10);

// 결과: plugin-a.css → plugin-b.css 순서로 출력

// 특별 priority 관례:
// 1~5: 코어 기능 (예약)
// 10 : 기본값 (대부분의 플러그인)
// 100: 나중에 실행 (다른 플러그인 출력 후)
// 999: 가장 마지막 (디버그, 성능 측정 등)


6장. 기본 제공 플러그인 목록


6.1 에디터 플러그인

플러그인 타입 주요 기능
ckeditor4-editor editor CKEditor 4 WYSIWYG. 자동번역·드래그테이블·테이블간격·셀배경색. 댓글 에디터 지원


6.2 결제 플러그인

플러그인 타입 주요 기능
tosspay-payment payment 토스페이먼츠 SDK v1. 카드·계좌이체·가상계좌·휴대폰결제
kakaopay-payment payment 카카오페이 단건결제·정기결제
nicepay-payment payment 나이스페이먼츠
kg-inicis-payment payment KG이니시스
kcp-payment payment NHN KCP
payletter-payment payment 페이레터
naverpay-payment payment 네이버페이
danal-payment payment 다날
paypal-payment payment PayPal (해외결제)
stripe-payment payment Stripe (해외결제)
custom-payment-template payment 커스텀 결제 모듈 개발 템플릿


6.3 실시간•유틸리티 플러그인

플러그인 타입 주요 기능
dx-socket socket 실시간 접속자 추적·알림·채팅·쪽지·댓글 live. WebSocket + Node.js
example-plugin example 플러그인 개발 예제. 실행 시간·쿼리 수 표시 (DX_DEBUG 시만)


7장. 관리자 사용방법


7.1 플러그인 목록 화면

관리자 → 플러그인 메뉴를 클릭하면 plugins/ 폴더를 자동 스캔하여 목록을 표시합니다.
 
UI 요소 기능
플러그인 카드 폴더명, 이름, 버전, 제작자, 설명 표시 (manifest.php에서 로드)
활성화 / 비활성화 토글 dx_plugins.status 컬럼 변경. 토글 후 사용 방법 안내 표시
모듈 설정 섹션 PluginRegistry에 등록된 타입별 드롭다운. 활성 플러그인 선택
플러그인 설정 (⚙️) 각 플러그인의 settings 필드가 입력 UI로 자동 생성
마켓 공유 버튼 플러그인을 DX마켓에 공유 (is_shared=1)


7.2 에디터 플러그인 설정 방법

  1. 관리자 → 플러그인 메뉴 클릭
  2. "모듈 설정" 섹션에서 "에디터" 드롭다운 확인
  3. "CKEditor 4" 선택 후 저장
  4. settings.active_editor = "ckeditor4-editor" 로 저장됨
  5. 글쓰기 페이지에서 CKEditor 에디터 자동 표시 확인

💡 에디터 없음으로 설정하는 방법
에디터 드롭다운에서 "기본 텍스트에어리어 (에디터 없음)" 선택 → 저장
또는 게시판 설정에서 해당 게시판만 editor = "none" 으로 개별 지정 가능


7.3 플러그인별 설정 저장 방식

플러그인의 settings에 정의된 필드는 관리자 "플러그인 설정" 아코디언 패널에 자동 표시되며, 저장 시 settings 테이블에 아래 형식으로 저장됩니다.
// 저장 키 형식
plugin_{플러그인ID}_{설정키}

// 예시: ckeditor4-editor 플러그인의 height 설정
plugin_ckeditor4-editor_height = "400"

// 예시: tosspay-payment 플러그인의 api_key 설정
plugin_tosspay-payment_api_key = "test_..."

// 읽기: plugin.php 내에서
$height = dx_config("plugin_ckeditor4-editor_height", "400");


7.4 활성 플러그인 선택 저장 방식

모듈 설정 드롭다운에서 선택 후 저장하면 settings 테이블에 타입별 활성 키가 저장됩니다.
// settings 테이블 저장 형식
active_editor  = "ckeditor4-editor"
active_payment = "tosspay-payment"
active_captcha = ""   (선택 안 함)
active_socket  = "dx-socket"

// 코드에서 확인
$editorId  = dx_active_plugin("editor");   // "ckeditor4-editor"
$paymentId = dx_active_plugin("payment");  // "tosspay-payment"


8장. 실전 플러그인 제작 예제


8.1 공지사항 배너 플러그인

모든 페이지 상단에 공지 배너를 표시하는 간단한 플러그인 예시입니다.

manifest.php
<?php
return array(
    "name"        => "공지 배너",
    "version"     => "1.0.0",
    "description" => "모든 페이지 상단에 공지 배너 표시",
    "author"      => "내 이름",
    "type"        => "utility",
    "category"    => "utility",
);

plugin.php
<?php
if (!defined("DX_CMS")) exit("Direct access not allowed.");

// 타입 등록 (관리자 드롭다운에는 표시 안 됨 — utility 타입)
dx_register_plugin(array(
    "id"      => "notice-banner",
    "type"    => "utility",
    "name"    => "공지 배너",
    "version" => "1.0.0",
    "settings" => array(
        "banner_text" => array(
            "label"   => "배너 문구",
            "type"    => "text",
            "default" => "🔔 공지사항: 서버 점검 중입니다.",
        ),
        "banner_color" => array(
            "label"   => "배경색",
            "type"    => "select",
            "options" => array("blue"=>"파랑","red"=>"빨강","green"=>"초록"),
            "default" => "blue",
        ),
        "banner_enabled" => array(
            "label"   => "배너 표시",
            "type"    => "select",
            "options" => array("1"=>"표시","0"=>"숨김"),
            "default" => "1",
        ),
    ),
));

// 배너가 활성화된 경우에만 훅 등록
if (dx_config("plugin_notice-banner_banner_enabled", "1") === "1") {
    $bannerText  = dx_config("plugin_notice-banner_banner_text", "");
    $bannerColor = dx_config("plugin_notice-banner_banner_color", "blue");

    if ($bannerText) {
        $colors = array(
            "blue"  => "background:#1a73e8;color:#fff",
            "red"   => "background:#dc2626;color:#fff",
            "green" => "background:#16a34a;color:#fff",
        );
        $style = isset($colors[$bannerColor]) ? $colors[$bannerColor] : $colors["blue"];

        dx_add_hook("dx_top", function($ctx) use ($bannerText, $style) {
            echo '<div style="' . $style . ';padding:10px 20px;text-align:center;font-size:.875rem;font-weight:600">'
               . htmlspecialchars($bannerText, ENT_QUOTES, "UTF-8")
               . '</div>';
        }, 1); // priority=1: 가장 먼저 출력
    }
}


8.2 글 저장 시 외부 알림 플러그인

게시글 등록 시 슬랙 웹훅으로 알림을 보내는 플러그인 예시입니다.
<?php
if (!defined("DX_CMS")) exit;

dx_register_plugin(array(
    "id"      => "slack-notify",
    "type"    => "utility",
    "name"    => "Slack 알림",
    "version" => "1.0.0",
    "settings" => array(
        "webhook_url" => array(
            "label"   => "Slack 웹훅 URL",
            "type"    => "text",
            "default" => "",
        ),
        "board_keys" => array(
            "label"   => "알림받을 게시판 키 (쉼표 구분, 비우면 전체)",
            "type"    => "text",
            "default" => "",
        ),
    ),
));

// 글 저장 완료 훅 등록
dx_add_hook("dx_after_write", function($args) {
    $webhookUrl = dx_config("plugin_slack-notify_webhook_url", "");
    if (!$webhookUrl) return;

    $boardKeys  = dx_config("plugin_slack-notify_board_keys", "");
    $board      = isset($args["board"]) ? $args["board"] : array();
    $boardKey   = isset($board["board_key"]) ? $board["board_key"] : "";

    // 특정 게시판만 알림 (설정된 경우)
    if ($boardKeys) {
        $keys = array_map("trim", explode(",", $boardKeys));
        if (!in_array($boardKey, $keys)) return;
    }

    $postId   = isset($args["post_id"]) ? $args["post_id"] : "";
    $postUrl  = dx_base_url($boardKey . "/view/" . $postId);
    $data     = isset($args["data"]) ? $args["data"] : array();
    $title    = isset($data["title"]) ? $data["title"] : "(제목 없음)";

    // 슬랙 웹훅 전송
    $payload = json_encode(array(
        "text" => "📝 새 글이 등록되었습니다: *" . $title . "*\n" . $postUrl
    ));
    $ch = curl_init($webhookUrl);
    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_setopt($ch, CURLOPT_TIMEOUT, 3);
    curl_exec($ch);
    curl_close($ch);
}, 10);


8.3 커스텀 에디터 플러그인 최소 구현

새 에디터 플러그인을 만들어 기존 에디터와 교체하는 예시입니다.
 
<?php
if (!defined("DX_CMS")) exit;

// 1. editor 타입으로 등록
dx_register_plugin(array(
    "id"      => "my-editor",
    "type"    => "editor",        // ← editor 타입이어야 드롭다운에 표시
    "name"    => "My Editor",
    "version" => "1.0.0",
    "settings" => array(
        "height" => array(
            "label"   => "에디터 높이 (px)",
            "type"    => "text",
            "default" => "300",
        ),
    ),
));

// 2. dx_editor_render 훅으로 에디터 HTML 출력
// 이 훅은 dx_render_editor()가 내부적으로 호출함
dx_add_hook("dx_editor_render", function($args) {
    // 이 플러그인이 활성화된 경우만 실행
    if (dx_active_plugin("editor") !== "my-editor") return;

    $name    = isset($args["name"])  ? $args["name"]  : "content";
    $value   = isset($args["value"]) ? $args["value"] : "";
    $height  = (int)dx_config("plugin_my-editor_height", "300");

    // 에디터 초기화 CSS/JS 로드 (중복 방지)
    static $loaded = false;
    if (!$loaded) {
        $loaded = true;
        echo '<script src="https://cdn.example.com/my-editor.js"></script>';
    }

    // textarea + 에디터 초기화 스크립트
    echo '<textarea id="editor_' . htmlspecialchars($name, ENT_QUOTES) . '"'
       . ' name="' . htmlspecialchars($name, ENT_QUOTES) . '"'
       . ' style="height:' . $height . 'px">' . htmlspecialchars($value, ENT_QUOTES, "UTF-8") . '</textarea>';
    echo '<script>MyEditor.init("editor_' . htmlspecialchars($name, ENT_QUOTES) . '");</script>';
}, 10);


9장. 자주 묻는 질문 (FAQ)

Q1. plugin.php가 자동으로 로드된다면 비활성화는 의미가 없나요?

맞습니다. plugin.php는 항상 로드됩니다. 관리자의 "활성화/비활성화" 토글(dx_plugins.status)은 관리자 UI 표시 여부와 공유 기능에 영향을 줄 뿐입니다. 실제로 플러그인을 비활성화하려면 plugin.php에서 dx_config("plugin_{id}_enabled", "1") 값을 체크하는 코드를 직접 작성해야 합니다.


Q2. 동일한 훅에 두 플러그인이 등록되면 어떻게 되나요?

priority 순서대로 모두 실행됩니다. 에디터처럼 "하나만 실행해야 하는" 경우에는 훅 콜백 내부에서 dx_active_plugin("editor") === "my-plugin-id" 조건을 확인하여 현재 활성화된 플러그인만 동작하도록 구현해야 합니다.


Q3. 플러그인에서 DB를 사용하려면?

Database::getInstance()로 글로벌 DB 인스턴스를 얻어 사용합니다. 플러그인 전용 테이블이 필요하면 설치 시 CREATE TABLE을 실행하거나, settings 테이블을 활용하는 것이 가장 단순합니다.

$db = Database::getInstance();
$rows = $db->rows("SELECT * FROM `{$db->table("posts")}` WHERE board_id=? LIMIT 5", array(1));

// 플러그인 전용 설정 저장
$db->query(
    "INSERT INTO `{$db->table("settings")}` (setting_key, setting_value, updated_at)
     VALUES (?, ?, NOW()) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value), updated_at=NOW()",
    array("plugin_my-plugin_custom_data", json_encode($data))
);

Q4. 플러그인의 CSS/JS 파일 URL을 얻으려면?
// 플러그인 assets 폴더 URL
$cssUrl = dx_base_url("plugins/my-plugin/assets/style.css");
$jsUrl  = dx_base_url("plugins/my-plugin/assets/script.js");

// 훅에서 사용
dx_add_hook("dx_head", function() {
    echo '<link rel="stylesheet" href="' . dx_base_url("plugins/my-plugin/assets/style.css") . '?v=1.0.0">';
});

Q5. PluginRegistry에 등록하지 않은 플러그인도 동작하나요?

네. dx_register_plugin() 호출 없이 훅만 등록해도 완전히 동작합니다. PluginRegistry는 "에디터를 선택한다", "결제 모듈을 고른다"처럼 관리자가 여러 구현체 중 하나를 선택해야 하는 경우에만 필요합니다. 단순히 기능을 추가하는 플러그인은 훅만 등록하면 됩니다.


Q6. 플러그인이 여러 타입을 동시에 등록할 수 있나요?

네. 예를 들어 소셜 로그인 플러그인이 sms 타입과 social_login 타입을 모두 등록할 수 있습니다. dx_register_plugin()을 여러 번 호출하되, id를 각각 다르게 지정하면 됩니다.


Q7. 플러그인을 삭제하려면?

plugins/ 폴더에서 해당 플러그인 폴더를 삭제하면 됩니다. DB의 dx_plugins 테이블에는 레코드가 남지만 기능상 문제는 없습니다. 깔끔하게 정리하려면 관리자 화면에서 비활성화 후 폴더를 삭제하거나, DB에서 해당 레코드도 직접 삭제하세요.

댓글0

로그인 후 댓글을 작성할 수 있습니다.
3.2 폴더 구조 assets/ — 정적 자원 2026.04.21 3.2 폴더 구조 plugins/ — 플러그인 시스템 2026.04.21 3.2 폴더 구조 themes/ — 테마 시스템 2026.04.21 3.2 폴더 구조 boards/ — 게시판 시스템 2026.04.21 3.2 폴더 구조 admin/ — 관리자 패널 2026.04.21 3.2 폴더 구조 core/ — CMS 엔진 2026.04.21 3.1 엔진 개요 실행 구조 개요 2026.04.21 3.1 엔진 개요 DX 엔진 구조 설명 2026.04.21 2. 시작 가이드 설치 시 보안 경로 구조 2026.04.20 2. 시작 가이드 서버별 설정 파일 상세 2026.04.20 2. 시작 가이드 기본 폴더 구조 설명 2026.04.20 2. 시작 가이드 설치 절차 2026.04.20 2. 시작 가이드 설치 환경 (PHP 버전, 서버 환경) 2026.04.20 비전 DXCMS 비전 2026.04.20 라이선스 DXCMS 오픈소스 및 제3자 소프트웨어 저작권 공지 2026.04.20 라이선스 DXCMS 라이선스 (LGPL 3.0) 2026.04.20 1. DX 철학 / 개념 생태계 확장 전략 2026.04.20 1. DX 철학 / 개념 DXCMS가 지향하는 방향 (플랫폼 vs 단순 CMS) 2026.04.20 1. DX 철학 / 개념 프레임워크 + CMS 통합 구조의 의미 2026.04.20 1. DX 철학 / 개념 기존 CMS와의 구조적 한계 2026.04.20
31
전체 회원
503
전체 게시글
770
전체 댓글
441
오늘 방문
33,173
전체 방문
2
현재 접속
인기글 7일 이내
최신글
최신댓글
목록