AI 단계별 작업 문서 03 — 3단계: 검색 필터
검색 필터 요청에 해당하는 원본 설명과 코드 블록을 보존했습니다. handler.php 패치, 필터 폼, 페이지네이션 연결을 모두 확인하세요.
5. 3단계 — 검색 필터 (is_search)
5-1. URL 규격 (헬퍼가 해석)
is_search=1 이고 활성인 필드만 허용한다. 정의되지 않았거나 검색 설정이 꺼진 key 는 무시된다.
| 파라미터 | 대상 타입 | 의미 | 예 |
|---|---|---|---|
bf_{key}=값 |
select, radio, text, date … | 정확히 일치 | ?bf_region=서울 |
bf_{key}[]=A&bf_{key}[]=B |
checkbox | 선택한 항목을 모두 포함(AND) | ?bf_facilities[]=주차&bf_facilities[]=에어컨 |
bfmin_{key} / bfmax_{key} |
number, date, datetime | 이상 / 이하 | ?bfmin_price=1000&bfmax_price=5000 |
bfq_{key}=검색어 |
text, textarea, tel, url | 부분 일치(LIKE) | ?bfq_memo=역세권 |
- 여러 필드·여러 파라미터는 AND 로 결합된다.
- 기본 검색(
s,sf), 카테고리(cat)와도 함께 쓸 수 있다. - 예:
/board/list?cat=아파트&bf_region=서울&bfmax_price=5000&page=2
5-2. boards/handler.php 패치 (3곳)
먼저 백업하고, 아래 3곳을 수정한다. 각 앵커 문자열은 파일 안에서 1회만 나타난다(v10.3.0 기준, 수정 전 grep -c 로 재확인).
패치 1 — $tag = dx_get('tag', ''); 바로 아래에 추가 (캐시 판정보다 앞이어야 함)
/* BF-PATCH-1: 여분 필드 검색 필터 (extend/top/60_board_fields_helpers.php) */
$_bfFilter = function_exists('dx_bf_build_filter')
? dx_bf_build_filter($board['id'], $_GET)
: array('sql'=>'', 'params'=>array(), 'active'=>false);
패치 2 — 캐시 조건에 필터 여부 추가
// 변경 전
!$search && !$cat && !$tag &&
// 변경 후
!$search && !$cat && !$tag && empty($_bfFilter['active']) && /* BF-PATCH-2 */
패치 3 — if ($tag) { $where.=" AND p.tags LIKE ?"; ... } 줄 바로 아래에 추가
/* BF-PATCH-3: 여분 필드 검색 조건 (COUNT 와 목록 쿼리가 같은 $where 를 쓰므로 한 번만 추가) */
if (!empty($_bfFilter['active'])) {
$where .= $_bfFilter['sql'];
foreach ($_bfFilter['params'] as $_bfp) { $params[] = $_bfp; }
}
적용 후 확인:
php -l boards/handler.php # No syntax errors detected
grep -n BF-PATCH boards/handler.php # 3줄 (BF-PATCH-1, -2, -3)
grep -n _bfFilter boards/handler.php # 5줄
- 이 패치는 v10.3.0 의 실제
handler.php에 적용해php -l문법 검사를 통과한 것이다. - DXCMS 업데이트로
handler.php가 교체되면 패치가 사라진다.BF-PATCH마커로 검색해 재적용 여부를 점검한다. - 헬퍼 파일(
60_...)이 없어도function_exists가드 때문에 패치 코드는 무해하다. (필터가 비활성 상태로 동작)
5-3. 스킨에 필터 폼·적용 표시·페이지네이션 연결
변수명은 스킨마다 다르다. 대상 스킨의 list.php 상단에서 목록 URL, 현재 카테고리, 검색어/검색필드 변수를 확인해 맞춘다. (gallery: $_base, $currentCategory, $_catParentSlug, $search, $searchField)
(1) 필터 계산 + 폼 출력 — 목록 위 원하는 위치
<?php
$_bfF = function_exists('dx_bf_build_filter')
? dx_bf_build_filter($board['id'], $_GET)
: array('active'=>false, 'applied'=>array(), 'qs'=>'');
if (function_exists('dx_bf_render_filter_form')) {
echo dx_bf_render_filter_form($board['id'], $_base, array(
'cat' => $currentCategory,
'parent' => $_catParentSlug,
's' => $search,
'sf' => $search ? $searchField : '',
));
}
?>
검색 가능 필드가 없으면 빈 문자열이 반환되어 아무것도 출력되지 않는다. 폼 컨트롤은 타입별로 자동 결정된다.
| 필드 타입 | 폼 컨트롤 |
|---|---|
| select, radio | 드롭다운 ("전체" 포함) |
| checkbox | 체크박스 묶음 |
| number, date, datetime | 최소 ~ 최대 입력 2칸 |
| text, textarea, tel, url | 부분 검색 입력 1칸 |
(2) 적용 중인 필터 표시 (선택)
<?php if (!empty($_bfF['active'])): ?>
<div class="dx-bf-applied" style="margin:0 0 12px;font-size:.8rem;display:flex;flex-wrap:wrap;gap:6px;align-items:center">
<?php foreach ($_bfF['applied'] as $_a): ?>
<span style="padding:2px 10px;border-radius:999px;background:var(--bg-hover,#f1f5f9)">
<?php echo htmlspecialchars($_a['label'] . ': ' . $_a['display'], ENT_QUOTES, 'UTF-8'); ?>
</span>
<?php endforeach; ?>
<span style="color:var(--text-muted,#888)">총 <?php echo (int)$total; ?>건</span>
</div>
<?php endif; ?>
(3) 페이지네이션 링크에 필터 유지 (필수) — gallery 의 $__pBase 예 (약 815행)
// 기존 식 끝에 아래 조각을 이어붙인다
.(!empty($_bfF['qs']) ? ltrim($_bfF['qs'], '&') . '&' : '')
$__pBase = $_base.'?' ... .($_catParentSlug?'parent='.urlencode($_catParentSlug).'&':'') . (!empty($_bfF['qs']) ? ltrim($_bfF['qs'],'&').'&' : '');
dx_pagination($total,$perPage,$current,$urlPattern) 를 쓰는 스킨이면 $urlPattern 에 같은 조각을 포함시킨다. 이것을 빠뜨리면 2페이지로 넘어가는 순간 필터가 풀린다.
(4) 키워드 검색 폼이 필터를 지우지 않게 (선택) — 기존 검색 <form> 안에 추가
<?php
if (!empty($_bfF['qs'])) {
parse_str(ltrim($_bfF['qs'], '&'), $_bfQ);
foreach ($_bfQ as $_n => $_v) {
foreach ((array)$_v as $_vv) {
echo '<input type="hidden" name="' . htmlspecialchars($_n . (is_array($_v) ? '[]' : ''), ENT_QUOTES, 'UTF-8')
. '" value="' . htmlspecialchars($_vv, ENT_QUOTES, 'UTF-8') . '">';
}
}
}
?>