게시판 목록 템플릿 (list.html)

게시판 목록 템플릿 (list.html)

기본 목록 구조

표준 테이블 형식

<include target="_header.html" />

<!-- 게시판 상단 도구 -->
<div class="board-tools">
    <div class="board-info">
        <span class="total-count">전체 {number_format($total_count)}개</span>
        <span class="current-page">({$page}/{$page_navigation->last_page})</span>
    </div>

    <div class="board-actions">
        <!-- 글쓰기 버튼 -->
        <a href="{getUrl('act', 'dispBoardWrite')}" 
           class="btn btn-primary" cond="$grant->write">
            <i class="icon-edit"></i> {$lang->cmd_write}
        </a>

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

            <select name="search_target">
                <option value="title" selected="selected"|cond="$search_target == 'title'">제목</option>
                <option value="content" selected="selected"|cond="$search_target == 'content'">내용</option>
                <option value="title_content" selected="selected"|cond="$search_target == 'title_content'">제목+내용</option>
                <option value="nick_name" selected="selected"|cond="$search_target == 'nick_name'">글쓴이</option>
            </select>

            <input type="text" name="search_keyword" value="{$search_keyword}" placeholder="검색어 입력" />
            <button type="submit" class="btn btn-secondary">검색</button>
        </form>
    </div>
</div>

<!-- 게시글 목록 테이블 -->
<div class="board-list-wrapper">
    <table class="board-list" cond="$document_list">
        <thead>
            <tr>
                <th class="num">번호</th>
                <th class="category" cond="$category_list && $skin_vars->show_category == 'Y'">분류</th>
                <th class="title">제목</th>
                <th class="author">글쓴이</th>
                <th class="date">작성일</th>
                <th class="hit">조회</th>
                <th class="vote" cond="$module_info->use_vote == 'Y'">추천</th>
            </tr>
        </thead>
        <tbody>
            <!-- 공지사항 -->
            <tr loop="$notice_list => $no, $document" class="notice">
                <td class="num">
                    <span class="notice-icon">공지</span>
                </td>
                <td class="category" cond="$category_list && $skin_vars->show_category == 'Y'">
                    <span cond="$document->get('category_srl')">
                        {$category_list[$document->get('category_srl')]->title}
                    </span>
                </td>
                <td class="title">
                    <a href="{getUrl('document_srl', $document->document_srl)}" class="title-link">
                        {$document->getTitle()}
                    </a>

                    <!-- 댓글 수 -->
                    <span class="comment-count" cond="$document->getCommentCount() > 0">
                        [{$document->getCommentCount()}]
                    </span>

                    <!-- 첨부파일 아이콘 -->
                    <span class="attach-icon" cond="$document->getAttachedFileCount() > 0">
                        <i class="icon-attachment"></i>
                    </span>

                    <!-- NEW 아이콘 -->
                    <span class="new-icon" cond="$document->isNew()">
                        <i class="icon-new">N</i>
                    </span>
                </td>
                <td class="author">
                    <span class="nick">{$document->getNickName()}</span>
                </td>
                <td class="date">
                    {zdate($document->getRegdate(), 'Y.m.d')}
                </td>
                <td class="hit">
                    {number_format($document->getReadedCount())}
                </td>
                <td class="vote" cond="$module_info->use_vote == 'Y'">
                    {$document->getVotedCount()}
                </td>
            </tr>

            <!-- 일반 게시글 -->
            <tr loop="$document_list => $no, $document" class="document">
                <td class="num">
                    {$document->getNumber()}
                </td>
                <td class="category" cond="$category_list && $skin_vars->show_category == 'Y'">
                    <span cond="$document->get('category_srl')">
                        {$category_list[$document->get('category_srl')]->title}
                    </span>
                </td>
                <td class="title">
                    <!-- 답글 들여쓰기 -->
                    <span class="reply-indent" cond="$document->get('depth') > 0">
                        <block loop="$i = 0; $i < $document->get('depth'); $i++">
                            <span class="reply-arrow">└</span>
                        </block>
                    </span>

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

                    <!-- 댓글 수 -->
                    <span class="comment-count" cond="$document->getCommentCount() > 0">
                        [{$document->getCommentCount()}]
                    </span>

                    <!-- 첨부파일 아이콘 -->
                    <span class="attach-icon" cond="$document->getAttachedFileCount() > 0">
                        <i class="icon-attachment"></i>
                    </span>

                    <!-- NEW 아이콘 -->
                    <span class="new-icon" cond="$document->isNew()">
                        <i class="icon-new">N</i>
                    </span>

                    <!-- 비밀글 아이콘 -->
                    <span class="secret-icon" cond="$document->get('status') == 'SECRET'">
                        <i class="icon-lock"></i>
                    </span>
                </td>
                <td class="author">
                    <span class="nick">{$document->getNickName()}</span>
                    <!--@if($document->getMemberSrl() > 0)-->
                        <span class="member-icon" title="회원">M</span>
                    <!--@endif-->
                </td>
                <td class="date">
                    <!--@if($document->isNew())-->
                        {zdate($document->getRegdate(), 'H:i')}
                    <!--@else-->
                        {zdate($document->getRegdate(), 'm.d')}
                    <!--@endif-->
                </td>
                <td class="hit">
                    {number_format($document->getReadedCount())}
                </td>
                <td class="vote" cond="$module_info->use_vote == 'Y'">
                    {$document->getVotedCount()}
                </td>
            </tr>
        </tbody>
    </table>

    <!-- 게시글이 없을 때 -->
    <div class="no-documents" cond="!$document_list">
        <p class="message">{$lang->no_documents}</p>
        <a href="{getUrl('act', 'dispBoardWrite')}" 
           class="btn btn-primary" cond="$grant->write">
            첫 글을 작성해보세요
        </a>
    </div>
