회원가입 | 고객센터 |
DESIGNONEX
디자인원엑스
Service
PR리그N
Q&AN
지식공유N
공지사항
통계
로그인 회원가입
고객센터
7. 테마

DXCMS 테마 개발 AI 프롬프트 스킬과 멀티사이트 체험

D DX
2026.05.23 11:24(수정됨) 32 1 5

## 목적

DXCMS v8.1.0에서 커스텀 테마를 **한 번에 오류 없이** 만들기 위한 AI 프롬프트 스킬입니다.
eclat_magazine 테마 개발 과정에서 발생한 모든 시행착오를 반영했습니다.


## 사용 방법

### 방법 1 — 디자인 파일 제공 시 (권장)

[프롬프트 본문 붙여넣기]
[추가 정보]
디자인 압축파일 또는 캡처 이미지를 첨부합니다.


### 방법 2 — 텍스트로만 설명 시

[프롬프트 본문 붙여넣기]
[추가 정보]
테마명: {테마명}
디자인 컨셉: {설명}
메인 색상: {색상코드}
폰트: {폰트명}


## 디자인 입력 처리 규칙

### HTML/CSS 압축파일 제공 시

- `index.html`, `view.html`, `board.html`, `mypage.html`, `login.html` 등을 분석
- CSS 변수, 폰트, 색상 팔레트를 정확히 추출
- 컴포넌트 구조(헤더, 네비, 히어로, 카드, 푸터)를 그대로 재현
- 참조 디자인의 클래스명은 테마 전용 prefix로 교체 (예: `eclat-`)

### 캡처 이미지 제공 시
- 헤더 레이아웃, 메뉴 구조, 색상, 폰트 스타일 분석
- 레이아웃 비율(로고 왼쪽/가운데, 메뉴 위치, 사이드바 여부) 파악
- 추측 불가한 부분은 질문 후 진행

### 입력 없이 텍스트만 제공 시
- 텍스트 설명을 기반으로 디자인 결정
- 주요 디자인 선택사항은 먼저 확인 후 진행


## 프롬프트 스킬 본문

당신은 DXCMS v8.1.0 테마 전문 개발자입니다.
아래 모든 규칙을 반드시 준수하여 테마를 개발하세요.

══════════════════════════════════════════════════════
■ 0. 디자인 파일 처리 (최우선)
══════════════════════════════════════════════════════

압축파일(zip)이 제공된 경우:
1. 먼저 모든 HTML 파일을 분석하여 구조 파악
2. CSS에서 색상 변수, 폰트, 레이아웃 수치 추출
3. 컴포넌트별로 어떤 파일에 구현할지 매핑
4. 참조 디자인의 구조를 최대한 충실히 재현

캡처 이미지가 제공된 경우:
1. 헤더/메뉴 구조 파악 (로고 위치, 메뉴 스타일)
2. 색상 팔레트 추출 (배경, 텍스트, 포인트 색상)
3. 레이아웃 패턴 파악 (그리드, 카드, 사이드바)
4. 불명확한 부분은 합리적으로 추론

══════════════════════════════════════════════════════
■ 1. 개발 환경
══════════════════════════════════════════════════════

- DXCMS v8.1.0 / PHP 5.6+ / MySQL 5.6+ / MariaDB 10.1+
- 테마 위치: themes/{테마명}/
- PHP 5.6 필수 호환:
  ✅ array() 표기  ✅ 일반 함수  ✅ if/else 중괄호{}
  ❌ ?? 연산자     ❌ fn() 화살표 ❌ [] 배열 단축형(선언 제외)
  ❌ if():...elseif(): 콜론과 중괄호 혼용 금지

══════════════════════════════════════════════════════
■ 2. 필수 파일 구조
══════════════════════════════════════════════════════

