카테고리 관리

카테고리 관리

게시판의 카테고리 시스템을 효과적으로 활용하는 방법을 학습합니다.

카테고리 설정

기본 카테고리 구조

<!-- 카테고리 목록 출력 -->
<div class="category-list">
    <ul>
        <li><a href="{getUrl('category','')}" class="active"|cond="!$category">전체</a></li>
        <li loop="$category_list=>$val">
            <!-- 깊이에 따른 들여쓰기 -->
            {@$indent = str_repeat('&nbsp;&nbsp;', $val->depth)}

            <a href="{getUrl('category',$val->category_srl)}" 
               class="active"|cond="$category==$val->category_srl">
                {$indent}{$val->title} ({$val->document_count})
            </a>

            <!-- 하위 카테고리가 있는 경우 -->
            <ul cond="$val->childs">
                <li loop="$val->childs=>$child">
                    <a href="{getUrl('category',$child->category_srl)}">
                        {$child->title} ({$child->document_count})
                    </a>
                </li>
            </ul>
        </li>
    </ul>
</div>

트리 구조 카테고리

<!-- 접기/펼치기 가능한 트리 구조 -->
<div class="category-tree">
    <ul class="tree-root">
        {@
            // 카테고리를 트리 구조로 재구성
            $tree = array();
            foreach($category_list as $cat) {
                if(!$cat->parent_srl) {
                    $tree[$cat->category_srl] = $cat;
                    $tree[$cat->category_srl]->children = array();
                }
            }

            foreach($category_list as $cat) {
                if($cat->parent_srl && isset($tree[$cat->parent_srl])) {
                    $tree[$cat->parent_srl]->children[] = $cat;
                }
            }
        }

        <li loop="$tree=>$cat" class="tree-item">
            <div class="tree-node">
                <span class="toggle" cond="count($cat->children)">
                    <i class="xi-angle-right"></i>
                </span>

                <a href="{getUrl('category',$cat->category_srl)}" 
                   class="active"|cond="$category==$cat->category_srl">
                    <i class="xi-folder"></i>
                    {$cat->title}
                    <span class="count">({$cat->document_count})</span>
                </a>
            </div>

            <ul class="tree-children" cond="count($cat->children)" style="display:none;">
                <li loop="$cat->children=>$child" class="tree-item">
                    <div class="tree-node">
                        <a href="{getUrl('category',$child->category_srl)}">
                            <i class="xi-folder-open"></i>
                            {$child->title}
                            <span class="count">({$child->document_count})</span>
                        </a>
                    </div>
                </li>
            </ul>
        </li>
    </ul>
</div>

<script>
jQuery(function($) {
    // 트리 토글
    $('.tree-node .toggle').click(function() {
        var $li = $(this).closest('.tree-item');
        var $children = $li.children('.tree-children');
        var $icon = $(this).find('i');

        $children.slideToggle(200);
        $icon.toggleClass('xi-angle-right xi-angle-down');
    });

    // 현재 카테고리 자동 펼치기
    $('.tree-node a.active').each(function() {
        $(this).closest('.tree-item').parents('.tree-children').show();
        $(this).closest('.tree-item').parents('.tree-item').find('.toggle i')
            .removeClass('xi-angle-right').addClass('xi-angle-down');
    });
});
</script>

카테고리별 스타일

카테고리별 색상 지정

<!-- 카테고리별 색상 적용 -->
{@
    // 카테고리별 색상 정의
    $category_colors = array(
        '공지사항' => '#ff4444',
        '자유게시판' => '#4CAF50',
        'Q&A' => '#2196F3',
        '자료실' => '#FF9800'
    );
}

<style>
    <!--@foreach($category_list as $cat)-->
    {@
        $color = $category_colors[$cat->title] ?: '#666';
    }

    .category-{$cat->category_srl} {
        color: {$color};
        border-left: 3px solid {$color};
    }

    .cat-badge-{$cat->category_srl} {
        background-color: {$color};
        color: white;
    }
    <!--@end-->
