회원가입 | 고객센터 |
DESIGNONEX
dxcms.kr
로그인 회원가입
고객센터
7. 테마

테마 구조

D DX
2026.05.01 01:35(수정됨) 119 0

1장. 테마 시스템 개요

DXCMS 테마는 사이트 전체의 레이아웃•디자인•기능을 정의하는 파일 모음입니다. themes/{테마명}/ 폴더 하나가 하나의 테마이며, 현재 활성 테마에 파일이 없으면 자동으로 default 테마 파일을 사용하는 2단계 폴백 체인이 핵심입니다. 이 구조 덕분에 원하는 파일만 오버라이드하는 최소한의 커스텀 테마를 만들 수 있습니다.


1.1 테마 vs 스킨 vs 플러그인

구분 테마 스킨 플러그인
위치 themes/{이름}/ boards/skins/{이름}/ plugins/{이름}/
역할 전체 레이아웃·디자인 게시판 화면만 기능 확장
적용 범위 사이트 전체 게시판 단위 선택 사이트 전체
필수 파일 theme.json + layout/main.php skin.json + list.php + view.php manifest.php + plugin.php
변경 방법 관리자 → 테마 관리 게시판 설정 → 스킨 선택 관리자 → 플러그인 관리


1.2 2단계 폴백 체인

DxTheme::resolve()는 항상 2단계로 파일을 탐색합니다:
1단계: themes/{현재테마}/{파일경로}  → 파일이 있으면 즉시 사용
2단계: themes/default/{파일경로}    → 없으면 기본 테마에서 폴백
결과:  둘 다 없으면 null 반환       → 화면에 404 또는 오류 박스

예시: 현재 테마 = "my-theme"
  resolve("board/list.php") 탐색 순서:
  1. themes/my-theme/board/list.php  → 없음
  2. themes/default/board/list.php   → 있음! ✅ 이 파일 사용

💡 핵심 전략
커스텀 테마를 만들 때 바꾸고 싶은 파일만 만들면 됩니다.
layout/main.php만 바꿔도 전체 레이아웃이 바뀝니다.
나머지(게시판, 검색, 홈페이지 등)는 default 테마가 자동으로 처리합니다.


2장. 테마 디렉토리 구조


2.1 전체 파일 트리

themes/
  default/                    ← 기본 테마 (항상 존재, 최종 폴백 기준)
  │  theme.json               ← 테마 메타 정보 (필수)
  │  style.css                ← 테마 공통 스타일시트
  │
  │  layout/
  │    main.php               ← 전체 레이아웃 (헤더+사이드바+본문+푸터) ★필수
  │    header.php             ← 헤더만 (선택)
  │    footer.php             ← 푸터만 (선택)
  │
  │  board/                   ← 게시판 스킨 (기본 테마 폴백용)
  │    list.php               ← 목록 (★필수)
  │    view.php               ← 상세 (★필수)
  │    write.php              ← 글쓰기/수정
  │    _list_rows.php         ← 목록 행 파셜
  │    style.css              ← 게시판 전용 CSS
  │    {스킨명}/              ← 게시판 스킨별 오버라이드
  │      list.php
  │      view.php
  │
  │  board_latest/            ← 최신글 위젯 스킨
  │    list.php               ← 기본 목록형 ★필수
  │    simple.php             ← 심플형
  │    card.php               ← 카드형
  │    {커스텀스킨}.php       ← 직접 추가 가능
  │
  │  page/                    ← 정적 페이지
  │    home.php               ← 홈페이지 (is_home=1인 페이지)
  │    404.php                ← 404 오류 페이지
  │    403.php                ← 403 오류 페이지
  │
  │  parts/                   ← 재사용 파셜
  │    pagination.php
  │    breadcrumb.php
  │
  │  search/
  │    list.php               ← 통합 검색 결과
  │
  │  icons/                   ← SVG 아이콘
  │
  └─ (assets/)               ← 선택: CSS/JS/이미지

  my-theme/                   ← 커스텀 테마 (바꾸고 싶은 파일만)
    theme.json                ← 필수
    layout/main.php           ← 필수 (레이아웃 전체 교체)
    board/list.php            ← 선택 (게시판 목록만 오버라이드)
    page/home.php             ← 선택 (홈페이지만 오버라이드)