</div>

<!-- 페이지 네비게이션 -->
<div class="pagination-wrapper" cond="$page_navigation">
    <nav class="pagination">
        <!-- 이전 페이지 -->
        <a href="{getUrl('page', $page_navigation->first_page)}" 
           class="page-btn first" cond="$page_navigation->first_page < $page_navigation->cur_page">
            <i class="icon-first"></i>
        </a>
        <a href="{getUrl('page', $page_navigation->prev_page)}" 
           class="page-btn prev" cond="$page_navigation->prev_page">
            <i class="icon-prev"></i>
        </a>

        <!-- 페이지 번호 -->
        <span class="page-numbers">
            <a loop="$page_navigation->page_list => $page_no" 
               href="{getUrl('page', $page_no)}"
               class="page-num {$page_no == $page_navigation->cur_page ? 'current' : ''}">
                {$page_no}
            </a>
        </span>

        <!-- 다음 페이지 -->
        <a href="{getUrl('page', $page_navigation->next_page)}" 
           class="page-btn next" cond="$page_navigation->next_page">
            <i class="icon-next"></i>
        </a>
        <a href="{getUrl('page', $page_navigation->last_page)}" 
           class="page-btn last" cond="$page_navigation->last_page > $page_navigation->cur_page">
            <i class="icon-last"></i>
        </a>
    </nav>
</div>

</div> <!-- board-container -->

카드형 목록

모던 카드 레이아웃

<div class="board-grid" cond="$document_list">
    <article class="document-card" loop="$document_list => $no, $document">
        <!-- 썸네일 이미지 -->
        <div class="card-thumbnail">
            <!--@if($document->getThumbnail())-->
                <img src="{$document->getThumbnail()}" alt="{$document->getTitle()}" />
            <!--@else-->
                <div class="no-thumbnail">
                    <i class="icon-document"></i>
                </div>
            <!--@endif-->

            <!-- 카테고리 배지 -->
            <div class="category-badge" cond="$document->get('category_srl')">
                {$category_list[$document->get('category_srl')]->title}
            </div>
        </div>

        <!-- 카드 내용 -->
        <div class="card-content">
            <h3 class="card-title">
                <a href="{getUrl('document_srl', $document->document_srl)}">
                    {cut_str($document->getTitle(), 50)}
                </a>
            </h3>

            <p class="card-summary">
                {cut_str(strip_tags($document->getContent()), 100)}
            </p>

            <div class="card-meta">
                <span class="author">{$document->getNickName()}</span>
                <span class="date">{zdate($document->getRegdate(), 'Y.m.d')}</span>
                <span class="stats">
                    <i class="icon-eye"></i> {$document->getReadedCount()}
                    <i class="icon-comment" cond="$document->getCommentCount() > 0"></i>
                    {$document->getCommentCount()}
                </span>
            </div>
        </div>
    </article>