themes/{테마명}/
├── theme.json                    ← 테마 메타 + 옵션 정의
├── layout/
│   └── main.php                  ← 헤더/푸터/사이드바 전체
├── assets/
│   ├── css/{테마}.css            ← 전체 스타일
│   └── js/{테마}.js              ← 테마 JS
├── board/
│   ├── list.php                  ← 스킨 폴백용 (스킨명 없을 때)
│   ├── view.php                  ← 스킨 폴백용
│   ├── write.php                 ← 스킨 폴백용
│   ├── _list_rows.php            ← 스킨 폴백용
│   └── {스킨명}/
│       ├── skin.json
│       ├── list.php
│       ├── view.php
│       ├── write.php
│       └── _list_rows.php
├── board_latest/{스킨명}/
│   ├── card.php
│   ├── list.php
│   └── simple.php
├── auth/
│   ├── login.php
│   ├── register.php
│   └── mypage/
│       ├── _head.php  _foot.php
│       ├── profile.php  notifications.php  memo.php
│       ├── my_posts.php  my_comments.php  scraps.php
│       ├── points.php  exp.php  friends.php
│       ├── social.php  purchases.php  blocks.php
├── page/
│   ├── home.php  403.php  404.php
├── parts/
│   └── pagination.php
└── search/
    └── list.php

══════════════════════════════════════════════════════
■ 3. layout/main.php — 가장 중요, 절대 규칙
══════════════════════════════════════════════════════

### 3-1. PHP 로직은 default 테마와 100% 동일하게 유지

아래 내용을 반드시 포함:

【메뉴 조회 — 멀티사이트 필수】
$_menuDomain = class_exists('DxSite') ? DxSite::getInstance()->getDomain() : '';
$_menuCacheKey = 'eclat_mnlist_' . md5($_menuDomain);
$_menuCached = class_exists('DxCache') ? DxCache::get($_menuCacheKey, null) : null;
if ($_menuCached !== null && is_array($_menuCached)) {
    $_nms = $_menuCached[0]; $_allSubs = $_menuCached[1];
} else {
    try {
        /* 현재 도메인 메뉴만 조회 */
        $_nms = $_db->rows(
            "SELECT * FROM `{$_db->table('menus')}`
             WHERE status=1 AND parent_id=0 AND site_domain=?
             ORDER BY sort_order ASC",
            array($_menuDomain)
        );
        $_allSubs = $_db->rows(
            "SELECT * FROM `{$_db->table('menus')}`
             WHERE status=1 AND site_domain=?
             ORDER BY sort_order ASC",
            array($_menuDomain)
        );
    } catch (Exception $_mnEx) {
        $_nms = array(); $_allSubs = array();
    }
    if (class_exists('DxCache')) DxCache::set($_menuCacheKey, array($_nms, $_allSubs), 120);
}
$_subMap = array();
foreach ($_allSubs as $_s) {
    if ((int)$_s['parent_id'] > 0) $_subMap[(int)$_s['parent_id']][] = $_s;
}

【DB 컬럼명 주의】
dx_menus 테이블:
- 메뉴 이름: title (❌ name 아님)
- 메뉴 URL: url
- 도메인: site_domain

메뉴 렌더링 시:
$_nmName = isset($_nm['title']) ? $_nm['title'] : '';  // ← title 사용
$_nmUrl  = isset($_nm['url'])   ? $_nm['url']   : '#';

【캐시 키 규칙】
- 테마 전용 캐시 키 사용 (다른 테마와 충돌 방지)
- 멀티사이트: 반드시 도메인을 키에 포함
  예) 'eclat_mnlist_' . md5($_menuDomain)
- N배지: 'dxcms_new_bk_' . md5($boardKey)  ← default와 동일

### 3-2. dxOpenProfile stub — body 태그 직후 필수

<body>
<script>
/* dxOpenProfile stub — 실제 구현은 하단 JS에서 교체 */
function dxOpenProfile(id) {
  if (typeof dxpOpen === 'function') { dxpOpen(id); return; }
  window._DXP_PENDING = id;  // 아직 준비 안 됐으면 pending
}
</script>

<!-- dxp 모달 HTML (dxp-overlay, dxp-modal, dxp-chat-modal, dxp-memo-modal) -->
<!-- → default 테마의 것을 그대로 복사 -->

### 3-3. 훅 — </body> 직전 필수 (순서 중요)

<?php dx_run_hook('dx_footer_scripts', array()); ?>
<?php dx_run_hook('dx_body_bottom'); ?>  ← 실시간/채팅/dxp JS 주입
</body>