2.2 파일별 역할과 필수 여부

파일/폴더 필수 여부 역할
theme.json 필수 테마 메타 정보. 없으면 폴더명이 이름으로 표시됨
layout/main.php 필수 전체 레이아웃. $dx_content 변수가 본문 콘텐츠를 받음
board/list.php 필수 게시판 목록 기본 뷰. 없으면 사이트 동작 불가
board/view.php 필수 게시글 상세 기본 뷰
board/write.php 권장 글쓰기/수정 폼. 없으면 default 테마 폴백
board_latest/list.php 권장 dx_board_latest() 함수의 기본 위젯 스킨
page/home.php 권장 홈페이지. 없으면 default 테마 home.php 사용
search/list.php 선택 통합 검색 결과. 없으면 default 폴백
parts/*.php 선택 파셜. dx_include_part()로 호출
style.css 선택 테마 공통 CSS
assets/ 선택 CSS/JS/이미지. dx_theme_asset()으로 URL 획득


3장. theme.json 완전 레퍼런스


3.1 theme.json 전체 형식

{
    "name": "My Theme",
    "version": "1.0.0",
    "author": "개발자명",
    "description": "테마 설명",
    "preview": "assets/preview.png",
    "supports": ["board", "page", "gallery", "qa"],
    "options": {
        "primary_color": {
            "type": "color",
            "label": "기본 색상",
            "default": "#1a73e8"
        },
        "logo_text": {
            "type": "text",
            "label": "로고 텍스트",
            "default": ""
        },
        "footer_desc": {
            "type": "text",
            "label": "푸터 설명",
            "default": ""
        }
    }
}


3.2 각 필드 상세

필드 설명
name 관리자 테마 목록에 표시될 테마 이름
version 테마 버전 (예: 1.0.0)
author 테마 제작자 이름
description 테마 설명 (관리자 화면에 표시)
preview 미리보기 이미지 경로 (테마 폴더 기준 상대 경로)
supports 테마가 지원하는 기능 배열. 예: ['board','page','gallery','qa']
options 관리자에서 설정 가능한 테마 옵션 정의. 키-값 객체


3.3 options 필드 타입

options의 각 항목은 관리자 → 테마 관리 → 옵션 편집 화면에서 입력 UI로 표시됩니다.
 
type 입력 UI 설명
color 색상 피커 16진수 색상 코드 (#RRGGBB). 예: primary_color
text 텍스트 입력란 단일 줄 문자열. 로고 텍스트, 푸터 문구 등
textarea 여러 줄 텍스트 긴 텍스트 입력. 푸터 설명, 저작권 문구 등
select 드롭다운 여러 선택지 중 하나. choices 배열로 선택지 정의
checkbox 체크박스 참/거짓 값 (on/off 설정)


3.4 options 값 읽기

theme.json의 options에 정의된 값은 관리자가 저장하면 settings 테이블에 theme_{테마명}_{키} 형식으로 저장됩니다. 테마 파일에서 아래 함수로 읽습니다:
// layout/main.php 등 테마 파일에서 사용
$primaryColor = dx_theme_option("primary_color", "#1a73e8");
$logoText     = dx_theme_option("logo_text", "");
$footerDesc   = dx_theme_option("footer_desc", "");

// 내부 저장 키 형식 (settings 테이블)
// theme_{테마명}_{옵션키}
// 예: theme_my-theme_primary_color → "#FF5733"

💡 default 테마 options 목록
primary_color    — 기본 색상 (CSS --p 변수에 적용)
logo_text        — 로고 텍스트 (비우면 site_name 사용)
footer_desc      — 푸터 설명 문구
company_name     — 상호명 (푸터 사업자 정보)
ceo_name         — 대표자명
biz_no           — 사업자번호
contact_email    — 연락처 이메일
contact_phone    — 연락처 전화번호
sns_instagram    — 인스타그램 URL
sns_github       — GitHub URL
footer_terms_url — 이용약관 URL
footer_privacy_url — 개인정보처리방침 URL


4장. layout/main.php 구조 상세

layout/main.php는 테마의 핵심 파일입니다. HTML 전체 골격을 담당하며, 헤더•내비게이션•사이드바•본문($dx_content)•푸터를 모두 포함합니다.

4.1 $dx_content — 본문 콘텐츠 주입

Dispatcher가 요청된 페이지(게시판, 홈, 정적 페이지 등)의 HTML을 ob_start()로 캡처한 후 $dx_content 변수에 담아 layout/main.php로 전달합니다. main.php는 원하는 위치에 이 변수를 출력합니다.
<!-- layout/main.php 본문 위치 (예시) -->
<main>
  <?php echo isset($dx_content) ? $dx_content : ""; ?>
</main>

<!-- 사이드바가 있을 때 (default 테마 방식) -->
<div style="display:flex;gap:22px">
  <div style="flex:1">
    <?php echo isset($dx_content) ? $dx_content : ""; ?>
  </div>
  <?php if ($_showSidebar): ?>
  <aside style="width:220px"><!-- 사이드바 --></aside>
  <?php endif; ?>
</div>


4.2 $context — 현재 페이지 컨텍스트

main.php에는 $context 배열도 함께 전달됩니다. 현재 페이지 유형과 게시판 정보에 따라 레이아웃을 동적으로 조정할 수 있습니다.
 
$context 값 및 설명
$context['type'] 'board' | 'home' | 'page' | 'search' | 'auth' | 'mypage'
$context['slug'] 게시판 키 또는 페이지 슬러그 (board_key 등)
$context['board'] type='board'일 때 게시판 설정 배열 (dx_boards 레코드)
$context['page'] type='page'일 때 페이지 설정 배열
$context['categories'] 게시판의 카테고리 배열 (사이드바 카테고리 탭용)
$context['currentCategory'] 현재 선택된 카테고리 이름
 
// layout/main.php에서 컨텍스트 활용 예시
$ctx = isset($context) ? $context : array();
$isBoard = isset($ctx["type"]) && $ctx["type"] === "board";
$bk      = isset($ctx["slug"]) ? $ctx["slug"] : "";

// 게시판 페이지에서만 사이드바 표시
if ($isBoard && !empty($ctx["categories"])) {
    // 카테고리 사이드바 렌더링...
}


4.3 CSS 변수 시스템 (다크모드 대응)

default 테마는 CSS Custom Properties(변수)로 색상과 배경을 관리합니다. body.dark 클래스가 추가되면 다크모드 변수로 자동 전환됩니다.
 
CSS 변수 라이트 기본값 다크 기본값 용도
--p #1a73e8 (동일) 주요 강조색. dx_theme_option("primary_color")로 변경
--bg-body #f1f3f5 #0f172a 페이지 전체 배경
--bg-card #ffffff #1e293b 카드·패널 배경
--bg-header #ffffff #1e293b 헤더 배경
--text-main #1e293b #f1f5f9 주요 텍스트 색
--text-muted #64748b #94a3b8 보조 텍스트 색
--border #e2e8f0 #334155 테두리·구분선
--hover-row #f8faff #243044 호버 시 배경
 
/* 커스텀 테마 CSS에서 활용 예시 */ .my-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    color: var(--text-main);
}
.my-title { color: var(--p); }