</div>

갤러리형 목록

이미지 중심 레이아웃

<div class="gallery-list" cond="$document_list">
    <div class="gallery-item" loop="$document_list => $no, $document">
        <div class="gallery-image">
            <a href="{getUrl('document_srl', $document->document_srl)}">
                <!--@if($document->getThumbnail())-->
                    <img src="{$document->getThumbnail()}" alt="{$document->getTitle()}" />
                <!--@else-->
                    <div class="no-image">
                        <i class="icon-image"></i>
                        <span>이미지 없음</span>
                    </div>
                <!--@endif-->
            </a>

            <!-- 오버레이 정보 -->
            <div class="image-overlay">
                <div class="overlay-info">
                    <span class="comment-count" cond="$document->getCommentCount() > 0">
                        <i class="icon-comment"></i> {$document->getCommentCount()}
                    </span>
                    <span class="view-count">
                        <i class="icon-eye"></i> {$document->getReadedCount()}
                    </span>
                </div>
            </div>
        </div>

        <div class="gallery-info">
            <h4 class="gallery-title">
                <a href="{getUrl('document_srl', $document->document_srl)}">
                    {cut_str($document->getTitle(), 30)}
                </a>
            </h4>
            <div class="gallery-meta">
                <span class="author">{$document->getNickName()}</span>
                <span class="date">{zdate($document->getRegdate(), 'm.d')}</span>
            </div>
        </div>
    </div>
</div>

모바일 최적화 목록

리스트 형식

<!-- 모바일 감지 -->
{@ $is_mobile = Mobile::isFromMobilePhone() }

<!--@if($is_mobile)-->
<!-- 모바일용 목록 -->
<div class="mobile-list" cond="$document_list">
    <div class="mobile-item" loop="$document_list => $no, $document">
        <div class="item-header">
            <span class="category" cond="$document->get('category_srl')">
                {$category_list[$document->get('category_srl')]->title}
            </span>
            <span class="date">{zdate($document->getRegdate(), 'm.d')}</span>
        </div>

        <h3 class="item-title">
            <a href="{getUrl('document_srl', $document->document_srl)}">
                {$document->getTitle()}
            </a>

            <span class="badges">
                <span class="comment-badge" cond="$document->getCommentCount() > 0">
                    {$document->getCommentCount()}
                </span>
                <span class="new-badge" cond="$document->isNew()">N</span>
                <span class="file-badge" cond="$document->getAttachedFileCount() > 0">
                    <i class="icon-attachment"></i>
                </span>
            </span>
        </h3>

        <div class="item-meta">
            <span class="author">{$document->getNickName()}</span>
            <span class="stats">
                조회 {$document->getReadedCount()}
                <span cond="$module_info->use_vote == 'Y'">
                    · 추천 {$document->getVotedCount()}
                </span>
            </span>
        </div>
    </div>
</div>
<!--@else-->
<!-- 데스크탑용 테이블 목록 (위의 표준 테이블 형식) -->
<!--@endif-->

필터링과 정렬

고급 검색 폼

