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

테마 구조

D DX
2026.05.01 01:35(수정됨) 118 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

로그인 후 댓글을 작성할 수 있습니다.
7. 테마 DXCMS 테마 개발 AI 프롬프트 스킬과 멀티사이트 체험 2026.05.23 7. 테마 테마 DX마켓 등록 2026.05.01 7. 테마 테마 제작 2026.05.01 7. 테마 테마 구조 2026.05.01
31
전체 회원
503
전체 게시글
775
전체 댓글
442
오늘 방문
33,174
전체 방문
3
현재 접속
인기글 7일 이내
최신글
최신댓글
목록