FAQ 게시판 만들기

FAQ 게시판 만들기

개요

아코디언 스타일의 FAQ(자주 묻는 질문) 게시판을 만드는 방법을 설명합니다.

디렉토리 구조

skins/board/faq/
├── board.default.css
├── board.default.js
├── list.html
├── write_form.html
├── view_document.html
├── info.xml
└── img/
    ├── icon-q.png
    └── icon-a.png

info.xml 설정

<?xml version="1.0" encoding="UTF-8"?>
<skin version="0.2">
    <title xml:lang="ko">FAQ 게시판</title>
    <title xml:lang="en">FAQ Board</title>
    <description xml:lang="ko">아코디언 스타일의 FAQ 게시판 스킨</description>
    <version>1.0.0</version>
    <author email_address="dev@example.com" link="https://example.com">
        <name xml:lang="ko">개발자</name>
    </author>

    <extra_vars>
        <var id="expand_all" type="checkbox">
            <title xml:lang="ko">모든 답변 펼치기</title>
            <default>N</default>
        </var>

        <var id="use_category_tab" type="checkbox">
            <title xml:lang="ko">카테고리 탭 사용</title>
            <default>Y</default>
        </var>

        <var id="animation_speed" type="select">
            <title xml:lang="ko">애니메이션 속도</title>
            <options value="fast">빠르게</options>
            <options value="normal">보통</options>
            <options value="slow">느리게</options>
            <default>normal</default>
        </var>

        <var id="icon_style" type="select">
            <title xml:lang="ko">아이콘 스타일</title>
            <options value="default">기본</options>
            <options value="circle">원형</options>
            <options value="square">사각형</options>
            <default>circle</default>
        </var>

        <var id="search_position" type="select">
            <title xml:lang="ko">검색창 위치</title>
            <options value="top">상단</options>
            <options value="bottom">하단</options>
            <options value="both">상단과 하단</options>
            <default>top</default>
        </var>
    </extra_vars>
</skin>

list.html - FAQ 목록

{@
    $expand_all = $module_info->expand_all == 'Y';
    $use_category_tab = $module_info->use_category_tab == 'Y';
    $animation_speed = $module_info->animation_speed ?: 'normal';
    $icon_style = $module_info->icon_style ?: 'circle';
    $search_position = $module_info->search_position ?: 'top';
}

<load target="board.default.css" />
<load target="board.default.js" />