<div class="advanced-search">
    <form action="{getUrl()}" method="get" class="search-form-advanced">
        <input type="hidden" name="mid" value="{$mid}" />

        <div class="search-row">
            <!-- 카테고리 필터 -->
            <div class="search-field" cond="$category_list">
                <label>분류</label>
                <select name="category">
                    <option value="">전체</option>
                    <option loop="$category_list => $key, $val" 
                            value="{$key}" 
                            selected="selected"|cond="$category == $key">
                        {$val->title}
                    </option>
                </select>
            </div>

            <!-- 검색 대상 -->
            <div class="search-field">
                <label>검색 대상</label>
                <select name="search_target">
                    <option value="title">제목</option>
                    <option value="content">내용</option>
                    <option value="title_content">제목+내용</option>
                    <option value="nick_name">글쓴이</option>
                    <option value="user_id">아이디</option>
                </select>
            </div>

            <!-- 검색어 -->
            <div class="search-field">
                <label>검색어</label>
                <input type="text" name="search_keyword" value="{$search_keyword}" />
            </div>
        </div>

        <div class="search-row">
            <!-- 기간 검색 -->
            <div class="search-field">
                <label>작성일</label>
                <input type="date" name="start_date" value="{$start_date}" />
                <span>~</span>
                <input type="date" name="end_date" value="{$end_date}" />
            </div>

            <!-- 정렬 -->
            <div class="search-field">
                <label>정렬</label>
                <select name="order_type">
                    <option value="desc">최신순</option>
                    <option value="asc">오래된순</option>
                    <option value="title">제목순</option>
                    <option value="nick_name">글쓴이순</option>
                    <option value="readed_count">조회순</option>
                    <option value="voted_count">추천순</option>
                </select>
            </div>
        </div>

        <div class="search-actions">
            <button type="submit" class="btn btn-primary">검색</button>
            <button type="button" class="btn btn-secondary" onclick="location.href='{getUrl('search_keyword', '', 'category', '', 'start_date', '', 'end_date', '')}'">
                초기화
            </button>
        </div>
    </form>
</div>

CSS 스타일링

기본 목록 스타일

/* 게시판 목록 테이블 */
.board-list {
    width: 100%;
    border-collapse: collapse;
    margin: 20px 0;
}

.board-list th,
.board-list td {
    padding: 12px 8px;
    border-bottom: 1px solid #eee;
    text-align: center;
}

.board-list th {
    background: #f8f9fa;
    font-weight: 600;
    color: #495057;
}

.board-list .title {
    text-align: left;
    min-width: 300px;
}

.board-list .title a {
    color: #333;
    text-decoration: none;
}

.board-list .title a:hover {
    color: #007bff;
}

/* 공지사항 스타일 */
.board-list .notice {
    background: #fff8e1;
}

.board-list .notice .title a {
    font-weight: 600;
    color: #f57c00;
}

/* 답글 스타일 */
.reply-indent .reply-arrow {
    color: #999;
    margin-right: 2px;
}

/* 아이콘들 */
.comment-count {
    color: #007bff;
    font-size: 12px;
    margin-left: 5px;
}

.new-icon {
    color: #dc3545;
    font-size: 11px;
    font-weight: bold;
    margin-left: 3px;
}

.attach-icon {
    color: #28a745;
    margin-left: 3px;
}

.secret-icon {
    color: #6c757d;
    margin-left: 3px;
}

/* 카드형 목록 */
.board-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 20px;
    margin: 20px 0;
}

.document-card {
    border: 1px solid #eee;
    border-radius: 8px;
    overflow: hidden;
    transition: transform 0.2s, box-shadow 0.2s;
}

.document-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

.card-thumbnail {
    position: relative;
    height: 200px;
    overflow: hidden;
}

.card-thumbnail img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.no-thumbnail {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    background: #f8f9fa;
    color: #6c757d;
}

.category-badge {
    position: absolute;
    top: 10px;
    left: 10px;
    background: rgba(0,123,255,0.9);
    color: white;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 12px;
}

.card-content {
    padding: 15px;
}

.card-title {
    margin: 0 0 10px;
    font-size: 16px;
    line-height: 1.4;
}

.card-title a {
    color: #333;
    text-decoration: none;
}

.card-summary {
    color: #666;
    font-size: 14px;
    line-height: 1.4;
    margin: 0 0 15px;
}

.card-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 12px;
    color: #999;
}

/* 반응형 */
@media (max-width: 768px) {
    .board-list {
        font-size: 13px;
    }

    .board-list th,
    .board-list td {
        padding: 8px 4px;
    }

    .board-list .hit,
    .board-list .vote {
        display: none;
    }

    .board-grid {
        grid-template-columns: 1fr;
        gap: 15px;
    }
}