</style>

<!-- 게시글 목록에서 카테고리 뱃지 -->
<td class="title">
    <span class="cat-badge cat-badge-{$document->get('category_srl')}" cond="$document->get('category_srl')">
        {$category_list[$document->get('category_srl')]->title}
    </span>

    <a href="{getUrl('document_srl',$document->document_srl)}">
        {$document->getTitle()}
    </a>
</td>

카테고리 필터 위젯

드롭다운 필터

<!-- 카테고리 드롭다운 -->
<div class="category-dropdown">
    <select id="category-select" onchange="filterByCategory(this.value)">
        <option value="">전체 카테고리</option>
        <optgroup loop="$category_list=>$cat" label="{$cat->title}" cond="!$cat->parent_srl">
            <option value="{$cat->category_srl}" selected="selected"|cond="$category==$cat->category_srl">
                {$cat->title} ({$cat->document_count})
            </option>

            <!-- 하위 카테고리 -->
            <option loop="$category_list=>$sub" value="{$sub->category_srl}" cond="$sub->parent_srl==$cat->category_srl">
                ㄴ {$sub->title} ({$sub->document_count})
            </option>
        </optgroup>
    </select>
</div>

<script>
function filterByCategory(category_srl) {
    var url = current_url.setQuery('category', category_srl);
    location.href = url;
}
</script>

태그 클라우드 스타일

<!-- 카테고리 태그 클라우드 -->
<div class="category-cloud">
    {@
        // 최대/최소 문서 수 계산
        $max_count = 0;
        $min_count = 999999;

        foreach($category_list as $cat) {
            $max_count = max($max_count, $cat->document_count);
            $min_count = min($min_count, $cat->document_count);
        }

        $range = $max_count - $min_count;
    }

    <a loop="$category_list=>$cat" 
       href="{getUrl('category',$cat->category_srl)}"
       class="cloud-tag"
       style="font-size: {12 + (($cat->document_count - $min_count) / $range * 20)}px">
        {$cat->title}
    </a>
</div>

<style>
.category-cloud {
    text-align: center;
    padding: 20px;
}

.cloud-tag {
    display: inline-block;
    margin: 5px;
    padding: 5px 15px;
    background: #f0f0f0;
    border-radius: 20px;
    text-decoration: none;
    transition: all 0.3s;
}

.cloud-tag:hover {
    background: #333;
    color: white;
    transform: scale(1.1);
}
</style>

카테고리별 권한 설정

카테고리별 접근 제한

<!-- 카테고리별 권한 체크 -->
{@
    // 카테고리별 권한 설정 (관리자에서 설정한 값)
    $category_grants = array(
        '123' => array('list' => -1, 'view' => -1), // 회원만
        '456' => array('list' => 1, 'view' => 1),   // 모두
        '789' => array('list' => -100, 'view' => -100) // 관리자만
    );

    $current_cat_grant = $category_grants[$category] ?: array('list' => 1, 'view' => 1);

    // 권한 체크
    $is_granted = true;
    if($current_cat_grant['list'] == -1 && !$logged_info) {
        $is_granted = false;
    } elseif($current_cat_grant['list'] == -100 && $logged_info->is_admin != 'Y') {
        $is_granted = false;
    }
}

<div cond="!$is_granted" class="message error">
    이 카테고리는 {$current_cat_grant['list'] == -1 ? '회원만' : '관리자만'} 접근할 수 있습니다.
</div>

<div cond="$is_granted">
    <!-- 게시글 목록 표시 -->
</div>

카테고리 통계

카테고리별 통계 표시