<div class="faq-board" data-expand-all="{$expand_all}" data-animation-speed="{$animation_speed}">
    <!-- 제목 영역 -->
    <div class="faq-header">
        <h2>{$module_info->browser_title}</h2>
        <!--@if($module_info->description)-->
        <p class="description">{$module_info->description}</p>
        <!--@end-->
    </div>

    <!-- 검색 영역 (상단) -->
    <!--@if($search_position == 'top' || $search_position == 'both')-->
    <div class="faq-search-box">
        <form action="{getUrl()}" method="get" class="search-form">
            <input type="hidden" name="vid" value="{$vid}" />
            <input type="hidden" name="mid" value="{$mid}" />
            <input type="hidden" name="category" value="{$category}" />
            <div class="search-input-group">
                <input type="text" name="search_keyword" value="{$search_keyword}" 
                       placeholder="검색어를 입력하세요" class="search-input" />
                <button type="submit" class="search-button">
                    <i class="xi-search"></i> 검색
                </button>
            </div>
        </form>

        <!-- 인기 검색어 -->
        <!--@if($popular_tags)-->
        <div class="popular-tags">
            <span class="label">인기 검색어:</span>
            <!--@foreach($popular_tags as $tag)-->
            <a href="{getUrl('search_keyword', $tag->tag)}" class="tag">{$tag->tag}</a>
            <!--@end-->
        </div>
        <!--@end-->
    </div>
    <!--@end-->

    <!-- 카테고리 탭 -->
    <!--@if($use_category_tab && $module_info->use_category == 'Y' && $category_list)-->
    <div class="category-tabs">
        <ul class="tab-list">
            <li class="<!--@if(!$category)-->active<!--@end-->">
                <a href="{getUrl('category','','page','')}" data-category="0">
                    전체 ({$total_count})
                </a>
            </li>
            <!--@foreach($category_list as $val)-->
            <li class="<!--@if($category == $val->category_srl)-->active<!--@end-->">
                <a href="{getUrl('category', $val->category_srl, 'page', '')}" 
                   data-category="{$val->category_srl}">
                    {$val->title} ({$val->document_count})
                </a>
            </li>
            <!--@end-->
        </ul>
    </div>
    <!--@end-->

    <!-- FAQ 목록 -->
    <div class="faq-list icon-style-{$icon_style}">
        <!--@if(!$document_list || count($document_list) == 0)-->
        <div class="no-data">
            <i class="xi-info-o"></i>
            <p>등록된 FAQ가 없습니다.</p>
        </div>
        <!--@else-->

        <!--@foreach($document_list as $no => $document)-->
        {@
            // 답변 내용 추출 (확장변수 사용 시)
            $answer = $document->getExtraValueHTML('answer') ?: $document->getContent(false);
            $is_new = $document->isNew();
            $is_updated = $document->isUpdated();
        }

        <div class="faq-item" data-document-srl="{$document->document_srl}">
            <div class="faq-question">
                <span class="icon icon-q">Q</span>
                <div class="question-content">
                    <h3 class="title">
                        <!--@if($document->get('is_notice') == 'Y')-->
                        <span class="notice-badge">공지</span>
                        <!--@end-->

                        <!--@if($is_new)-->
                        <span class="new-badge">NEW</span>
                        <!--@end-->

                        <!--@if($is_updated)-->
                        <span class="update-badge">UPDATE</span>
                        <!--@end-->

                        {$document->getTitleText()}
                    </h3>

                    <!--@if($module_info->use_category == 'Y' && $document->get('category_srl'))-->
                    <span class="category">{$category_list[$document->get('category_srl')]->title}</span>
                    <!--@end-->
                </div>
                <button class="toggle-button" aria-expanded="false">
                    <i class="xi-angle-down"></i>
                </button>
            </div>

            <div class="faq-answer" style="display: none;">
                <span class="icon icon-a">A</span>
                <div class="answer-content">
                    {$answer}

                    <!-- 첨부파일 -->
                    <!--@if($document->hasUploadedFiles())-->
                    <div class="attached-files">
                        <h4><i class="xi-paperclip"></i> 첨부파일</h4>
                        <ul>
                            {@$uploaded_list = $document->getUploadedFiles()}
                            <!--@foreach($uploaded_list as $file)-->
                            <li>
                                <a href="{$file->download_url}" class="file-link">
                                    <i class="xi-download"></i>
                                    {$file->source_filename} 
                                    <span class="file-size">({FileHandler::filesize($file->file_size)})</span>
                                </a>
                            </li>
                            <!--@end-->
                        </ul>
                    </div>
                    <!--@end-->

                    <!-- 메타 정보 -->
                    <div class="answer-meta">
                        <span class="author">작성자: {$document->getNickName()}</span>
                        <span class="date">작성일: {$document->getRegdate('Y.m.d')}</span>
                        <!--@if($document->get('last_update'))-->
                        <span class="update">수정일: {$document->getUpdate('Y.m.d')}</span>
                        <!--@end-->
                        <span class="views">조회수: {$document->get('readed_count')}</span>
                    </div>

                    <!-- 유용함 투표 -->
                    <div class="helpfulness">
                        <p>이 답변이 도움이 되셨나요?</p>
                        <button class="vote-button vote-up" data-vote="up">
                            <i class="xi-thumbs-up"></i> 예 
                            <span class="count">({$document->get('voted_count')})</span>
                        </button>
                        <button class="vote-button vote-down" data-vote="down">
                            <i class="xi-thumbs-down"></i> 아니오
                            <span class="count">({$document->get('blamed_count')})</span>
                        </button>
                    </div>
                </div>
            </div>
        </div>
        <!--@end-->

        <!--@end-->
    </div>

    <!-- 페이지네이션 -->
    <!--@if($page_navigation)-->
    <div class="pagination">
        <a href="{getUrl('page','')}" class="first-page" title="첫 페이지">
            <i class="xi-angle-left-min"></i>
        </a>

        <!--@if($page_navigation->first_page > 1)-->
        <a href="{getUrl('page', $page_navigation->first_page - 1)}" class="prev-block">
            <i class="xi-angle-left"></i>
        </a>
        <!--@end-->

        <!--@foreach($page_navigation->page_list as $val)-->
        <a href="{getUrl('page', $val)}" class="page-number <!--@if($page == $val)-->active<!--@end-->">
            {$val}
        </a>
        <!--@end-->

        <!--@if($page_navigation->last_page < $page_navigation->total_page)-->
        <a href="{getUrl('page', $page_navigation->last_page + 1)}" class="next-block">
            <i class="xi-angle-right"></i>
        </a>
        <!--@end-->

        <a href="{getUrl('page', $page_navigation->last_page)}" class="last-page" title="마지막 페이지">
            <i class="xi-angle-right-min"></i>
        </a>
    </div>
    <!--@end-->

    <!-- 검색 영역 (하단) -->
    <!--@if($search_position == 'bottom' || $search_position == 'both')-->
    <div class="faq-search-box bottom">
        <!-- 상단과 동일한 검색 폼 -->
    </div>
    <!--@end-->

    <!-- 관리자 버튼 -->
    <!--@if($grant->write_document || $grant->manager)-->
    <div class="admin-buttons">
        <!--@if($grant->write_document)-->
        <a href="{getUrl('act','dispBoardWrite')}" class="btn btn-primary">
            <i class="xi-pen"></i> FAQ 작성
        </a>
        <!--@end-->

        <!--@if($grant->manager)-->
        <a href="{getUrl('act','dispBoardAdminBoardInfo')}" class="btn btn-secondary">
            <i class="xi-cog"></i> 게시판 설정
        </a>
        <!--@end-->
    </div>
    <!--@end-->