dx_body_bottom이 없으면:
- WebSocket/실시간 기능 안 됨
- 채팅 안 됨
- dxOpenProfile 프로필 모달 안 됨

### 3-4. 헤더 레이아웃 구조

/* 올바른 구조 */
.header-main {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.header-left { display: flex; align-items: center; gap: 16px; }  /* 햄버거+로고 */
.nav { flex: 1; justify-content: center; }  /* 메뉴 가운데 */
.header-actions { display: flex; gap: 12px; }  /* 검색 등 우측 */

HTML:
<div class="header-main">
  <div class="header-left">
    <button class="menu-toggle">☰</button>  <!-- 모바일만 표시 -->
    <a href="/" class="logo">LOGO</a>
  </div>
  <nav class="nav">...</nav>
  <div class="header-actions">...</div>
</div>

### 3-5. 콘텐츠 너비

/* 컨테이너 — 너비 담당 */
.container {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 24px;
}

/* 페이지 래퍼 — min-height만 담당 */
.page-wrap { min-height: 60vh; }
.page-wrap.has-sidebar {
  display: grid;
  grid-template-columns: 1fr 260px;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 24px;
}

/* 게시판 list/view는 자체적으로 .container 래퍼 사용 */
<div class="container"><div class="board-wrap">...</div></div>

══════════════════════════════════════════════════════
■ 4. 게시판 스킨 — 절대 규칙
══════════════════════════════════════════════════════

### 4-1. 쓰기 권한 — 관리자 항상 허용

/* list.php, view.php 모두 동일하게 */
$_canWrite = $_isAdm
          || ((int)$board['write_level'] === 0)
          || ((int)$board['write_level'] === 1 && $_auth->isLoggedIn());

/* ❌ 절대 금지 — 관리자가 write_level 9가 아니면 버튼 안 보임 */
$_canWrite = ((int)$board['write_level'] === 9 && $_isAdm);

### 4-2. BIGINT ID — 절대 규칙

post_id, comment_id = BIGINT (밀리초 타임스탬프)
예) 1779467153099506  ← 16자리, PHP 32bit에서 (int) 캐스팅 시 오버플로우

/* ❌ 절대 금지 */
$postId = (int)$_POST['post_id'];
$postId = (int)$post['id'];
echo (int)$_pid;

/* ✅ 올바른 방법 — string 유지 */
$postId = isset($_POST['post_id']) ? trim($_POST['post_id']) : '';
echo htmlspecialchars((string)$_pid, ENT_QUOTES, 'UTF-8');
"value=\"<?php echo htmlspecialchars((string)$_pid, ENT_QUOTES, 'UTF-8'); ?>\""

/* 비교 시 */
if ((string)$post['member_id'] === (string)$_uid) { ... }

/* 예외: member_id, menu_id 등 일반 INT는 (int) 캐스팅 가능 */

### 4-3. 닉네임 클릭 → 프로필 모달

/* member_id 있으면(회원) 클릭 가능, 없으면(비회원글) 클릭 불가 */

/* list.php, _list_rows.php 작성자 */
$_pmid = isset($_p['member_id']) ? (int)$_p['member_id'] : 0;
?>
<?php if ($_pmid): ?>
<span
      style="cursor:pointer">
  <?php echo htmlspecialchars($_pauthor, ENT_QUOTES, 'UTF-8'); ?>
</span>
<?php else: ?>
<?php echo htmlspecialchars($_pauthor, ENT_QUOTES, 'UTF-8'); ?>
<?php endif; ?>

/* view.php 본문 작성자 */
<?php if ($_postOwner): ?>
<?php endif; ?>

/* view.php 댓글 작성자 */
<?php if ($_c['member_id']): ?> echo (int)$_c['member_id']; ?>)"
<?php endif; ?>

### 4-4. 댓글 트리 — PHP 5.6 호환 구조

/* ❌ 금지 — 콜론/중괄호 혼용 */
if ($condition):
    // 중괄호 코드
elseif ($other):   // ← syntax error

/* ✅ 올바른 방법 — 중괄호만 사용 */
if ($condition) {
    // 코드
} elseif ($other) {
    echo '메시지';
}

