게시판 스킨 기본 구조

게시판 스킨 기본 구조

게시판 스킨이란?

게시판 스킨은 게시판 모듈의 화면을 담당하는 템플릿입니다. 목록, 글쓰기, 글보기, 댓글 등 게시판의 모든 화면을 정의합니다.

필수 파일 구조

skins/board/
└── my_board_skin/
    ├── conf/
    │   └── info.xml              # 스킨 정보
    ├── css/
    │   └── board.css             # 스타일시트
    ├── js/
    │   └── board.js              # 자바스크립트
    ├── images/                   # 이미지 파일들
    ├── list.html                 # 목록 화면 (필수)
    ├── view.html                 # 글보기 화면 (필수)
    ├── write_form.html           # 글쓰기 폼 (필수)
    ├── delete_form.html          # 글삭제 폼 (필수)
    ├── comment.html              # 댓글 템플릿 (필수)
    ├── comment_form.html         # 댓글 폼 (필수)
    ├── file_link.html            # 첨부파일 링크
    ├── file_image.html           # 이미지 첨부파일
    └── _header.html              # 공통 헤더 (선택)

info.xml 작성

<?xml version="1.0" encoding="UTF-8"?>
<skin version="0.2">
    <title xml:lang="ko">커스텀 게시판 스킨</title>
    <description xml:lang="ko">사용자 정의 게시판 스킨입니다.</description>
    <version>1.0</version>
    <date>2024-01-01</date>

    <author email="user@example.com">
        <name xml:lang="ko">제작자명</name>
    </author>

    <!-- 스킨 설정 변수 -->
    <extra_vars>
        <var name="colorset" type="select">
            <title xml:lang="ko">컬러셋</title>
            <description xml:lang="ko">게시판 테마를 선택하세요</description>
            <options value="default">
                <title xml:lang="ko">기본</title>
            </options>
            <options value="blue">
                <title xml:lang="ko">블루</title>
            </options>
            <options value="green">
                <title xml:lang="ko">그린</title>
            </options>
        </var>

        <var name="list_count" type="text">
            <title xml:lang="ko">목록 개수</title>
            <description xml:lang="ko">한 페이지에 표시할 게시글 수</description>
            <default>20</default>
        </var>

        <var name="thumbnail_width" type="text">
            <title xml:lang="ko">썸네일 너비</title>
            <description xml:lang="ko">목록에서 썸네일 이미지 너비 (px)</description>
            <default>150</default>
        </var>

        <var name="show_category" type="select">
            <title xml:lang="ko">카테고리 표시</title>
            <options value="Y">
                <title xml:lang="ko">표시</title>
            </options>
            <options value="N">
                <title xml:lang="ko">숨김</title>
            </options>
        </var>
    </extra_vars>
</skin>

주요 변수들

시스템 변수

<!-- 모듈 정보 -->
{$module_info->mid}              <!-- 모듈 아이디 -->
{$module_info->browser_title}    <!-- 브라우저 제목 -->
{$module_info->description}      <!-- 모듈 설명 -->

<!-- 권한 정보 -->
{$grant->write}                  <!-- 글쓰기 권한 -->
{$grant->view}                   <!-- 글보기 권한 -->
{$grant->comment}                <!-- 댓글 권한 -->
{$grant->is_admin}               <!-- 관리자 권한 -->

<!-- 페이지 정보 -->
{$page}                          <!-- 현재 페이지 -->
{$total_count}                   <!-- 전체 게시글 수 -->
{$page_navigation}               <!-- 페이지 네비게이션 -->

<!-- 현재 액션 -->
{$act}                           <!-- 현재 실행 중인 액션 -->

스킨 설정 변수

<!-- 스킨 설정값 사용 -->
{$skin_vars->colorset}           <!-- 선택한 컬러셋 -->
{$skin_vars->list_count}         <!-- 목록 개수 -->
{$skin_vars->thumbnail_width}    <!-- 썸네일 너비 -->
{$skin_vars->show_category}      <!-- 카테고리 표시 여부 -->

공통 헤더 패턴

_header.html 활용

<!-- _header.html -->
{@
    // 스킨 설정
    $colorset = $skin_vars->colorset ?: 'default';
    $show_category = $skin_vars->show_category == 'Y';
}

<!-- CSS 로드 -->
<load target="css/board.css" />
<load target="js/board.js" />

<!-- 컬러셋별 CSS -->
<load target="css/colorset_{$colorset}.css" cond="$colorset != 'default'" />