/* 다크모드 추가 스타일 */  body.dark .my-special { opacity: 0.8; }


4.4 훅 포인트

layout/main.php는 세 곳에 훅 포인트를 제공합니다. 플러그인과 테마가 이 훅으로 <head>•스크립트•푸터 영역에 코드를 삽입할 수 있습니다.
 
훅 이름 위치 및 용도
dx_head <head> 태그 안 — CSS, meta 태그, JSON-LD 스크립트 삽입용. $context 배열 전달
dx_footer_scripts </body> 직전 — 추가 JS 파일 삽입용
dx_body_bottom </body> 최하단 — 분석 스크립트, 채팅 위젯 등
 
// 플러그인에서 <head>에 CSS 삽입 예시
dx_add_hook("dx_head", function($context) {
    echo '<link rel="stylesheet" href="' . dx_base_url("plugins/my-plugin/style.css") . '">';
});

// 플러그인에서 </body> 직전에 JS 삽입
dx_add_hook("dx_body_bottom", function() {
    echo '<script src="' . dx_base_url("plugins/my-plugin/script.js") . '"></script>';
});


4.5 사이드바 자동 표시 조건

default 테마의 사이드바는 아래 두 조건 중 하나를 만족하면 자동으로 표시됩니다:
  • 서브메뉴 존재 — 현재 URL이 속한 상위 메뉴에 서브메뉴가 있을 때
  • 게시판 카테고리 — 현재 게시판에 카테고리가 설정되어 있을 때 카테고리 탭 표시