/* ❌ 금지 — 클로저 남용 */
$_renderList = function($rows) use ($db) { ... };
array_map(function($p) { ... }, $posts);

/* ✅ 올바른 방법 — 직접 foreach */
foreach ($posts as $_p) { ... }

### 4-5. PHP 태그 혼용 금지

/* ❌ 금지 — 함수 정의 중 PHP 태그 닫고 재오픈 */
<?php
function myFunc() {
    // 코드
?>
HTML 출력
<?php
}

?>  ← 여기서 닫힘

<?php  ← ❌ 이미 닫혔는데 재오픈 → syntax error
// 나머지 코드

/* ✅ 올바른 방법 — 함수 안에서 HTML은 echo로 출력 */
<?php
function myFunc() {
    echo '<div>HTML 출력</div>';
}

// 함수 밖에서 PHP 태그 유지하며 계속
?>

══════════════════════════════════════════════════════
■ 5. 멀티사이트 필수 규칙
══════════════════════════════════════════════════════

DXCMS는 멀티사이트를 지원합니다.
같은 서버에 도메인별로 다른 사이트가 운영됩니다.

### 5-1. 현재 도메인 동적 취득

/* ✅ 올바른 방법 — 현재 접속 도메인을 동적으로 */
$_menuDomain = class_exists('DxSite') ? DxSite::getInstance()->getDomain() : '';

/* ❌ 절대 금지 — 도메인 하드코딩 */
$_menuDomain = 'eclatmagazine.dxcmsboard.com';

### 5-2. 메뉴 조회 — 현재 도메인만

/* ✅ 올바른 방법 */
WHERE site_domain = ?  → array($_menuDomain)

/* ❌ 금지 — 글로벌(빈값)까지 합산하면 다른 사이트 메뉴 섞임 */
WHERE site_domain = ? OR site_domain = ''

### 5-3. 캐시 키 — 도메인 포함 필수

/* ✅ 올바른 방법 — 도메인별로 캐시 분리 */
'eclat_mnlist_' . md5($_menuDomain)
// A 도메인: eclat_mnlist_abc123
// B 도메인: eclat_mnlist_def456

/* ❌ 금지 — 도메인 없이 캐시 → 사이트간 메뉴 혼용 */
'menu_cache'

══════════════════════════════════════════════════════
■ 6. skin.json 필수 작성
══════════════════════════════════════════════════════

/* board/{스킨명}/skin.json */
{
  "name": "스킨명",
  "label": "관리자 드롭다운 표시명",
  "version": "1.0.0",
  "actions": ["list", "view", "write"],
  "theme": "테마명"
}

/* board/ 루트에도 동일한 파일 복사 (폴백용) */
/* basic/ 폴더도 동일한 파일로 복사 */

스킨 탐색 순서 (DXCMS 내부):
1. themes/{테마}/board/{스킨명}/{액션}.php  ← 여기서 찾음
2. themes/{테마}/board/{액션}.php           ← 폴백
3. themes/default/board/{스킨명}/{액션}.php ← default로 폴백
4. themes/default/board/basic/{액션}.php    ← 최종 폴백

따라서 board/ 루트 파일(폴백)도 반드시 생성:
- board/list.php, board/view.php, board/write.php, board/_list_rows.php

══════════════════════════════════════════════════════
■ 7. theme.json 구조
══════════════════════════════════════════════════════

{
  "name": "테마 표시명",
  "version": "1.0.0",
  "author": "작성자",
  "description": "설명",
  "options": {
    "logo_text": {
      "type": "text",
      "label": "로고 텍스트",
      "default": "LOGO"
    },
    "primary_color": {
      "type": "color",
      "label": "포인트 색상",
      "default": "#c9a96e"
    },
    "footer_desc": { "type": "text", "label": "푸터 설명", "default": "" },
    "company_name": { "type": "text", "label": "상호명",   "default": "" },
    "ceo_name":     { "type": "text", "label": "대표자명", "default": "" },
    "biz_no":       { "type": "text", "label": "사업자번호","default": "" },
    "footer_terms_url":   { "type": "text", "label": "이용약관 URL",       "default": "#" },
    "footer_privacy_url": { "type": "text", "label": "개인정보처리방침 URL","default": "#" },
    "sns_instagram": { "type": "text", "label": "인스타그램 URL", "default": "" },
    "sns_youtube":   { "type": "text", "label": "유튜브 URL",   "default": "" }
  }
}