<!-- 카테고리 통계 대시보드 -->
<div class="category-stats">
    <h3>카테고리별 통계</h3>

    <div class="stats-grid">
        <div loop="$category_list=>$cat" class="stat-card">
            <h4>{$cat->title}</h4>

            {@
                // 오늘 작성된 글 수 계산
                $today = date('Ymd');
                $today_count = 0;

                $args = new stdClass();
                $args->module_srl = $module_info->module_srl;
                $args->category_srl = $cat->category_srl;
                $args->regdate_more = $today . '000000';
                $args->regdate_less = $today . '235959';

                $output = executeQuery('board.getDocumentCount', $args);
                $today_count = $output->data->count ?: 0;
            }

            <div class="stat-item">
                <span class="label">전체 글</span>
                <span class="value">{number_format($cat->document_count)}</span>
            </div>

            <div class="stat-item">
                <span class="label">오늘 글</span>
                <span class="value">{$today_count}</span>
            </div>

            <!-- 차트 -->
            <div class="mini-chart" data-category="{$cat->category_srl}">
                <canvas id="chart-{$cat->category_srl}"></canvas>
            </div>
        </div>
    </div>
</div>

<script>
// 카테고리별 차트 생성
jQuery(function($) {
    $('.mini-chart').each(function() {
        var category_srl = $(this).data('category');
        var canvas = $(this).find('canvas')[0];

        // AJAX로 최근 7일 데이터 가져오기
        exec_xml('board', 'getCategoryStats', {category_srl: category_srl}, function(ret) {
            if(ret.data) {
                createMiniChart(canvas, ret.data);
            }
        });
    });

    function createMiniChart(canvas, data) {
        new Chart(canvas, {
            type: 'line',
            data: {
                labels: data.labels,
                datasets: [{
                    data: data.values,
                    borderColor: '#2196F3',
                    fill: false
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                legend: { display: false },
                scales: {
                    xAxes: [{ display: false }],
                    yAxes: [{ display: false }]
                }
            }
        });
    }
});
</script>

카테고리 네비게이션

이전/다음 카테고리 이동

<!-- 카테고리 네비게이션 -->
<div class="category-navigation">
    {@
        // 현재 카테고리의 인덱스 찾기
        $current_index = -1;
        $category_array = array_values($category_list);

        foreach($category_array as $idx => $cat) {
            if($cat->category_srl == $category) {
                $current_index = $idx;
                break;
            }
        }

        $prev_category = $current_index > 0 ? $category_array[$current_index - 1] : null;
        $next_category = $current_index < count($category_array) - 1 ? $category_array[$current_index + 1] : null;
    }

    <a cond="$prev_category" href="{getUrl('category', $prev_category->category_srl)}" class="nav-prev">
        <i class="xi-angle-left"></i> {$prev_category->title}
    </a>

    <span class="nav-current">
        {$category_list[$category]->title}
    </span>

    <a cond="$next_category" href="{getUrl('category', $next_category->category_srl)}" class="nav-next">
        {$next_category->title} <i class="xi-angle-right"></i>
    </a>
</div>

카테고리 검색

카테고리 내 검색

<!-- 카테고리별 검색 -->
<form action="{getUrl()}" method="get" class="category-search">
    <input type="hidden" name="vid" value="{$vid}" />
    <input type="hidden" name="mid" value="{$mid}" />
    <input type="hidden" name="category" value="{$category}" />

    <div class="search-box">
        <select name="search_target">
            <option value="title_content">제목+내용</option>
            <option value="title">제목</option>
            <option value="content">내용</option>
            <option value="user_name">작성자</option>
            <option value="tag">태그</option>
        </select>

        <input type="text" name="search_keyword" value="{$search_keyword}" placeholder="검색어 입력" />

        <button type="submit">
            <i class="xi-search"></i>
            {$category ? $category_list[$category]->title . '에서' : ''} 검색
        </button>
    </div>
</form>

모범 사례

  1. 계층 구조: 깊이 제한하여 복잡도 관리
  2. 네이밍: 명확하고 일관된 카테고리명 사용
  3. 권한 관리: 카테고리별 세밀한 권한 설정
  4. UI/UX: 직관적인 카테고리 네비게이션
  5. 성능: 카테고리 수가 많을 때 캐싱 활용

다음 단계

카테고리 관리를 구현했다면, 확장변수 활용을 학습하세요.