
1. 개요 및 특징
DX 시도/시군구/동 선택 플러그인은 DXCMS 게시판 글쓰기 폼에 시/도 → 시/군/구 → 동/읍/면 3단계 연동 셀렉트 박스를 자동으로 삽입합니다. 선택된 값은 DXCMS 여분필드(BoardFields) 시스템에 저장되며, 뷰와 리스트 스킨에서 수동으로 가져와 표시합니다.• 3단계 연동 셀렉트 — 시/도 선택 → 시/군/구 자동 로드 → 동/읍/면 자동 로드
• 단계 수 선택 — 1단계(시/도만) / 2단계(시/도+시군구) / 3단계(전체) 게시판별 독립 설정
• 필수 입력 설정 — ON 시 시/도 미선택 상태에서 글 등록 차단
• 글 수정 자동 복원 — 수정 폼 진입 시 기존 선택값 자동 복원 (AJAX)
• 여분필드 자동 관리 — 활성화 시 sido1/sido2/sido3 여분필드 자동 생성
• 검색 가능 — 여분필드 검색으로 지역 필터링
• PHP 5.6+ 호환 — 외부 라이브러리 없음
데이터 흐름 요약
글쓰기 폼에서 지역 선택
↓
bf_sido1, bf_sido2, bf_sido3 로 POST 전송
↓
BoardFields가 post_meta 테이블에 자동 저장
field_key: sido1 / sido2 / sido3
↓
뷰/리스트 스킨에서 수동으로 BoardFields 또는 직접 post_meta 조회
2. 설치 방법
파일 구조
plugins/
dx-sido-select/
manifest.php ← 플러그인 메타 정보
plugin.php ← 핵심 로직 (훅, AJAX, 관리자)
data/
sido_data.php ← 시/도/시군구/동 전체 데이터
설치 절차
1. plugins/dx-sido-select/ 폴더를 DXCMS 루트의 plugins/ 폴더에 복사2. 관리자 → 플러그인 → DX 시도/시군구/동 선택 → 활성화
3. 관리자 사이드바 → 레이아웃 → 시도 선택 필드 메뉴 확인
4. 설정 화면에서 게시판별 활성화 및 단계 설정
5. 글쓰기 폼에서 셀렉트가 표시되는지 확인
3. 관리자 화면 사용법
관리자 사이드바 → 레이아웃 → 시도 선택 필드 접속게시판별 설정
| 설정 항목 | 설명 |
| 활성 (토글) | 해당 게시판에서 시도 선택 필드 ON/OFF |
| 단계 수 | 1단계(시/도만) / 2단계(시/도+시군구) / 3단계(전체) |
| 필수 입력 | 체크 시 시/도 미선택 상태에서 글 등록 차단 |
단계 수 안내
| 단계 | 생성되는 여분필드 | 설명 |
| 1단계 | sido1 | 시/도만 선택 |
| 2단계 | sido1, sido2 | 시/도 + 시/군/구 선택 |
| 3단계 | sido1, sido2, sido3 | 시/도 + 시/군/구 + 동/읍/면 선택 |
저장 시 자동 처리
• 활성화 → 설정한 단계 수만큼 여분필드(sido1/sido2/sido3) 자동 생성• 단계 수 변경 → 기존 여분필드 삭제 후 새 단계에 맞게 재생성
• 비활성화 → 여분필드 삭제 (DB 데이터는 유지)
4. 글쓰기 / 글 수정 동작
4-1. 글쓰기
게시판 글쓰기 폼에서 공지/비밀글 체크박스 아래에 지역 셀렉트가 자동으로 삽입됩니다.• 시/도 선택 → AJAX로 시/군/구 목록 자동 로드
• 시/군/구 선택 → AJAX로 동/읍/면 목록 자동 로드 (3단계 설정 시)
• 선택값은 bf_sido1, bf_sido2, bf_sido3 이름으로 폼과 함께 전송
• BoardFields 시스템이 post_meta 테이블에 자동 저장
4-2. 글 수정
글 수정 폼 진입 시 기존 선택값이 자동으로 복원됩니다.• post_meta 테이블에서 sido1/sido2/sido3 값을 조회
• sido1 값으로 시/도 선택 자동 설정
• AJAX로 시/군/구 목록 로드 후 sido2 값 자동 선택
• AJAX로 동/읍/면 목록 로드 후 sido3 값 자동 선택
// 올바른 방법
$postId = (string)$editPost['id'];
// 잘못된 방법 — 18자리 숫자가 잘려 수정 시 기존값 복원 안 됨
$postId = (int)$editPost['id']; // ← 절대 사용 금지
5. DB 저장 구조
선택된 지역 값은 DXCMS 기본 post_meta 테이블에 저장됩니다.post_meta 테이블 저장 예시
| id | post_id | field_key | value |
| 19 | 1784825082393178 (문자열) | sido1 | 서울 |
| 20 | 1784825082393178 (문자열) | sido2 | 강남구 |
| 21 | 1784825082393178 (문자열) | sido3 | 역삼동 |
settings 테이블 저장 형식
| setting_key | setting_value | 의미 |
| dxsido_board_{게시판ID} | 3r | 3단계 + 필수 입력 |
| dxsido_board_{게시판ID} | 2 | 2단계 + 선택 입력 |
| dxsido_board_{게시판ID} | 1r | 1단계 + 필수 입력 |
6. 뷰(view) 스킨에서 데이터 가져오기 ★
뷰 스킨에서 지역 정보를 표시하려면 수동으로 가져와야 합니다. 3가지 방법을 제공합니다.방법 A — BoardFields 시스템 사용 (권장)
BoardFields를 require하면 DXCMS가 자동으로 해당 게시글의 여분필드 값을 모두 로드합니다.view.php — BoardFields 로드
<?php
/**
* 게시판 뷰 스킨 — view.php
* BoardFields를 통해 여분필드(sido1/sido2/sido3) 가져오기
*/
// ① BoardFields 인스턴스 생성
// $board, $post 변수는 DXCMS가 스킨에 주입해줌
if (function_exists('dx_board_fields')) {
$bf = dx_board_fields();
$fields = $bf->getValues($post['id']);
// $fields = array(
// 'sido1' => '서울',
// 'sido2' => '강남구',
// 'sido3' => '역삼동',
// ... 기타 여분필드
// )
} else {
$fields = array();
}
// ② 값 추출
$sido1 = isset($fields['sido1']) ? $fields['sido1'] : '';
$sido2 = isset($fields['sido2']) ? $fields['sido2'] : '';
$sido3 = isset($fields['sido3']) ? $fields['sido3'] : '';
// ③ HTML 출력
if ($sido1):
echo '<div class="post-location">';
echo '<i class="fa-solid fa-location-dot"></i> ';
echo htmlspecialchars($sido1, ENT_QUOTES, 'UTF-8');
if ($sido2) echo ' › ' . htmlspecialchars($sido2, ENT_QUOTES, 'UTF-8');
if ($sido3) echo ' › ' . htmlspecialchars($sido3, ENT_QUOTES, 'UTF-8');
echo '</div>';
endif;
?>
방법 B — post_meta 직접 조회
BoardFields를 사용하지 않고 post_meta 테이블을 직접 조회하는 방법입니다.
<?php
// post_meta 직접 조회
$db = Database::getInstance();
$tblMeta = $db->table('post_meta');
// ⚠ 반드시 (string) 캐스팅 — post_id는 18자리
$postId = (string)$post['id'];
$metaRows = $db->rows(
"SELECT field_key, value FROM `{$tblMeta}`
WHERE post_id = ? AND field_key IN ('sido1','sido2','sido3')",
array($postId)
);
$sido1 = ''; $sido2 = ''; $sido3 = '';
foreach ($metaRows as $mr) {
if ($mr['field_key'] === 'sido1') $sido1 = $mr['value'];
if ($mr['field_key'] === 'sido2') $sido2 = $mr['value'];
if ($mr['field_key'] === 'sido3') $sido3 = $mr['value'];
}
// 출력
if ($sido1):
echo '<span class="location">';
echo htmlspecialchars($sido1, ENT_QUOTES, 'UTF-8');
if ($sido2) echo ' ' . htmlspecialchars($sido2, ENT_QUOTES, 'UTF-8');
if ($sido3) echo ' ' . htmlspecialchars($sido3, ENT_QUOTES, 'UTF-8');
echo '</span>';
endif;
?>
방법 C — 여분필드 배열 직접 접근 ($extraFields)
일부 스킨에서는 DXCMS가 $extraFields 배열을 자동으로 주입합니다. 이 경우 바로 사용 가능합니다.
<?php
// $extraFields 배열이 스킨에 자동 주입된 경우
// (스킨의 handler.php 또는 render.php에서 확인 필요)
$sido1 = isset($extraFields['sido1']) ? $extraFields['sido1'] : '';
$sido2 = isset($extraFields['sido2']) ? $extraFields['sido2'] : '';
$sido3 = isset($extraFields['sido3']) ? $extraFields['sido3'] : '';
?>
뷰 출력 HTML 예시
<!-- 기본 텍스트 출력 -->
<?php if ($sido1): ?>
<div class="post-region">
<span class="label">지역</span>
<span class="value">
<?php echo htmlspecialchars($sido1, ENT_QUOTES, 'UTF-8'); ?>
<?php if ($sido2) echo ' › ' . htmlspecialchars($sido2, ENT_QUOTES, 'UTF-8'); ?>
<?php if ($sido3) echo ' › ' . htmlspecialchars($sido3, ENT_QUOTES, 'UTF-8'); ?>
</span>
</div>
<?php endif; ?>
<!-- 뱃지 스타일 출력 -->
<?php if ($sido1): ?>
<div class="region-badges">
<span class="badge sido1"><?php echo htmlspecialchars($sido1, ENT_QUOTES, 'UTF-8'); ?></span>
<?php if ($sido2): ?>
<span class="badge sido2"><?php echo htmlspecialchars($sido2, ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
<?php if ($sido3): ?>
<span class="badge sido3"><?php echo htmlspecialchars($sido3, ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
</div>
<?php endif; ?>
7. 리스트(list) 스킨에서 데이터 가져오기 ★
게시글 목록(list)에서 각 행에 지역 정보를 표시하려면 게시글 목록 조회 후 별도로 post_meta를 가져와야 합니다.방법 A — 목록 조회 후 일괄 IN 조회 (권장)
게시글 목록을 가져온 후, 모든 post_id를 모아서 post_meta를 한 번에 조회합니다.
<?php
/**
* 리스트 스킨 — list.php
* 게시글 목록에서 sido1/sido2/sido3 일괄 조회
*/
// ① $posts 배열이 이미 있다고 가정
// (DXCMS 스킨에서 $posts 는 자동 주입됨)
// ② 모든 post_id 수집 — 반드시 string으로 처리
$postIds = array();
foreach ($posts as $p) {
$postIds[] = (string)$p['id']; // ⚠ (string) 필수
}
// ③ sido 데이터 일괄 조회 (IN 절 — 쿼리 1번)
$sidoMap = array(); // [ post_id => ['sido1'=>...,'sido2'=>...,'sido3'=>...] ]
if (!empty($postIds)) {
$db = Database::getInstance();
$tblMeta = $db->table('post_meta');
// IN 절 플레이스홀더 생성
$ph = implode(',', array_fill(0, count($postIds), '?'));
$params = array_merge($postIds, array('sido1','sido2','sido3'));
$metaRows = $db->rows(
"SELECT post_id, field_key, value FROM `{$tblMeta}`
WHERE post_id IN ({$ph})
AND field_key IN ('sido1','sido2','sido3')",
$params
);
foreach ($metaRows as $mr) {
$pid = (string)$mr['post_id']; // ⚠ (string) 필수
if (!isset($sidoMap[$pid])) {
$sidoMap[$pid] = array('sido1'=>'','sido2'=>'','sido3'=>'');
}
$sidoMap[$pid][$mr['field_key']] = $mr['value'];
}
}
// ④ 목록 출력 시 $sidoMap에서 꺼내 사용
foreach ($posts as $p):
$pid = (string)$p['id']; // ⚠ (string) 필수
$sido = isset($sidoMap[$pid]) ? $sidoMap[$pid] : array();
$sido1 = isset($sido['sido1']) ? $sido['sido1'] : '';
$sido2 = isset($sido['sido2']) ? $sido['sido2'] : '';
$sido3 = isset($sido['sido3']) ? $sido['sido3'] : '';
?>
<li>
<a href="..."><?php echo htmlspecialchars($p['title'], ENT_QUOTES, 'UTF-8'); ?></a>
<?php if ($sido1): ?>
<span class="region">
<?php echo htmlspecialchars($sido1, ENT_QUOTES, 'UTF-8'); ?>
<?php if ($sido2) echo ' ' . htmlspecialchars($sido2, ENT_QUOTES, 'UTF-8'); ?>
</span>
<?php endif; ?>
</li>
<?php endforeach; ?>
방법 B — BoardFields getValuesBulk 사용 (지원 시)
DXCMS 버전에 따라 BoardFields에 일괄 조회 메서드가 있을 수 있습니다.
<?php
// BoardFields 일괄 조회 (DXCMS 8.7+ 지원 여부 확인 후 사용)
if (function_exists('dx_board_fields')) {
$bf = dx_board_fields();
$postIds = array_map(function($p){ return (string)$p['id']; }, $posts);
// getValuesBulk 메서드 존재 여부 확인
if (method_exists($bf, 'getValuesBulk')) {
$allFields = $bf->getValuesBulk($postIds);
// $allFields = [ post_id => [ 'sido1'=> ..., 'sido2'=>... ] ]
}
}
// 지원하지 않는 경우 방법 A (IN 절 직접 조회) 사용
?>
목록 출력 HTML 예시
<!-- 테이블 형태 목록 -->
<table>
<thead>
<tr>
<th>제목</th>
<th>지역</th>
<th>작성일</th>
</tr>
</thead>
<tbody>
<?php foreach ($posts as $p):
$pid = (string)$p['id'];
$sido = isset($sidoMap[$pid]) ? $sidoMap[$pid] : array();
$sido1 = isset($sido['sido1']) ? $sido['sido1'] : '';
$sido2 = isset($sido['sido2']) ? $sido['sido2'] : '';
?>
<tr>
<td><a href="..."><?php echo htmlspecialchars($p['title'], ENT_QUOTES, 'UTF-8'); ?></a></td>
<td>
<?php if ($sido1): ?>
<span class="badge-region"><?php echo htmlspecialchars($sido1, ENT_QUOTES, 'UTF-8'); ?></span>
<?php if ($sido2) echo '<span class="badge-region">' . htmlspecialchars($sido2, ENT_QUOTES, 'UTF-8') . '</span>'; ?>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<td><?php echo $p['created_at']; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
8. 검색 필터 사용법
sido1/sido2/sido3 여분필드는 is_search=1로 생성되어 DXCMS 게시판 검색 기능에서 필터링 가능합니다.8-1. DXCMS 기본 검색 URL
// 시/도 검색
/게시판키/list?search_key=sido1&search_val=서울
// 시/군/구 검색
/게시판키/list?search_key=sido2&search_val=강남구
// 동/읍/면 검색
/게시판키/list?search_key=sido3&search_val=역삼동
8-2. 목록 스킨에서 지역 검색 폼 직접 구현
<!-- 시/도 필터 셀렉트 -->
<form method="get">
<select name="search_val">
<option value="">전체 지역</option>
<option value="서울" <?php echo (isset($_GET['search_val']) && $_GET['search_val']==="서울") ? 'selected' : ''; ?>>서울</option>
<option value="경기">경기</option>
<option value="인천">인천</option>
<!-- ... -->
</select>
<input type="hidden" name="search_key" value="sido1">
</form>
9. 실전 예제 모음
예제 1 — 뷰에서 지역 뱃지 표시 (Bootstrap 스타일)
<?php
// view.php 상단
if (function_exists('dx_board_fields')) {
$bf = dx_board_fields();
$fields = $bf->getValues((string)$post['id']);
$sido1 = isset($fields['sido1']) ? $fields['sido1'] : '';
$sido2 = isset($fields['sido2']) ? $fields['sido2'] : '';
$sido3 = isset($fields['sido3']) ? $fields['sido3'] : '';
}
?>
<!-- 출력 -->
<?php if (!empty($sido1)): ?>
<div class="d-flex gap-1 mb-3">
<span class="badge bg-primary"><?php echo htmlspecialchars($sido1, ENT_QUOTES, 'UTF-8'); ?></span>
<?php if (!empty($sido2)): ?>
<span class="badge bg-secondary"><?php echo htmlspecialchars($sido2, ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
<?php if (!empty($sido3)): ?>
<span class="badge bg-light text-dark"><?php echo htmlspecialchars($sido3, ENT_QUOTES, 'UTF-8'); ?></span>
<?php endif; ?>
</div>
<?php endif; ?>
예제 2 — 리스트에서 카드 형태로 지역 표시
<?php
// list.php — 카드형 목록
// 게시글 목록 $posts 에서 post_id 수집
$postIds = array_map(function($p){ return (string)$p['id']; }, $posts);
$sidoMap = array();
if (!empty($postIds)) {
$db = Database::getInstance();
$tblMeta = $db->table('post_meta');
$ph = implode(',', array_fill(0, count($postIds), '?'));
$params = array_merge($postIds, array('sido1','sido2'));
$rows = $db->rows(
"SELECT post_id, field_key, value FROM `{$tblMeta}`
WHERE post_id IN ({$ph}) AND field_key IN ('sido1','sido2')",
$params
);
foreach ($rows as $r) {
$pid = (string)$r['post_id'];
if (!isset($sidoMap[$pid])) $sidoMap[$pid] = array();
$sidoMap[$pid][$r['field_key']] = $r['value'];
}
}
?>
<!-- 카드 목록 출력 -->
<div class="post-grid">
<?php foreach ($posts as $p):
$pid = (string)$p['id'];
$sido = isset($sidoMap[$pid]) ? $sidoMap[$pid] : array();
$sido1 = isset($sido['sido1']) ? $sido['sido1'] : '';
$sido2 = isset($sido['sido2']) ? $sido['sido2'] : '';
?>
<div class="post-card">
<h3 class="post-title">
<a href="..."><?php echo htmlspecialchars($p['title'], ENT_QUOTES, 'UTF-8'); ?></a>
</h3>
<?php if ($sido1): ?>
<div class="post-meta">
<i class="fa-solid fa-map-pin"></i>
<span><?php echo htmlspecialchars($sido1, ENT_QUOTES, 'UTF-8'); ?></span>
<?php if ($sido2) echo '<span class="sep">›</span><span>' . htmlspecialchars($sido2, ENT_QUOTES, 'UTF-8') . '</span>'; ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
예제 3 — 특정 지역 게시글만 조회 (직접 쿼리)
<?php
// 서울 강남구 게시글만 가져오기
$db = Database::getInstance();
$tblPost = $db->table('posts');
$tblMeta = $db->table('post_meta');
$sidoPosts = $db->rows(
"SELECT p.*, m1.value AS sido1, m2.value AS sido2
FROM `{$tblPost}` p
INNER JOIN `{$tblMeta}` m1 ON m1.post_id = p.id AND m1.field_key = 'sido1'
INNER JOIN `{$tblMeta}` m2 ON m2.post_id = p.id AND m2.field_key = 'sido2'
WHERE p.board_id = ? AND p.status = 1
AND m1.value = '서울'
AND m2.value = '강남구'
ORDER BY p.id DESC",
array($board['id'])
);
?>
10. 주의사항 및 FAQ
⚠ 절대 지켜야 할 규칙
// ✅ 올바름
$postId = (string)$post['id'];
$postId = (string)$editPost['id'];
$postIds[] = (string)$p['id'];
$pid = (string)$mr['post_id'];
// ❌ 잘못됨
$postId = (int)$post['id']; // 18자리 숫자 잘림
$postId = intval($post['id']); // 동일하게 잘림