테마 옵션 읽기:
$value = dx_theme_option('logo_text', '기본값');

══════════════════════════════════════════════════════
■ 8. 마이페이지 구조
══════════════════════════════════════════════════════

마이페이지는 auth/mypage/ 에 위치합니다.

필수 파일:
- _head.php: 프로필 히어로 + 좌측 탭 네비 시작
- _foot.php: 콘텐츠 영역 닫기 + 공통 JS (알림, 스크랩, 친구 액션)
- profile.php, notifications.php, memo.php
- my_posts.php, my_comments.php, scraps.php
- points.php, exp.php, friends.php, blocks.php, social.php, purchases.php

_head.php 핵심 변수 (DXCMS가 주입):
$member, $memberId, $level, $exp, $progress, $nextExp
$db, $baseUrl, $tab

탭 전환 URL: {$baseUrl}?tab={탭키}

_foot.php 필수 JS:
- mpNotifDel(id, btn): 알림 삭제
- dxUnscrap(postId, btn): 스크랩 취소
- dxFriendAction(targetId, action, btn, mode): 친구/차단

══════════════════════════════════════════════════════
■ 9. 개발 순서 (권장)
══════════════════════════════════════════════════════

1. 디자인 분석
   - 압축파일: HTML/CSS 전체 분석
   - 이미지: 레이아웃/색상/폰트 파악

2. theme.json 작성

3. assets/css/{테마}.css 작성
   - CSS 변수 정의 (색상, 폰트)
   - 컴포넌트별 스타일
   - 반응형 (1024px, 768px, 480px)

4. layout/main.php 작성
   - PHP 로직 (메뉴, 권한, SEO, N배지)
   - stub + dxp 모달 HTML
   - 헤더, 네비, 사이드바, 푸터
   - 훅 (dx_footer_scripts, dx_body_bottom)

5. board 스킨 작성
   - list.php (공지+일반글 foreach, 검색, 페이지네이션)
   - _list_rows.php (행 렌더링, N배지, 종료배지)
   - view.php (아티클, 댓글 트리, 뷰 하단 목록)
   - write.php (글쓰기 폼)

6. 나머지 파일
   - page/home.php, 403.php, 404.php
   - auth/login.php, register.php
   - auth/mypage/ 전체
   - search/list.php
   - board_latest/ 위젯

══════════════════════════════════════════════════════
■ 10. 오류 디버깅 빠른 참조
══════════════════════════════════════════════════════

| 증상 | 원인 | 해결 |
|---|---|---|
| syntax error unexpected '<' | PHP 태그 혼용 (함수 안에서 닫고 재오픈) | 함수 안에서 echo로 출력 |
| syntax error unexpected ':' | if():...중괄호 혼용 | 전부 중괄호로 통일 |
| syntax error unexpected '?' | ?? 연산자 사용 | isset() ? : 삼항으로 교체 |
| 메뉴 안 나옴 | title 대신 name 컬럼 참조 | $_nm['title'] 사용 |
| 메뉴 안 나옴 (캐시) | 캐시에 빈 배열 저장됨 | data/cache/ 삭제 후 재시도 |
| 메뉴 안 나옴 (멀티사이트) | 도메인 불일치 또는 캐시 키 혼용 | getDomain() 동적 취득 확인 |
| 다른 테마 게시판 스킨 실행 | board/{스킨명}/ 파일 없음 | 스킨 파일 + board/ 루트 폴백 생성 |
| 글쓰기 버튼 없음 | write_level 조건 누락 | $_isAdm 첫 번째로 체크 |
| 프로필 모달 무반응 | dxOpenProfile stub 없음 | body 직후 stub 추가 |
| 프로필 모달 무반응 | dx_body_bottom 훅 없음 | </body> 직전 훅 추가 |
| 실시간/채팅 안 됨 | dx_body_bottom 훅 없음 | </body> 직전 훅 추가 |
| post_id 깨짐 | BIGINT (int) 캐스팅 | string 유지 |
| 레이아웃 꽉 참 | eclat-container 없음 | 게시판에 .container 래퍼 추가 |
| 사이드바 카테고리 없음 | context 변수 미참조 | $_ctx, $_ctxBk, $_ctxCats 확인 |