사이드바가 표시되면 본문과 사이드바가 flex 2컬럼 레이아웃이 되고, 없으면 본문이 전체 폭을 차지합니다.


4.6 메뉴 시스템

main.php는 DB에서 메뉴를 로드하여 헤더 내비게이션을 렌더링합니다. 메뉴는 120초 캐시가 적용됩니다.
 
변수 설명
$_nms 최상위 메뉴 배열 (parent_id=0)
$_allSubs 전체 서브메뉴 배열
$_subMap[부모ID] 각 최상위 메뉴의 하위 메뉴 배열 (사이드바용)
$_newBkSet 신규 글이 있는 board_key 집합 (메뉴 N 배지용)


5장. 폴백 체인 전체 상세


5.1 DxTheme::resolve() — 기본 파일 탐색

// 사용 예
$file = DxTheme::getInstance()->resolve("board/list.php");
// 또는 헬퍼 함수
$file = dx_theme_file("board/list.php");

// 탐색 순서:
// 1. themes/{현재테마}/board/list.php
// 2. themes/default/board/list.php
// 3. 없으면 null 반환


5.2 resolveBoardSkin() — 게시판 스킨 탐색

게시판 스킨(boards/skins/)에 파일이 없을 때 테마에서 탐색하는 경로입니다.
 
순위 경로 설명
1 themes/{현재테마}/board/{스킨명}/{액션}.php 현재 테마 스킨 전용 뷰
2 themes/{현재테마}/board/{액션}.php 현재 테마 공통 뷰
3 themes/default/board/{스킨명}/{액션}.php default 테마 스킨 전용 뷰
4 themes/default/board/{액션}.php default 테마 공통 뷰 (최종 폴백)


5.3 resolveLatestSkin() — board_latest 위젯 탐색

// dx_board_latest("free", 5, "card") 호출 시 탐색 순서:
1. themes/{현재테마}/board_latest/card.php
2. themes/default/board_latest/card.php
3. themes/default/board_latest/list.php  ← 최종 폴백 (항상 존재)


5.4 resolvePart() — 파셜 탐색

// dx_include_part("pagination", [...]) 호출 시 탐색 순서:
1. themes/{현재테마}/parts/pagination.php
2. themes/default/parts/pagination.php
3. 없으면 아무것도 출력 안 함 (조용히 실패)


6장. 테마 헬퍼 함수 완전 레퍼런스


6.1 DxTheme 전용 헬퍼

함수 시그니처 설명 및 반환값
dx_theme_file('board/list.php') 테마 파일 절대 경로 반환 (폴백 체인 적용). null 가능
dx_theme_asset('css/style.css') themes/{현재테마}/assets/ 기준 URL 반환
dx_theme_option('primary_color', '#1a73e8') theme.json options에 정의된 값 반환. 기본값 지정 가능
dx_include_part('pagination', ['total'=>100]) parts/ 파셜 파일 include. 두 번째 인자 변수가 파셜에서 사용 가능
DxTheme::getInstance()->getTheme() 현재 활성 테마명 반환 (예: 'default', 'my-theme')
DxTheme::getInstance()->meta() 현재 테마의 theme.json 배열 반환
DxTheme::getInstance()->allThemes() 설치된 모든 테마 목록 배열 반환 (관리자용)
DxTheme::reset() 싱글턴 리셋 (테마 변경 후 재초기화 시 사용)


6.2 board_latest 위젯 함수

