1. 설정 및 기본 필터
1.1 KBoard 기본 설정 제어
kboard_settings
내용: 자바스크립트 설정을 동적으로 변경할 수 있습니다.
코드:
add_filter('kboard_settings', 'customize_kboard_settings');
function customize_kboard_settings($settings) {
// AJAX 로딩 텍스트 커스터마이징
$settings['loading_text'] = '잠시만 기다려주세요...';
// 파일 업로드 제한 설정
$settings['max_file_size'] = 10 * 1024 * 1024; // 10MB
// 게시판별 다른 설정 적용
if (isset($_GET['id']) && $_GET['id'] == 3) {
$settings['auto_refresh'] = 30000; // 30초마다 자동 새로고침
}
return $settings;
}
kboard_localize_strings
내용: 다국어 문자열을 커스터마이징합니다.
코드:
add_filter('kboard_localize_strings', 'custom_localize_strings');
function custom_localize_strings($strings) {
// 기본 메시지 커스터마이징
$strings['confirm_delete'] = '정말로 삭제하시겠습니까?';
$strings['loading'] = '로딩 중...';
$strings['error_occurred'] = '오류가 발생했습니다. 다시 시도해주세요.';
// 브랜드에 맞는 메시지로 변경
$strings['write_post'] = '새 글쓰기';
$strings['reply'] = '답글달기';
return $strings;
}
kboard_per_rss
내용: RSS 피드의 게시글 수를 조절합니다.
코드:
add_filter('kboard_per_rss', 'customize_rss_per_page', 10, 2);
function customize_rss_per_page($per_page, $board_id) {
// 공지사항 게시판은 모든 게시글 포함
if ($board_id == 1) {
return -1; // 전체
}
// 일반 게시판은 최신 50개
return 50;
}
2. 스킨 시스템 필터
2.1 스킨 관리 및 커스터마이징
kboard_skin_list
내용: 사용 가능한 스킨 목록을 확장합니다.
코드:
add_filter('kboard_skin_list', 'add_custom_skins');
function add_custom_skins($skins) {
// 커스텀 스킨 추가
$skins['premium_gallery'] = 'Premium Gallery Skin';
$skins['corporate_board'] = 'Corporate Board Skin';
$skins['mobile_optimized'] = 'Mobile Optimized Skin';
return $skins;
}
kboard_skin_file_path
내용: 스킨 파일 경로를 동적으로 변경합니다.
코드:
add_filter('kboard_skin_file_path', 'custom_skin_file_path', 10, 5);
function custom_skin_file_path($file_path, $skin_name, $file, $vars, $skin_obj) {
// 테마 디렉토리에서 스킨 파일 우선 로드
$theme_skin_path = get_template_directory() . '/kboard-skins/' . $skin_name . '/' . $file;
if (file_exists($theme_skin_path)) {
return $theme_skin_path;
}
// A/B 테스트용 스킨 분기
if ($skin_name === 'default' && isset($_GET['variant']) && $_GET['variant'] === 'b') {
$ab_test_path = str_replace('/default/', '/default-variant-b/', $file_path);
if (file_exists($ab_test_path)) {
return $ab_test_path;
}
}
return $file_path;
}
kboard_skin_category_type
내용: 카테고리 표시 방식을 커스터마이징합니다.
코드:
add_filter('kboard_skin_category_type', 'dynamic_category_type', 10, 3);
function dynamic_category_type($type, $board, $boardBuilder) {
// 모바일에서는 드롭다운으로 표시
if (wp_is_mobile()) {
return 'dropdown';
}
// 카테고리가 많은 게시판은 검색 가능한 드롭다운
if (count($board->categories) > 10) {
return 'searchable_dropdown';
}
// 기본은 탭 형태
return 'tabs';
}
3. 함수 및 매개변수 필터
3.1 사용자 관련 필터
kboard_current_user_roles
내용: 현재 사용자의 역할을 확장합니다.
코드:
add_filter('kboard_current_user_roles', 'extend_user_roles');
function extend_user_roles($roles) {
$current_user = wp_get_current_user();
// VIP 회원 역할 추가
if (get_user_meta($current_user->ID, 'is_vip_member', true)) {
$roles[] = 'vip_member';
}
// 활동 점수 기반 역할 추가
$activity_score = get_user_meta($current_user->ID, 'activity_score', true);
if ($activity_score > 1000) {
$roles[] = 'power_user';
}
// 게시판별 모더레이터 역할
if (current_user_can('moderate_board_' . kboard_id())) {
$roles[] = 'board_moderator';
}
return $roles;
}
kboard_user_ip
내용: 사용자 IP 주소를 정확하게 감지합니다.
코드:
add_filter('kboard_user_ip', 'get_real_user_ip');
function get_real_user_ip($ip) {
// 프록시 서버 환경에서 실제 IP 획득
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
return $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// 여러 IP가 있는 경우 첫 번째 IP 사용
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
return trim($ips[0]);
} elseif (!empty($_SERVER['HTTP_X_FORWARDED'])) {
return $_SERVER['HTTP_X_FORWARDED'];
} elseif (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP'])) {
return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_FORWARDED_FOR'])) {
return $_SERVER['HTTP_FORWARDED_FOR'];
} elseif (!empty($_SERVER['HTTP_FORWARDED'])) {
return $_SERVER['HTTP_FORWARDED'];
}
return $ip;
}
3.2 콘텐츠 에디터 커스터마이징
kboard_content_editor_list
내용: 사용 가능한 에디터 목록을 확장합니다.
코드:
add_filter('kboard_content_editor_list', 'add_custom_editors');
function add_custom_editors($editors) {
// 마크다운 에디터 추가
$editors['markdown'] = 'Markdown Editor';
// 코드 에디터 추가 (개발자 게시판용)
$editors['code_editor'] = 'Code Editor (Syntax Highlighting)';
// 위지윅 에디터 업그레이드
$editors['tinymce_advanced'] = 'Advanced WYSIWYG Editor';
return $editors;
}
kboard_content_editor
내용: 에디터 HTML을 완전히 커스터마이징합니다.
코드:
add_filter('kboard_content_editor', 'customize_content_editor', 10, 2);
function customize_content_editor($editor_html, $vars) {
$editor_type = $vars['editor'] ?? 'default';
switch ($editor_type) {
case 'markdown':
$editor_html = '<div class="markdown-editor-container">';
$editor_html .= '<textarea name="content" class="markdown-editor">' . esc_textarea($vars['content']) . '</textarea>';
$editor_html .= '<div class="markdown-preview"></div>';
$editor_html .= '</div>';
$editor_html .= '<script>initMarkdownEditor();</script>';
break;
case 'code_editor':
$editor_html = '<div class="code-editor-container">';
$editor_html .= '<select name="code_language" class="code-language-selector">';
$editor_html .= '<option value="php">PHP</option>';
$editor_html .= '<option value="javascript">JavaScript</option>';
$editor_html .= '<option value="python">Python</option>';
$editor_html .= '</select>';
$editor_html .= '<textarea name="content" class="code-editor">' . esc_textarea($vars['content']) . '</textarea>';
$editor_html .= '</div>';
$editor_html .= '<script>initCodeEditor();</script>';
break;
}
return $editor_html;
}
4. 리스트 및 쿼리 필터
4.1 게시글 목록 커스터마이징
kboard_list_rpp
내용: 페이지당 게시글 수를 동적으로 조절합니다.
코드:
add_filter('kboard_list_rpp', 'dynamic_posts_per_page', 10, 3);
function dynamic_posts_per_page($rpp, $board_id, $list) {
// VIP 회원은 더 많은 게시글 표시
if (in_array('vip_member', kboard_current_user_roles())) {
return $rpp * 2;
}
// 모바일에서는 적게 표시
if (wp_is_mobile()) {
return min($rpp, 10);
}
// 갤러리 게시판은 많이 표시
if ($board_id == 4) { // 갤러리 게시판
return 50;
}
// 사용자 설정 반영
$user_preference = get_user_meta(get_current_user_id(), 'kboard_posts_per_page', true);
if ($user_preference) {
return intval($user_preference);
}
return $rpp;
}
kboard_list_where
내용: 게시글 목록 쿼리 조건을 수정합니다.
코드:
add_filter('kboard_list_where', 'customize_list_query', 10, 2);
function customize_list_query($where, $board_id) {
global $wpdb;
// 신고된 게시글 숨기기
$where .= " AND content.uid NOT IN (
SELECT content_uid FROM {$wpdb->prefix}kboard_reports
WHERE report_count >= 3
)";
// VIP 전용 게시판 처리
if ($board_id == 5) { // VIP 게시판
if (!in_array('vip_member', kboard_current_user_roles())) {
$where .= " AND 1=0"; // VIP가 아니면 결과 없음
}
}
// 지역별 필터링 (사용자 설정 기반)
$user_region = get_user_meta(get_current_user_id(), 'user_region', true);
if ($user_region && $board_id == 6) { // 지역 게시판
$where .= $wpdb->prepare(" AND content.region = %s", $user_region);
}
return $where;
}
kboard_list_orderby
내용: 정렬 방식을 고급화합니다.
코드:
add_filter('kboard_list_orderby', 'advanced_list_ordering', 10, 2);
function advanced_list_ordering($orderby, $board_id) {
$sort_option = kboard_search_option();
switch ($sort_option) {
case 'popular':
// 인기도 = 조회수 + 댓글수 + 좋아요
$orderby = "(content.view + content.comment + content.vote_up) DESC, content.date DESC";
break;
case 'trending':
// 트렌딩 = 최근 활동 기반 인기도
$orderby = "((content.view + content.comment + content.vote_up) * EXP(-TIMESTAMPDIFF(HOUR, content.date, NOW()) / 24)) DESC";
break;
case 'controversial':
// 논란성 = 좋아요와 싫어요 비율
$orderby = "ABS(content.vote_up - content.vote_down) DESC,
(content.vote_up + content.vote_down) DESC";
break;
case 'quality':
// 품질 = 글자수 + 이미지수 + 댓글수
$orderby = "(CHAR_LENGTH(content.content) / 100 +
content.comment * 2 +
(CHAR_LENGTH(content.content) - CHAR_LENGTH(REPLACE(content.content, '<img', ''))) * 5) DESC";
break;
}
return $orderby;
}
4.2 검색 기능 강화
kboard_list_search_option
내용: 검색 옵션을 확장합니다.
코드:
add_filter('kboard_list_search_option', 'extended_search_options');
function extended_search_options($options) {
// 기존 옵션에 추가
$options['tag'] = '태그';
$options['author_email'] = '작성자 이메일';
$options['ip_address'] = 'IP 주소';
$options['date_range'] = '날짜 범위';
$options['file_name'] = '첨부파일명';
$options['custom_field'] = '사용자 정의 필드';
return $options;
}
5. URL 및 라우팅 필터
5.1 URL 구조 커스터마이징
kboard_url_file_download
내용: 파일 다운로드 URL을 보안 강화합니다.
코드:
add_filter('kboard_url_file_download', 'secure_download_url', 10, 4);
function secure_download_url($url, $file, $content, $board) {
// 토큰 기반 다운로드 URL 생성
$token = wp_create_nonce('kboard_download_' . $file['uid']);
$url = add_query_arg([
'action' => 'kboard_secure_download',
'file_id' => $file['uid'],
'token' => $token,
'expires' => time() + 3600 // 1시간 후 만료
], admin_url('admin-ajax.php'));
return $url;
}
kboard_url_document_uid
내용: SEO 친화적 URL 구조를 만듭니다.
코드:
add_filter('kboard_url_document_uid', 'seo_friendly_document_url', 10, 3);
function seo_friendly_document_url($url, $content_uid, $content) {
if ($content && $content->title) {
// 제목을 URL 슬러그로 변환
$slug = sanitize_title($content->title);
$url = add_query_arg([
'uid' => $content_uid,
'slug' => $slug
], $url);
}
return $url;
}
5.2 리다이렉트 제어
kboard_url_document_redirect
내용: 문서 리다이렉트를 조건부로 처리합니다.
코드:
add_filter('kboard_url_document_redirect', 'conditional_redirect', 10, 3);
function conditional_redirect($redirect_url, $content, $board) {
// 외부 링크인 경우 경고 페이지로 리다이렉트
if (filter_var($redirect_url, FILTER_VALIDATE_URL) &&
!str_contains($redirect_url, get_site_url())) {
return add_query_arg([
'action' => 'external_link_warning',
'target' => urlencode($redirect_url)
], home_url());
}
// 회원 전용 링크 처리
if (!is_user_logged_in() && $board->permission_read) {
return wp_login_url($redirect_url);
}
return $redirect_url;
}
6. 고급 데이터 처리 필터
6.1 문서 데이터 가공
kboard_insert_data
내용: 문서 등록 시 데이터를 전처리합니다.
코드:
add_filter('kboard_insert_data', 'preprocess_insert_data', 10, 2);
function preprocess_insert_data($data, $board) {
// 제목 자동 보정
$data['title'] = trim($data['title']);
if (empty($data['title'])) {
$data['title'] = '제목 없음 ' . date('Y-m-d H:i:s');
}
// 내용 정제
$data['content'] = wp_kses_post($data['content']);
// 해시태그 자동 추출
if (preg_match_all('/#([가-힣a-zA-Z0-9_]+)/', $data['content'], $matches)) {
$data['hashtags'] = implode(',', array_unique($matches[1]));
}
// 읽기 시간 계산
$word_count = str_word_count(strip_tags($data['content']));
$data['reading_time'] = ceil($word_count / 200); // 분당 200단어
// SEO 메타데이터 생성
$data['meta_description'] = wp_trim_words(strip_tags($data['content']), 20);
// 감정 분석 (긍정/부정)
$data['sentiment'] = analyze_sentiment($data['content']);
return $data;
}
kboard_update_data
내용: 문서 수정 시 변경 이력을 관리합니다.
코드:
add_filter('kboard_update_data', 'track_update_history', 10, 3);
function track_update_history($data, $content_uid, $board) {
global $wpdb;
// 기존 데이터 조회
$original = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}kboard_board_content WHERE uid = %d",
$content_uid
));
if ($original) {
// 변경 사항 추적
$changes = [];
if ($original->title !== $data['title']) {
$changes['title'] = ['from' => $original->title, 'to' => $data['title']];
}
if ($original->content !== $data['content']) {
$changes['content'] = ['from' => $original->content, 'to' => $data['content']];
}
// 변경 이력 저장
if (!empty($changes)) {
$wpdb->insert(
$wpdb->prefix . 'kboard_edit_history',
[
'content_uid' => $content_uid,
'user_id' => get_current_user_id(),
'changes' => json_encode($changes),
'date' => current_time('mysql')
]
);
// 수정 횟수 업데이트
$data['edit_count'] = intval($original->edit_count ?? 0) + 1;
}
}
return $data;
}
6.2 투표 및 평가 시스템
kboard_vote_filter
내용: 투표 시스템을 고도화합니다.
코드:
add_filter('kboard_vote_filter', 'advanced_voting_system', 10, 3);
function advanced_voting_system($vote_data, $content_uid, $vote_type) {
// 중복 투표 방지 (IP + 사용자 ID 기반)
$user_id = get_current_user_id();
$user_ip = kboard_user_ip();
global $wpdb;
$existing_vote = $wpdb->get_var($wpdb->prepare(
"SELECT vote_type FROM {$wpdb->prefix}kboard_vote_log
WHERE content_uid = %d AND (user_id = %d OR user_ip = %s)
ORDER BY date DESC LIMIT 1",
$content_uid, $user_id, $user_ip
));
if ($existing_vote === $vote_type) {
// 같은 투표 취소
$vote_data['action'] = 'cancel';
} elseif ($existing_vote) {
// 투표 변경
$vote_data['action'] = 'change';
$vote_data['previous_vote'] = $existing_vote;
} else {
// 새 투표
$vote_data['action'] = 'new';
}
// 투표 가중치 계산 (사용자 신뢰도 기반)
$user_trust_score = get_user_meta($user_id, 'trust_score', true) ?: 1.0;
$vote_data['weight'] = min(max($user_trust_score, 0.1), 3.0);
// 투표 이유 저장 (선택사항)
if (!empty($_POST['vote_reason'])) {
$vote_data['reason'] = sanitize_textarea_field($_POST['vote_reason']);
}
return $vote_data;
}
7. 고급 활용 패턴
7.1 조건부 필터 체인
코드:
// 게시판별 다른 필터 적용
add_filter('kboard_list_where', 'board_specific_filters', 10, 2);
function board_specific_filters($where, $board_id) {
switch ($board_id) {
case 1: // 공지사항
return apply_filters('kboard_notice_list_where', $where);
case 2: // 자유게시판
return apply_filters('kboard_free_list_where', $where);
case 3: // 갤러리
return apply_filters('kboard_gallery_list_where', $where);
default:
return $where;
}
}
// 공지사항 게시판 전용 필터
add_filter('kboard_notice_list_where', 'notice_board_filter');
function notice_board_filter($where) {
// 중요 공지사항 우선 표시
global $wpdb;
$where .= " ORDER BY CASE WHEN content.priority = 'urgent' THEN 0 ELSE 1 END, content.date DESC";
return $where;
}
7.2 캐싱 활용 필터
코드:
add_filter('kboard_content_list_items', 'cached_content_list', 10, 2);
function cached_content_list($items, $board_id) {
$cache_key = 'kboard_list_' . $board_id . '_' . md5(serialize($_GET));
// 캐시된 데이터 확인
$cached_items = wp_cache_get($cache_key, 'kboard');
if ($cached_items !== false) {
return $cached_items;
}
// 아이템 후처리
foreach ($items as &$item) {
// 썸네일 생성
if (empty($item->thumbnail)) {
$item->thumbnail = extract_first_image($item->content);
}
// 요약 생성
$item->summary = wp_trim_words(strip_tags($item->content), 30);
// 읽기 시간 계산
$word_count = str_word_count(strip_tags($item->content));
$item->reading_time = ceil($word_count / 200);
}
// 캐시 저장 (5분)
wp_cache_set($cache_key, $items, 'kboard', 300);
return $items;
}
7.3 A/B 테스트 필터
코드:
add_filter('kboard_skin_file_path', 'ab_test_skin_variant', 10, 5);
function ab_test_skin_variant($file_path, $skin_name, $file, $vars, $skin_obj) {
// 사용자를 A/B 그룹으로 분할
$user_id = get_current_user_id() ?: ip2long(kboard_user_ip());
$ab_group = ($user_id % 2 === 0) ? 'A' : 'B';
// B 그룹용 스킨 파일이 존재하면 사용
if ($ab_group === 'B') {
$variant_path = str_replace($file, 'variant-b-' . $file, $file_path);
if (file_exists($variant_path)) {
// A/B 테스트 로깅
error_log("AB Test: User $user_id using variant B for $file");
return $variant_path;
}
}
return $file_path;
}
8. 성능 최적화 필터
8.1 쿼리 최적화
코드:
add_filter('kboard_list_select', 'optimize_list_query');
function optimize_list_query($select) {
// 불필요한 필드 제외하고 필요한 필드만 선택
$select = "content.uid, content.title, content.date, content.member_display,
content.view, content.comment, content.vote_up, content.vote_down,
SUBSTRING(content.content, 1, 200) as content_preview";
return $select;
}
add_filter('kboard_list_from', 'add_query_joins');
function add_query_joins($from) {
global $wpdb;
// 필요한 경우에만 조인 추가
if (kboard_search_option() === 'author_info') {
$from .= " LEFT JOIN {$wpdb->users} u ON content.member_uid = u.ID";
}
return $from;
}
8.2 이미지 최적화
코드:
add_filter('kboard_content_editor', 'optimize_image_upload', 10, 2);
function optimize_image_upload($editor_html, $vars) {
// 이미지 업로드 시 자동 압축 설정 추가
$editor_html .= '<script>
document.addEventListener("DOMContentLoaded", function() {
const imageInputs = document.querySelectorAll("input[type=file][accept*=image]");
imageInputs.forEach(function(input) {
input.addEventListener("change", function(e) {
const files = Array.from(e.target.files);
const compressedFiles = [];
files.forEach(function(file) {
if (file.type.startsWith("image/")) {
compressImage(file, 0.8, 1920, 1080).then(function(compressed) {
compressedFiles.push(compressed);
});
}
});
});
});
});
</script>';
return $editor_html;
}
실무 베스트 프랙티스
1. 필터 체인 관리
코드:
// 필터 우선순위를 명확하게 설정
add_filter('kboard_list_where', 'security_filter', 5); // 보안 필터 우선
add_filter('kboard_list_where', 'permission_filter', 10); // 권한 필터
add_filter('kboard_list_where', 'custom_filter', 15); // 커스텀 필터
2. 데이터 검증
코드:
add_filter('kboard_insert_data', 'validate_and_sanitize_data', 1, 2);
function validate_and_sanitize_data($data, $board) {
// 모든 입력 데이터 검증
$data['title'] = sanitize_text_field($data['title'] ?? '');
$data['content'] = wp_kses_post($data['content'] ?? '');
// 필수 필드 검증
if (empty($data['title'])) {
wp_die('제목을 입력해주세요.');
}
return $data;
}
3. 에러 처리
코드:
add_filter('kboard_content_list_items', 'handle_list_errors', 999, 2);
function handle_list_errors($items, $board_id) {
if (is_wp_error($items)) {
error_log('KBoard 리스트 오류: ' . $items->get_error_message());
return []; // 빈 배열 반환
}
return $items;
}