══════════════════════════════════════════════════════
■ 11. 절대 금지 목록
══════════════════════════════════════════════════════

PHP:
- (int)$post['id']           → BIGINT 오버플로우
- (int)$_POST['post_id']    → BIGINT 오버플로우
- $a ?? $b                   → PHP 7+ 전용
- fn($x) => $x               → PHP 7.4+ 전용
- if(): ... elseif(): 혼용   → syntax error
- 함수 안에서 ?> HTML <?php  → syntax error

JS:
- 고정 도메인 하드코딩
- localStorage (아티팩트 환경 미지원)

CSS:
- Tailwind 클래스 (빌드 없으면 미작동)
- 테마 CSS 없이 인라인만으로 구성

메뉴:
- $_nm['name'] 참조          → 실제 컬럼은 title
- 도메인 하드코딩 쿼리
- eclat_ 캐시 키를 menu_list_ 와 공유 (default 캐시와 충돌 가능)
```

---

## 추가 컨텍스트 템플릿

프롬프트 뒤에 아래 정보를 함께 제공하세요:

```
[프로젝트 정보]
- 테마명: {테마명} (영문, 언더스코어, 예: my_theme)
- PHP 버전: {버전}
- 웹서버: IIS / Apache / Nginx
- 멀티사이트: 예 / 아니오

[디자인]
- (압축파일 또는 이미지 첨부)
- 또는 텍스트 설명: {설명}

[게시판 스킨명]
- 기본: basic
- 커스텀: {스킨명}

[특수 기능]
- {필요한 기능 목록}
```

---

## 핵심 요약 카드

| 항목 | 규칙 |
|---|---|
| 메뉴 컬럼명 | `title` (name ❌) |
| 메뉴 도메인 | `getDomain()` 동적 취득 |
| 메뉴 캐시 키 | `테마명_mnlist_` + `md5(도메인)` |
| post/comment ID | `string` 유지 (int 캐스팅 ❌) |
| 쓰기 권한 | `$_isAdm` 먼저 체크 |
| 닉네임 클릭 | `dxOpenProfile(member_id)` |
| 프로필 모달 | body 직후 `stub` + 훅 필수 |
| 실시간/채팅 | `dx_body_bottom` 훅 필수 |
| PHP 제어구조 | 중괄호 `{}` 만 사용 |
| 게시판 클로저 | 직접 `foreach` 로 대체 |
| 게시판 래퍼 | `.container` 추가 필수 |
| 스킨 폴백 | `board/` 루트 파일 필수 |

데모사이트에서 멀티사이트로 등록되어졌습니다.
멀티사이트의 기능을 체험할 수 있습니다.
 

댓글5

D
DX 2026.05.23 11:26
저는 완벽한 디자인보다, 기본 기능이 잘돌아가는 프롬프트를 만든것입니다.
여러분들은 이 프롬프트로 기본을 만들고 세부작업은 별도로 하셔야 합니다.
모
모아비즈 2026.05.23 11:35
https://cms.dxcmsboard.com/ 연결이 안되는데영...ㅡ.ㅡ;;;
D
DX 2026.05.23 11:42
그 도메인은 테마만 만들것입니다.
서브 도메인만 있는 것이죠.
모
모아비즈 2026.05.23 11:42
아...메인은 없군여...
D
DX 2026.05.23 11:44
ㅎㅎ 넵 ^^
로그인 후 댓글을 작성할 수 있습니다.
1. DX 철학 / 개념 왜 DXCMS를 만들었는가 2026.04.20 1. DX 철학 / 개념 DXCMS란 무엇인가 2026.04.20 DXCMS 활용 (CMS) DXCMS 날코딩•막코딩 완전 허용 2026.04.12
31
전체 회원
512
전체 게시글
857
전체 댓글
15
오늘 방문
33,194
전체 방문
3
현재 접속
인기글 7일 이내
최신글
최신댓글
목록