dx_board_latest()는 특정 게시판의 최신글을 board_latest 스킨으로 렌더링합니다. 테마의 홈페이지나 사이드바에서 자주 사용합니다.
// 함수 시그니처
dx_board_latest(
    $boardKey,    // 게시판 키 (필수)
    $limit = 5,   // 표시할 글 수
    $skin = "list", // 스킨명 (list|simple|card|커스텀)
    $title = "",  // 섹션 제목 (비우면 게시판 이름 자동)
    $icon = "",   // 이모지 아이콘 (예: "📋")
    $titleLen = 0, // 제목 최대 길이 (0=무제한)
    $showExcerpt = false  // 내용 발췌 표시 여부
);

// 사용 예
<?php dx_board_latest("notice", 5, "simple", "공지사항", "📢"); ?>
<?php dx_board_latest("free", 10, "card", "자유게시판", "💬", 30, true); ?>
<?php dx_board_latest("gallery", 8, "card"); ?>


6.3 board_latest 스킨 파일 변수

board_latest/{스킨}.php 파일에서 사용 가능한 변수:
 
변수 설명
$board_key 게시판 키 문자열
$board_name 게시판 이름
$title 표시 제목 (지정 없으면 board_name)
$icon 이모지 아이콘 문자열
$more_url 더보기 URL (게시판 목록 URL)
$posts 최신글 배열 (아래 필드 포함)
$show_excerpt 내용 발췌 표시 여부 (bool)


6.4 $posts 배열 각 요소 필드

필드명 설명
id 게시글 ID
title 제목 (titleLen 적용 후)
author 작성자 이름
date 날짜 (Y-m-d 형식)
date_short 날짜 단축 (m.d 형식)
url 게시글 view URL
board_key 게시판 키
view_count 조회수 (int)
comment_count 댓글 수 (int)
excerpt 내용 발췌 (show_excerpt=true일 때, 최대 100자)


6.5 dx_board_posts() — 원시 데이터 조회

스킨 없이 최신글 배열만 필요할 때 사용합니다
$posts = dx_board_posts(
    "notice",  // 게시판 키
    5,         // 가져올 글 수
    false,     // 공지글 포함 여부
    30,        // 제목 최대 길이 (0=무제한)
    100        // 내용 발췌 길이 (0=발췌 없음)
);

foreach ($posts as $post) {
    echo $post["title"];     // 제목
    echo $post["url"];       // 게시글 URL
    echo $post["date"];      // 날짜
    echo $post["excerpt"];   // 내용 발췌
}


6.6 기타 테마 관련 전역 헬퍼

함수 설명
dx_is_login() 로그인 여부 확인 (bool). Auth::getInstance()->isLoggedIn() 단축
dx_is_admin() 관리자 여부 확인 (bool)
dx_base_url('path') 사이트 절대 URL 생성
dx_config('site_name', '기본값') 사이트 설정값 읽기
dx_csrf_token() CSRF 토큰 값 반환 (meta 태그용)
dx_run_hook('훅명', $args) 훅 실행 — 플러그인 연동


7장. board_latest 위젯 스킨 제작


7.1 기본 제공 스킨

스킨 파일 특징
list.php 기본 목록형. 제목·날짜·조회수를 한 줄로 표시. 더보기 링크 포함
simple.php 심플 목록형. 제목만 한 줄. 가장 가볍고 깔끔한 디자인
card.php 카드형. 썸네일 이미지 + 제목 + 메타 정보. 갤러리 게시판에 적합


7.2 커스텀 board_latest 스킨 만들기

themes/{현재테마}/board_latest/ 또는 themes/default/board_latest/ 폴더에 PHP 파일을 추가하면 새 스킨이 됩니다.
<?php
// themes/my-theme/board_latest/timeline.php
// 타임라인형 위젯 스킨 예시
if (!defined("DX_CMS")) exit;
?>
<div class="my-timeline">
  <div class="timeline-header">
    <?php if ($icon): ?><span><?php echo htmlspecialchars($icon, ENT_QUOTES); ?></span><?php endif; ?>
    <strong><?php echo htmlspecialchars($title, ENT_QUOTES, "UTF-8"); ?></strong>
    <a href="<?php echo htmlspecialchars($more_url, ENT_QUOTES); ?>">더보기</a>
  </div>
  <?php if (empty($posts)): ?>
  <p>글이 없습니다.</p>
  <?php else: ?>
  <ul>
    <?php foreach ($posts as $post): ?>
    <li>
      <span class="date"><?php echo $post["date_short"]; ?></span>
      <a href="<?php echo htmlspecialchars($post["url"], ENT_QUOTES); ?>">
        <?php echo htmlspecialchars($post["title"], ENT_QUOTES, "UTF-8"); ?>
      </a>
      <?php if ($post["comment_count"] > 0): ?>
      <span>[<?php echo $post["comment_count"]; ?>]</span>
      <?php endif; ?>
    </li>
    <?php endforeach; ?>
  </ul>
  <?php endif; ?>