</div>

board.default.css - 스타일시트

/* FAQ 게시판 기본 스타일 */
.faq-board {
    max-width: 900px;
    margin: 0 auto;
    padding: 20px;
}

/* 헤더 */
.faq-header {
    text-align: center;
    margin-bottom: 40px;
}

.faq-header h2 {
    font-size: 28px;
    margin-bottom: 10px;
}

.faq-header .description {
    color: #666;
    font-size: 16px;
}

/* 검색 영역 */
.faq-search-box {
    background: #f8f9fa;
    padding: 20px;
    border-radius: 8px;
    margin-bottom: 30px;
}

.search-form {
    max-width: 600px;
    margin: 0 auto;
}

.search-input-group {
    display: flex;
    gap: 10px;
}

.search-input {
    flex: 1;
    padding: 12px 16px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 14px;
}

.search-button {
    padding: 12px 24px;
    background: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.3s;
}

.search-button:hover {
    background: #0056b3;
}

/* 인기 태그 */
.popular-tags {
    margin-top: 15px;
    text-align: center;
}

.popular-tags .label {
    color: #666;
    margin-right: 10px;
}

.popular-tags .tag {
    display: inline-block;
    padding: 5px 12px;
    margin: 2px;
    background: #e9ecef;
    color: #495057;
    border-radius: 20px;
    text-decoration: none;
    font-size: 13px;
    transition: all 0.3s;
}

.popular-tags .tag:hover {
    background: #007bff;
    color: white;
}

/* 카테고리 탭 */
.category-tabs {
    margin-bottom: 30px;
    border-bottom: 2px solid #e9ecef;
}

.tab-list {
    display: flex;
    list-style: none;
    margin: 0;
    padding: 0;
}