<div class="board-container colorset-{$colorset}">
    <!-- 게시판 헤더 -->
    <div class="board-header">
        <h1 class="board-title">{$module_info->browser_title}</h1>
        <p class="board-description" cond="$module_info->description">
            {$module_info->description}
        </p>

        <!-- 카테고리 목록 -->
        <div class="category-list" cond="$show_category && $category_list">
            <ul>
                <li><a href="{getUrl('category', '')}" class="active"|cond="!$category">전체</a></li>
                <li loop="$category_list => $key, $val">
                    <a href="{getUrl('category', $key)}" 
                       class="active"|cond="$category == $key">
                        {$val->title} ({$val->count})
                    </a>
                </li>
            </ul>
        </div>
    </div>

각 파일에서 헤더 포함

<!-- list.html, view.html 등에서 -->
<include target="_header.html" />

<!-- 여기에 각 페이지별 내용 -->

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

액션별 파일 매핑

주요 액션과 템플릿 파일

dispBoardContent          → list.html
dispBoardWrite           → write_form.html
dispBoardModify          → write_form.html
dispBoardView            → view.html
dispBoardDelete          → delete_form.html
procBoardInsertDocument  → write_form.html (오류 시)

액션 체크 패턴

<!-- write_form.html에서 신규/수정 구분 -->
<!--@if($act == 'dispBoardModify')-->
    <h2>글 수정</h2>
    <input type="hidden" name="document_srl" value="{$oDocument->document_srl}" />
<!--@else-->
    <h2>글 쓰기</h2>
<!--@endif-->

반응형 구조

모바일 우선 접근법

/* 기본 (모바일) 스타일 */
.board-container {
    padding: 10px;
    font-size: 14px;
}

.board-list table {
    width: 100%;
    font-size: 13px;
}

/* 태블릿 */
@media (min-width: 768px) {
    .board-container {
        padding: 20px;
        font-size: 15px;
    }

    .board-list table {
        font-size: 14px;
    }
}

/* 데스크탑 */
@media (min-width: 1024px) {
    .board-container {
        padding: 30px;
        font-size: 16px;
    }

    .board-list table {
        font-size: 15px;
    }
}

모바일 감지

{@
    // 모바일 여부 체크
    $is_mobile = Mobile::isFromMobilePhone();
}

<!--@if($is_mobile)-->
    <!-- 모바일 전용 레이아웃 -->
    <div class="mobile-board">
        <!-- 모바일 목록 -->
    </div>
<!--@else-->
    <!-- 데스크탑 레이아웃 -->
    <table class="board-table">
        <!-- 테이블 목록 -->
    </table>
<!--@endif-->

스킨 상속

부모 스킨 확장

<!-- info.xml에서 부모 스킨 지정 -->
<skin version="0.2">
    <title xml:lang="ko">확장 게시판 스킨</title>
    <parent>default</parent>  <!-- 부모 스킨 지정 -->

    <!-- 추가 설정 -->
    <extra_vars>
        <var name="custom_option" type="text">
            <title xml:lang="ko">커스텀 옵션</title>
        </var>
    </extra_vars>
</skin>

필요한 파일만 재정의하면 부모 스킨의 나머지 파일들을 상속받습니다.

다국어 지원

언어 파일 구조

lang/
├── ko.php                <!-- 한국어 -->
├── en.php                <!-- 영어 -->
└── jp.php                <!-- 일본어 -->

ko.php 예제

<?php
$lang->board_list = '목록';
$lang->board_write = '글쓰기';
$lang->board_view = '상세보기';
$lang->board_modify = '수정';
$lang->board_delete = '삭제';
$lang->board_reply = '답글';
$lang->no_documents = '등록된 게시글이 없습니다.';
?>

템플릿에서 사용

<button type="button">{$lang->board_write}</button>
<p cond="!$document_list">{$lang->no_documents}</p>

캐싱 고려사항

스킨 캐시 무효화

// 스킨 파일 수정 후 캐시 삭제
{@
    if(__DEBUG__) {
        // 개발 중에는 캐시 사용 안 함
        Context::set('_use_cache', false);
    }
}

조건부 캐싱

<!-- 자주 변경되지 않는 부분만 캐싱 -->
<!--@cache('board_categories_' . $module_info->module_srl, 3600)-->
<div class="category-list">
    <!-- 카테고리 목록 -->
</div>
<!--@endcache-->

디버깅 도구

스킨 변수 확인

<!--@if(__DEBUG__ && $logged_info->is_admin == 'Y')-->
<div class="debug-panel" style="background: #f0f0f0; padding: 10px; margin: 10px 0;">
    <h4>디버그 정보</h4>
    <details>
        <summary>스킨 변수</summary>
        <pre>{print_r($skin_vars, true)}</pre>
    </details>
    <details>
        <summary>모듈 정보</summary>
        <pre>{print_r($module_info, true)}</pre>
    </details>
</div>
<!--@endif-->

개발 모드 표시

<!--@if(__DEBUG__)-->
<div class="dev-notice" style="background: #ff0; padding: 5px; text-align: center;">
    개발 모드 - 이 메시지는 운영 환경에서는 표시되지 않습니다.
</div>
<!--@endif-->