</div>

<?php /* 호출: dx_board_latest("free", 5, "timeline", "최신글", "🕐"); */ ?>


8장. extend/ 폴더 시스템


8.1 extend/란

extend/ 폴더는 PHP 파일을 넣기만 하면 CMS가 자동으로 실행하는 "파일 기반 훅" 시스템입니다. 플러그인을 별도로 등록하지 않아도 되므로 빠른 커스텀 코드 삽입에 유용합니다.
 
폴더 실행 시점 사용 예
extend/top/ CMS 초기화 완료 직후 (DB·세션·인증 완료, 라우팅 전) 점검모드, IP 차단, 다크모드 FOUC 제거
extend/middle/ 라우트 확정 직후 (핸들러 실행 전) 권한 체크 추가, 요청 로깅
extend/bottom/ 렌더링 완료 후 (ob_end_flush 직전) 성능 로그, 분석 코드

파일명 오름차순으로 실행됩니다. 예: 01_darkmode.php → 02_maintenance.php → 03_custom.php
에러가 발생해도 다른 파일 실행에 영향을 주지 않습니다.


8.2 extend/ 실전 예시

점검 모드 (extend/top/99_maintenance.php)
<?php
// extend/top/99_maintenance.php
// 파일명을 .disabled 로 끝내면 무시됨
if (!defined("DX_CMS")) exit;

$isAdmin = function_exists("dx_is_admin") ? dx_is_admin() : false;
if (!$isAdmin) {
    http_response_code(503);
    echo "<!DOCTYPE html><html><body><h1>점검 중입니다</h1><p>잠시 후 다시 방문해 주세요.</p></body></html>";
    exit;
}

성능 로그 (extend/bottom/99_perf_log.php)
<?php
// extend/bottom/99_perf_log.php
if (!defined("DX_CMS")) exit;

$elapsed = microtime(true) - (defined("DX_START") ? DX_START : microtime(true));
dx_log("페이지 생성: " . round($elapsed * 1000) . "ms — " . $_SERVER["REQUEST_URI"], "warning");


9장. 커스텀 테마 제작 완전 가이드


9.1 최소 구조 (레이아웃만 교체)

가장 단순한 커스텀 테마는 theme.json + layout/main.php 두 파일만으로 구성됩니다. 나머지 파일(게시판, 검색, 홈페이지 등)은 default 테마가 자동으로 처리합니다.
themes/my-theme/
  ├── theme.json        ← 테마 메타 정보
  └── layout/main.php  ← 전체 레이아웃 교체


9.2 theme.json 작성

{
    "name": "나의 테마",
    "version": "1.0.0",
    "author": "나의 이름",
    "description": "미니멀 커스텀 테마",
    "supports": ["board", "page"],
    "options": {
        "primary_color": {
            "type": "color",
            "label": "주 색상",
            "default": "#6366f1"
        },
        "site_slogan": {
            "type": "text",
            "label": "사이트 슬로건",
            "default": "DXCMS로 만든 사이트"
        }
    }
}


9.3 layout/main.php 최소 구현