.tab-list li {
    margin-right: 5px;
}

.tab-list a {
    display: block;
    padding: 12px 20px;
    color: #666;
    text-decoration: none;
    border: 1px solid transparent;
    border-bottom: none;
    transition: all 0.3s;
}

.tab-list li.active a,
.tab-list a:hover {
    color: #007bff;
    background: #f8f9fa;
    border-color: #e9ecef;
    border-bottom: 2px solid white;
    margin-bottom: -2px;
}

/* FAQ 아이템 */
.faq-item {
    border: 1px solid #e9ecef;
    border-radius: 8px;
    margin-bottom: 10px;
    overflow: hidden;
    transition: box-shadow 0.3s;
}

.faq-item:hover {
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.faq-item.active {
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

/* 질문 영역 */
.faq-question {
    display: flex;
    align-items: center;
    padding: 20px;
    cursor: pointer;
    background: #fff;
    transition: background 0.3s;
}

.faq-question:hover {
    background: #f8f9fa;
}

/* 아이콘 스타일 */
.icon {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    margin-right: 15px;
}

.icon-q {
    background: #007bff;
    color: white;
}

.icon-a {
    background: #28a745;
    color: white;
}

/* 아이콘 스타일 - 원형 */
.icon-style-circle .icon {
    border-radius: 50%;
}

/* 아이콘 스타일 - 사각형 */
.icon-style-square .icon {
    border-radius: 4px;
}

/* 질문 내용 */
.question-content {
    flex: 1;
}

.question-content .title {
    font-size: 16px;
    margin: 0;
    line-height: 1.5;
}

/* 배지 */
.notice-badge,
.new-badge,
.update-badge {
    display: inline-block;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: normal;
    border-radius: 3px;
    margin-right: 5px;
}

.notice-badge {
    background: #dc3545;
    color: white;
}

.new-badge {
    background: #007bff;
    color: white;
}

.update-badge {
    background: #ffc107;
    color: #212529;
}

/* 카테고리 */
.category {
    display: inline-block;
    margin-left: 10px;
    padding: 2px 8px;
    background: #e9ecef;
    color: #666;
    font-size: 12px;
    border-radius: 3px;
}

/* 토글 버튼 */
.toggle-button {
    background: none;
    border: none;
    font-size: 20px;
    color: #666;
    cursor: pointer;
    padding: 10px;
    transition: transform 0.3s;
}

.faq-item.active .toggle-button {
    transform: rotate(180deg);
}

/* 답변 영역 */
.faq-answer {
    display: none;
    padding: 0 20px 20px;
    background: #f8f9fa;
    border-top: 1px solid #e9ecef;
}

.faq-answer {
    display: flex;
    align-items: flex-start;
}

.answer-content {
    flex: 1;
    line-height: 1.8;
}

/* 답변 내용 스타일 */
.answer-content p {
    margin: 0 0 15px;
}

.answer-content ul,
.answer-content ol {
    margin: 0 0 15px;
    padding-left: 20px;
}

.answer-content img {
    max-width: 100%;
    height: auto;
    margin: 10px 0;
}

/* 첨부파일 */
.attached-files {
    margin-top: 20px;
    padding: 15px;
    background: white;
    border-radius: 4px;
}

.attached-files h4 {
    font-size: 14px;
    margin: 0 0 10px;
}

.attached-files ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.attached-files li {
    margin-bottom: 5px;
}

.file-link {
    color: #007bff;
    text-decoration: none;
}

.file-link:hover {
    text-decoration: underline;
}

.file-size {
    color: #666;
    font-size: 12px;
}

/* 메타 정보 */
.answer-meta {
    margin-top: 20px;
    padding-top: 20px;
    border-top: 1px solid #dee2e6;
    font-size: 13px;
    color: #666;
}

.answer-meta span {
    margin-right: 15px;
}

/* 유용함 투표 */
.helpfulness {
    margin-top: 20px;
    padding: 15px;
    background: white;
    border-radius: 4px;
    text-align: center;
}

.helpfulness p {
    margin: 0 0 10px;
    font-size: 14px;
}

.vote-button {
    padding: 8px 16px;
    margin: 0 5px;
    background: white;
    border: 1px solid #ddd;
    border-radius: 4px;
    cursor: pointer;
    transition: all 0.3s;
}

.vote-button:hover {
    background: #f8f9fa;
}

.vote-button.voted {
    background: #007bff;
    color: white;
    border-color: #007bff;
}

/* 데이터 없음 */
.no-data {
    text-align: center;
    padding: 60px 20px;
    color: #666;
}

.no-data i {
    font-size: 48px;
    color: #dee2e6;
    margin-bottom: 20px;
}

/* 페이지네이션 */
.pagination {
    text-align: center;
    margin: 40px 0;
}

.pagination a {
    display: inline-block;
    padding: 8px 12px;
    margin: 0 2px;
    color: #666;
    text-decoration: none;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    transition: all 0.3s;
}

.pagination a:hover,
.pagination a.active {
    background: #007bff;
    color: white;
    border-color: #007bff;
}

/* 관리자 버튼 */
.admin-buttons {
    text-align: center;
    margin-top: 40px;
}

.btn {
    display: inline-block;
    padding: 10px 20px;
    margin: 0 5px;
    text-decoration: none;
    border-radius: 4px;
    transition: all 0.3s;
}

.btn-primary {
    background: #007bff;
    color: white;
}

.btn-primary:hover {
    background: #0056b3;
}

.btn-secondary {
    background: #6c757d;
    color: white;
}

.btn-secondary:hover {
    background: #545b62;
}

/* 반응형 디자인 */
@media (max-width: 768px) {
    .faq-board {
        padding: 10px;
    }

    .faq-header h2 {
        font-size: 24px;
    }

    .search-input-group {
        flex-direction: column;
    }

    .search-button {
        width: 100%;
    }

    .tab-list {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .faq-question {
        padding: 15px;
    }

    .icon {
        width: 32px;
        height: 32px;
        font-size: 14px;
    }

    .question-content .title {
        font-size: 14px;
    }

    .answer-meta span {
        display: block;
        margin-bottom: 5px;
    }
}

board.default.js - JavaScript

jQuery(function($) {
    const $faqBoard = $('.faq-board');
    const expandAll = $faqBoard.data('expand-all');
    const animationSpeed = $faqBoard.data('animation-speed');

    // 애니메이션 속도 설정
    const speeds = {
        fast: 200,
        normal: 400,
        slow: 600
    };
    const speed = speeds[animationSpeed] || 400;

    // 모든 답변 펼치기 옵션
    if (expandAll) {
        $('.faq-answer').show();
        $('.faq-item').addClass('active');
        $('.toggle-button').attr('aria-expanded', 'true');
    }

    // FAQ 토글
    $('.faq-question').on('click', function(e) {
        e.preventDefault();

        const $item = $(this).closest('.faq-item');
        const $answer = $item.find('.faq-answer');
        const $button = $item.find('.toggle-button');
        const isExpanded = $button.attr('aria-expanded') === 'true';

        // 다른 항목 닫기 (아코디언 효과)
        if (!expandAll) {
            $('.faq-item').not($item).removeClass('active');
            $('.faq-answer').not($answer).slideUp(speed);
            $('.toggle-button').not($button).attr('aria-expanded', 'false');
        }

        // 현재 항목 토글
        $item.toggleClass('active');
        $answer.slideToggle(speed);
        $button.attr('aria-expanded', !isExpanded);
    });

    // 직접 링크로 접근 시 해당 FAQ 열기
    const hash = window.location.hash;
    if (hash) {
        const documentSrl = hash.replace('#faq-', '');
        const $targetItem = $(`.faq-item[data-document-srl="${documentSrl}"]`);
        if ($targetItem.length) {
            $targetItem.addClass('active');
            $targetItem.find('.faq-answer').show();
            $targetItem.find('.toggle-button').attr('aria-expanded', 'true');

            // 스크롤 이동
            $('html, body').animate({
                scrollTop: $targetItem.offset().top - 100
            }, 500);
        }
    }

    // 검색 기능 강화
    let searchTimer;
    $('.search-input').on('input', function() {
        clearTimeout(searchTimer);
        const keyword = $(this).val().toLowerCase();

        searchTimer = setTimeout(function() {
            if (keyword.length >= 2) {
                $('.faq-item').each(function() {
                    const $item = $(this);
                    const title = $item.find('.title').text().toLowerCase();
                    const content = $item.find('.answer-content').text().toLowerCase();

                    if (title.includes(keyword) || content.includes(keyword)) {
                        $item.show();
                        // 검색어 하이라이트
                        highlightKeyword($item, keyword);
                    } else {
                        $item.hide();
                    }
                });

                // 검색 결과 없음 표시
                if ($('.faq-item:visible').length === 0) {
                    if (!$('.no-search-result').length) {
                        $('.faq-list').append(`
                            <div class="no-search-result">
                                <i class="xi-search"></i>
                                <p>"${keyword}"에 대한 검색 결과가 없습니다.</p>
                            </div>
                        `);
                    }
                } else {
                    $('.no-search-result').remove();
                }
            } else {
                $('.faq-item').show();
                $('.no-search-result').remove();
                removeHighlight();
            }
        }, 300);
    });

    // 검색어 하이라이트
    function highlightKeyword($element, keyword) {
        const regex = new RegExp(`(${keyword})`, 'gi');

        $element.find('.title, .answer-content').each(function() {
            const $this = $(this);
            const html = $this.html();
            const highlighted = html.replace(regex, '<mark>$1</mark>');
            $this.html(highlighted);
        });
    }

    // 하이라이트 제거
    function removeHighlight() {
        $('mark').each(function() {
            const $this = $(this);
            $this.replaceWith($this.text());
        });
    }

    // 유용함 투표
    $('.vote-button').on('click', function(e) {
        e.stopPropagation();

        const $button = $(this);
        const $item = $button.closest('.faq-item');
        const documentSrl = $item.data('document-srl');
        const voteType = $button.data('vote');

        // 이미 투표한 경우
        if ($button.hasClass('voted')) {
            alert('이미 평가하셨습니다.');
            return;
        }

        // AJAX 요청
        $.ajax({
            url: request_uri,
            type: 'POST',
            data: {
                module: 'document',
                act: voteType === 'up' ? 'procDocumentVoteUp' : 'procDocumentVoteDown',
                target_srl: documentSrl
            },
            success: function(response) {
                if (response.error) {
                    alert(response.message);
                } else {
                    $button.addClass('voted');
                    const $count = $button.find('.count');
                    const currentCount = parseInt($count.text().match(/\d+/)[0]);
                    $count.text(`(${currentCount + 1})`);

                    // 다른 버튼 비활성화
                    $button.siblings('.vote-button').prop('disabled', true);
                }
            }
        });
    });

    // 카테고리 필터 (AJAX)
    $('.tab-list a').on('click', function(e) {
        if ($(this).data('ajax') === 'true') {
            e.preventDefault();

            const $link = $(this);
            const categorySrl = $link.data('category');

            // 탭 활성화
            $('.tab-list li').removeClass('active');
            $link.parent().addClass('active');

            // FAQ 목록 로드
            $.ajax({
                url: request_uri,
                type: 'GET',
                data: {
                    mid: current_mid,
                    category: categorySrl,
                    page: 1
                },
                beforeSend: function() {
                    $('.faq-list').html('<div class="loading"><i class="xi-spinner-3 xi-spin"></i> 로딩 중...</div>');
                },
                success: function(response) {
                    const $newContent = $(response).find('.faq-list').html();
                    $('.faq-list').html($newContent);

                    // 이벤트 재바인딩
                    bindEvents();
                }
            });
        }
    });

    // 키보드 접근성
    $('.faq-item').attr('tabindex', '0');
    $('.faq-item').on('keydown', function(e) {
        if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            $(this).find('.faq-question').click();
        }
    });

    // 인쇄 시 모든 답변 표시
    window.onbeforeprint = function() {
        $('.faq-answer').show();
    };

    window.onafterprint = function() {
        if (!expandAll) {
            $('.faq-item:not(.active) .faq-answer').hide();
        }
    };
});

write_form.html - FAQ 작성 폼

<load target="write_form.css" />

<div class="faq-write-form">
    <h2>FAQ 작성</h2>

    <form action="{getUrl()}" method="post" enctype="multipart/form-data">
        <input type="hidden" name="module" value="board" />
        <input type="hidden" name="act" value="procBoardInsertDocument" />
        <input type="hidden" name="mid" value="{$mid}" />
        <input type="hidden" name="document_srl" value="{$document_srl}" />

        <!-- 카테고리 -->
        <!--@if($module_info->use_category == 'Y' && $category_list)-->
        <div class="form-group">
            <label for="category_srl">카테고리 <span class="required">*</span></label>
            <select name="category_srl" id="category_srl" required>
                <option value="">카테고리 선택</option>
                <!--@foreach($category_list as $val)-->
                <option value="{$val->category_srl}" <!--@if($oDocument->get('category_srl') == $val->category_srl)-->selected<!--@end-->>
                    {$val->title}
                </option>
                <!--@end-->
            </select>
        </div>
        <!--@end-->

        <!-- 질문 -->
        <div class="form-group">
            <label for="title">질문 <span class="required">*</span></label>
            <input type="text" name="title" id="title" value="{$oDocument->getTitleText()}" 
                   placeholder="자주 묻는 질문을 입력하세요" required />
            <p class="help-text">명확하고 구체적인 질문을 작성해주세요.</p>
        </div>

        <!-- 답변 -->
        <div class="form-group">
            <label for="content">답변 <span class="required">*</span></label>
            {@$oDocument->getEditor()}
            <p class="help-text">상세하고 이해하기 쉬운 답변을 작성해주세요.</p>
        </div>

        <!-- 첨부파일 -->
        <!--@if($module_info->use_file_box)-->
        <div class="form-group">
            <label>첨부파일</label>
            {@$oDocument->printUploadForm()}
        </div>
        <!--@end-->

        <!-- 추가 옵션 -->
        <div class="form-options">
            <!--@if($grant->manager)-->
            <label>
                <input type="checkbox" name="is_notice" value="Y" 
                       <!--@if($oDocument->isNotice())-->checked<!--@end--> />
                공지사항으로 등록
            </label>
            <!--@end-->

            <label>
                <input type="checkbox" name="notify_message" value="Y" 
                       <!--@if($oDocument->useNotify())-->checked<!--@end--> />
                댓글 알림 받기
            </label>
        </div>

        <!-- 버튼 -->
        <div class="form-buttons">
            <button type="submit" class="btn btn-primary">
                <i class="xi-check"></i> 저장
            </button>
            <a href="{getUrl('act','')}" class="btn btn-secondary">
                <i class="xi-close"></i> 취소
            </a>
        </div>
    </form>
</div>

<style>
.faq-write-form {
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
}

.form-group {
    margin-bottom: 25px;
}

.form-group label {
    display: block;
    font-weight: bold;
    margin-bottom: 8px;
}

.form-group input[type="text"],
.form-group select {
    width: 100%;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
}

.required {
    color: #dc3545;
}

.help-text {
    font-size: 13px;
    color: #666;
    margin-top: 5px;
}

.form-options {
    margin: 20px 0;
}

.form-options label {
    display: block;
    margin-bottom: 10px;
}

.form-buttons {
    text-align: center;
    margin-top: 30px;
}
</style>

관련 문서