<?php if (!defined("DX_CMS")) exit; ?>
<?php
$siteName = dx_config("site_name", "내 사이트");
$primaryColor = dx_theme_option("primary_color", "#6366f1");
$slogan = dx_theme_option("site_slogan", "");
?>
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><?php echo htmlspecialchars($siteName, ENT_QUOTES); ?></title>
  <style>
    :root { --p: <?php echo htmlspecialchars($primaryColor, ENT_QUOTES); ?>; }
    body { margin: 0; font-family: sans-serif; background: #f8fafc; }
    header { background: var(--p); color: #fff; padding: 16px 24px; }
    main { max-width: 960px; margin: 24px auto; padding: 0 16px; }
    footer { text-align: center; padding: 24px; color: #64748b; font-size: .85rem; }
  </style>
  <meta name="csrf-token" content="<?php echo dx_csrf_token(); ?>">
  <?php dx_run_hook("dx_head", isset($context) ? $context : array()); ?>
</head>
<body>

<header>
  <a href="<?php echo dx_base_url(); ?>" style="color:#fff;text-decoration:none;font-size:1.2rem;font-weight:700">
    <?php echo htmlspecialchars($siteName, ENT_QUOTES); ?>
  </a>
  <?php if ($slogan): ?>
  <p style="margin:4px 0 0;font-size:.8rem;opacity:.8"><?php echo htmlspecialchars($slogan, ENT_QUOTES); ?></p>
  <?php endif; ?>
</header>

<main>
  <?php echo isset($dx_content) ? $dx_content : ""; ?>
</main>

<footer>
  <p>&copy; <?php echo date("Y"); ?> <?php echo htmlspecialchars($siteName, ENT_QUOTES); ?></p>
</footer>

<?php dx_run_hook("dx_footer_scripts", array()); ?>
<?php dx_run_hook("dx_body_bottom"); ?>
</body>
</html>


9.4 홈페이지 커스텀 (page/home.php)

is_home=1인 페이지가 요청될 때 themes/{테마}/page/home.php가 사용됩니다. dx_board_latest()로 최신글 위젯을 배치할 수 있습니다.
<?php if (!defined("DX_CMS")) exit; ?>

<!-- 히어로 섹션 -->
<div style="background:var(--p);color:#fff;padding:60px 24px;text-align:center;border-radius:16px;margin-bottom:32px">
  <h1 style="font-size:2rem;margin:0 0 12px">환영합니다!</h1>
  <p style="opacity:.85;margin:0">우리 사이트에 오신 것을 환영합니다.</p>
</div>

<!-- 최신글 위젯 2컬럼 -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:24px">
  <div>
    <?php dx_board_latest("notice", 5, "simple", "공지사항", "📢"); ?>
  </div>
  <div>
    <?php dx_board_latest("free", 10, "simple", "자유게시판", "💬"); ?>
  </div>
</div>

<!-- 갤러리 최신글 -->
<div style="margin-top:32px">
  <?php dx_board_latest("gallery", 8, "card", "갤러리", "🖼️"); ?>
</div>


9.5 부분 오버라이드 전략

전체 레이아웃을 유지하면서 특정 페이지만 바꾸는 방법입니다:
 
목표 파일 추가 위치
홈페이지 디자인만 교체 themes/my-theme/page/home.php
게시판 목록 레이아웃 교체 themes/my-theme/board/list.php
게시글 상세 레이아웃 교체 themes/my-theme/board/view.php
검색 결과 페이지 교체 themes/my-theme/search/list.php
최신글 위젯 스킨 추가 themes/my-theme/board_latest/{스킨}.php
파셜(페이지네이션 등) 교체 themes/my-theme/parts/{파셜명}.php
갤러리 스킨 오버라이드 themes/my-theme/board/gallery/list.php


10장. 관리자에서 테마 관리


10.1 테마 활성화

  1. themes/ 폴더에 테마 폴더 업로드 (theme.json + layout/main.php 필수)
  2. 관리자 → 테마 관리 메뉴 접속
  3. 설치된 테마 목록 확인 (themes/ 폴더를 자동 스캔)
  4. 원하는 테마의 "활성화" 버튼 클릭
  5. settings 테이블의 theme 키가 해당 테마 폴더명으로 저장됨
  6. DxTheme::reset() 호출로 캐시 초기화 및 즉시 적용


10.2 테마 옵션 편집

  1. 관리자 → 테마 관리 → 해당 테마의 "옵션 편집" 클릭
  2. theme.json의 options에 정의된 항목이 입력 UI로 표시됨
  3. 값 입력 후 "저장" 클릭
  4. settings 테이블에 theme_{테마명}_{키} 형식으로 저장
  5. 테마 파일에서 dx_theme_option()으로 즉시 읽기 가능


10.3 테마 저장 구조 (DB)

-- 활성 테마 저장
settings.setting_key = "theme"
settings.setting_value = "my-theme"

-- 테마 옵션 저장 형식
settings.setting_key = "theme_my-theme_primary_color"
settings.setting_value = "#FF5733"

settings.setting_key = "theme_my-theme_logo_text"
settings.setting_value = "나의 사이트"


10.4 테마 변경 후 주의사항

  • 캐시 초기화 — 테마 변경 시 DxTheme::reset() + DxCache 초기화가 자동 실행됨
  • 옵션 초기화 — 새 테마로 변경 후 이전 테마의 옵션은 DB에 남아있지만 미사용 상태
  • 게시판 스킨 유지 — 테마를 변경해도 각 게시판의 스킨 설정은 변경되지 않음
  • default 테마 주의 — default 테마 파일 수정 시 CMS 업데이트로 덮어씌워질 수 있음. 커스텀 테마 폴더를 따로 만들 것을 권장


11장. 테마 제작 자주 묻는 질문

Q1. 테마를 만들 때 반드시 필요한 파일은?

theme.json과 layout/main.php 두 파일이 필수입니다. 이 두 파일만 있어도 완전히 동작하는 테마가 됩니다. 게시판, 검색, 홈페이지 등 나머지는 default 테마가 자동으로 처리합니다.


Q2. default 테마 파일을 직접 수정해도 되나요?

동작은 하지만 권장하지 않습니다. DXCMS 업데이트 시 default 테마 파일이 덮어씌워질 수 있습니다. 새 폴더에 커스텀 테마를 만들고 바꾸고 싶은 파일만 추가하는 방식을 권장합니다.


Q3. 다크모드를 지원하는 테마를 만들려면?

CSS 변수(--bg-card, --text-main 등)를 사용하고 body.dark 클래스에 대한 다크 색상을 정의하면 됩니다. extend/top/01_darkmode_early.php의 FOUC 제거 스크립트도 함께 사용하면 깜빡임 없는 다크모드가 구현됩니다.


Q4. 게시판마다 다른 레이아웃을 쓰고 싶습니다.

테마는 사이트 전체에 적용됩니다. 게시판마다 다른 화면 구성이 필요하면 게시판 스킨(boards/skins/)을 사용하세요. 스킨은 게시판 단위로 설정 가능하며, 목록•뷰•글쓰기 화면을 완전히 교체할 수 있습니다.


Q5. board_latest 위젯에 내 스킨을 추가하려면?

themes/{현재테마}/board_latest/{스킨명}.php 또는 themes/default/board_latest/{스킨명}.php 파일을 만들면 즉시 사용 가능합니다. dx_board_latest("게시판키", 5, "{스킨명}") 형태로 호출합니다.


Q6. 테마 옵션(theme.json options)에 select나 checkbox도 되나요?

type: "select"와 type: "checkbox"를 사용할 수 있습니다. select는 choices 배열로 선택지를 정의하고, checkbox는 저장 값이 "on"/"off" 문자열입니다. 관리자 테마 옵션 편집 화면에서 자동으로 해당 UI가 표시됩니다.


Q7. 플러그인이 로드한 CSS/JS가 테마보다 먼저 실행되나요?

dx_head 훅은 layout/main.php의 <head> 내부에서 실행됩니다. 플러그인이 dx_add_hook("dx_head")에 CSS/JS를 등록하면 테마의 <head> 안에 삽입됩니다. 우선순위(세 번째 인자)로 실행 순서를 조정할 수 있습니다.

댓글0

로그인 후 댓글을 작성할 수 있습니다.
1. DX 철학 / 개념 왜 DXCMS를 만들었는가 2026.04.20 1. DX 철학 / 개념 DXCMS란 무엇인가 2026.04.20 DXCMS 활용 (CMS) DXCMS 날코딩•막코딩 완전 허용 2026.04.12
31
전체 회원
503
전체 게시글
775
전체 댓글
442
오늘 방문
33,174
전체 방문
3
현재 접속
인기글 7일 이내
최신글
최신댓글
목록