위젯 시스템¶
라이믹스 레이아웃에서 위젯을 활용하는 고급 기법을 학습합니다.
위젯 없이 최근게시물 출력¶
기본편 - 단일 게시판¶
<!-- 위젯 없이 최근게시물 코드 삽입하기 (기본편) -->
{@
// 특정 게시판의 최근 게시물 가져오기
$board_srl = 123; // 게시판 번호
$args = new stdClass();
$args->module_srl = $board_srl;
$args->list_count = 5;
$args->sort_index = 'list_order';
$args->order_type = 'asc';
$args->status = 'PUBLIC';
$oDocumentModel = getModel('document');
$output = $oDocumentModel->getDocumentList($args);
$recent_documents = $output->data;
}
<div class="recent-posts">
<h3>최근 게시물</h3>
<ul class="post-list">
<li loop="$recent_documents=>$document">
<a href="{getUrl('', 'mid', 'board', 'document_srl', $document->document_srl)}">
{$document->getTitle()}
<span class="date">{$document->getRegdate('m.d')}</span>
</a>
</li>
</ul>
</div>
활용편 - 다중 게시판¶
<!-- 위젯 없이 최근게시물 코드 삽입하기 (활용편) -->
{@
// 여러 게시판의 최근 게시물 통합
$board_srls = array(123, 456, 789); // 게시판 번호들
$args = new stdClass();
$args->module_srl = $board_srls;
$args->list_count = 10;
$args->sort_index = 'regdate';
$args->order_type = 'desc';
$args->status = array('PUBLIC', 'SECRET');
// 카테고리 필터
$args->category_srl = 111; // 특정 카테고리만
// 확장변수 필터
$args->extra_vars = array(
'product_type' => 'featured'
);
$oDocumentModel = getModel('document');
$output = $oDocumentModel->getDocumentList($args);
$documents = $output->data;
// 게시판 정보 미리 로드
$module_info = array();
$oModuleModel = getModel('module');
foreach($board_srls as $module_srl) {
$module_info[$module_srl] = $oModuleModel->getModuleInfoByModuleSrl($module_srl);
}
}
<div class="integrated-recent">
<h3>전체 최신글</h3>
<table class="recent-table">
<thead>
<tr>
<th>게시판</th>
<th>제목</th>
<th>작성자</th>
<th>날짜</th>
</tr>
</thead>
<tbody>
<tr loop="$documents=>$document">
{@$board_info = $module_info[$document->get('module_srl')]}
<td class="board-name">
<a href="{getUrl('', 'mid', $board_info->mid)}">
{$board_info->browser_title}
</a>
</td>
<td class="title">
<a href="{getUrl('', 'mid', $board_info->mid, 'document_srl', $document->document_srl)}">
{$document->getTitle()}
<span class="comment-count" cond="$document->getCommentCount()">
[{$document->getCommentCount()}]
</span>
</a>
</td>
<td class="author">{$document->getNickName()}</td>
<td class="date">{$document->getRegdate('Y.m.d')}</td>
</tr>
</tbody>
</table>
</div>
고급활용편 - 커스텀 쿼리¶
<!-- 위젯 없이 최근게시물 코드 삽입하기 (고급활용편) -->
{@
// 복잡한 조건의 게시물 가져오기
$args = new stdClass();
$args->module_srl = array(123, 456, 789);
$args->list_count = 20;
$args->page = 1;
// 기간 조건 (최근 7일)
$args->start_date = date('YmdHis', strtotime('-7 days'));
$args->end_date = date('YmdHis');
// 추천수 조건
$args->voted_count = 5; // 5개 이상
// 검색 조건
$args->search_target = 'title_content';
$args->search_keyword = '라이믹스';
// 제외할 문서
$args->exclude_document = array(111, 222, 333);
// 회원 그룹 필터
$args->member_group = array(1, 2); // 특정 그룹 회원의 글만
// 직접 쿼리 실행
$output = executeQuery('document.getDocumentList', $args);
$documents = $output->data;
// 썸네일 미리 생성
foreach($documents as $document) {
if($document->thumbnailExists()) {
$document->thumbnail = $document->getThumbnail(200, 150);
}
}
}
<div class="advanced-recent">
<h3>인기 게시물 (최근 7일)</h3>
<div class="grid-layout">
<article loop="$documents=>$document" class="grid-item">
<!-- 썸네일 -->
<div class="thumbnail">
<a href="{getUrl('', 'document_srl', $document->document_srl)}">
<img cond="$document->thumbnail" src="{$document->thumbnail}" alt="" />
<span cond="!$document->thumbnail" class="no-thumb">No Image</span>
</a>
</div>
<!-- 내용 -->
<div class="content">
<h4 class="title">
<a href="{getUrl('', 'document_srl', $document->document_srl)}">
{$document->getTitle()}
</a>
</h4>
<p class="excerpt">
{$document->getSummary(100)}
</p>
<div class="meta">
<span class="author">{$document->getNickName()}</span>
<span class="voted">추천 {$document->get('voted_count')}</span>
</div>
</div>
</article>
</div>
</div>
전체 코멘트 출력¶
최근 댓글 위젯¶
<!-- 전체 코멘트 출력하기 -->
{@
// 최근 댓글 가져오기
$args = new stdClass();
$args->list_count = 10;
$args->sort_index = 'list_order';
$args->order_type = 'asc';
// 특정 모듈만
$args->module_srl = array(123, 456, 789);
// 비밀댓글 제외
$args->is_secret = 'N';
$oCommentModel = getModel('comment');
$output = $oCommentModel->getTotalCommentList($args);
$comment_list = $output->data;
// 문서 정보 미리 로드
$document_srls = array();
foreach($comment_list as $comment) {
$document_srls[] = $comment->document_srl;
}
$oDocumentModel = getModel('document');
$documents = $oDocumentModel->getDocuments($document_srls);
}
<div class="recent-comments">
<h3>최근 댓글</h3>
<ul class="comment-list">
<li loop="$comment_list=>$comment">
{@$document = $documents[$comment->document_srl]}
<div class="comment-item">
<!-- 프로필 이미지 -->
<div class="profile">
{@$profile_image = $comment->getProfileImage()}
<img cond="$profile_image" src="{$profile_image->src}" alt="" />
<span cond="!$profile_image" class="no-profile">
{substr($comment->getNickName(), 0, 1)}
</span>
</div>
<!-- 댓글 내용 -->
<div class="comment-content">
<div class="meta">
<strong>{$comment->getNickName()}</strong>
<span class="date">{$comment->getRegdate('m.d H:i')}</span>
</div>
<p class="text">
{$comment->getSummary(50)}
</p>
<a href="{getUrl('', 'mid', $document->mid, 'document_srl', $comment->document_srl)}#comment_{$comment->comment_srl}"
class="link">
on "{$document->getTitle()}"
</a>
</div>
</div>
</li>
</ul>
</div>
탭형 최근게시물¶
카테고리별 탭¶
<!-- 탭형 최근게시물 출력 -->
{@
// 카테고리별로 게시물 가져오기
$board_module_srl = 123;
$categories = array(
array('srl' => 111, 'title' => '공지사항'),
array('srl' => 222, 'title' => '자유게시판'),
array('srl' => 333, 'title' => 'Q&A')
);
$tab_data = array();
foreach($categories as $idx => $category) {
$args = new stdClass();
$args->module_srl = $board_module_srl;
$args->category_srl = $category['srl'];
$args->list_count = 5;
$args->sort_index = 'regdate';
$args->order_type = 'desc';
$output = executeQuery('document.getDocumentList', $args);
$tab_data[$idx] = array(
'category' => $category,
'documents' => $output->data
);
}
}
<div class="tab-widget">
<!-- 탭 네비게이션 -->
<ul class="tab-nav">
<li loop="$tab_data=>$idx,$data" class="active"|cond="$idx==0">
<a href="#tab-{$idx}" data-tab="{$idx}">
{$data['category']['title']}
</a>
</li>
</ul>
<!-- 탭 콘텐츠 -->
<div class="tab-content">
<div loop="$tab_data=>$idx,$data" id="tab-{$idx}" class="tab-pane active"|cond="$idx==0">
<ul class="doc-list">
<li loop="$data['documents']=>$document">
<a href="{getUrl('', 'document_srl', $document->document_srl)}">
<span class="title">{$document->getTitle()}</span>
<span class="date">{$document->getRegdate('m.d')}</span>
</a>
</li>
</ul>
<!-- 더보기 -->
<a href="{getUrl('', 'mid', 'board', 'category', $data['category']['srl'])}" class="more-link">
더보기 →
</a>
</div>
</div>
</div>
<script>
// 탭 전환
$('.tab-nav a').click(function(e) {
e.preventDefault();
var tab = $(this).data('tab');
// 네비게이션 활성화
$('.tab-nav li').removeClass('active');
$(this).parent().addClass('active');
// 콘텐츠 전환
$('.tab-pane').removeClass('active');
$('#tab-' + tab).addClass('active');
});
</script>
현재위치 표시 (Breadcrumb)¶
동적 브레드크럼¶
<!-- 현재위치출력하기(breadcrumb) -->
{@
// 브레드크럼 데이터 생성
$breadcrumb = array();
// 홈
$breadcrumb[] = array(
'title' => '홈',
'url' => getUrl('')
);
// 현재 모듈 정보
if($mid && $module_info) {
// 메뉴 정보 가져오기
$oMenuAdminModel = getAdminModel('menu');
$menu_info = $oMenuAdminModel->getMenuItemInfo($module_info->menu_srl);
// 상위 메뉴가 있으면 추가
if($menu_info->parent_srl) {
$parent_menu = $oMenuAdminModel->getMenuItemInfo($menu_info->parent_srl);
$breadcrumb[] = array(
'title' => $parent_menu->name,
'url' => $parent_menu->url
);
}
// 현재 메뉴
$breadcrumb[] = array(
'title' => $module_info->browser_title ?: $module_info->mid,
'url' => getUrl('', 'mid', $module_info->mid)
);
}
// 문서 보기
if($document_srl && $oDocument && $oDocument->isExists()) {
// 카테고리가 있으면 추가
if($oDocument->get('category_srl')) {
$category_info = $oDocumentModel->getCategory($oDocument->get('category_srl'));
$breadcrumb[] = array(
'title' => $category_info->title,
'url' => getUrl('', 'mid', $mid, 'category', $oDocument->get('category_srl'))
);
}
// 문서 제목
$breadcrumb[] = array(
'title' => $oDocument->getTitle(),
'url' => getUrl('', 'mid', $mid, 'document_srl', $document_srl)
);
}
}
<nav class="breadcrumb" aria-label="현재 위치">
<ol class="breadcrumb-list">
<li loop="$breadcrumb=>$idx,$item" class="breadcrumb-item">
<!-- 마지막 항목이 아닌 경우 링크 -->
<a cond="$idx < count($breadcrumb) - 1" href="{$item['url']}">
{$item['title']}
</a>
<!-- 마지막 항목은 텍스트만 -->
<span cond="$idx == count($breadcrumb) - 1" aria-current="page">
{$item['title']}
</span>
</li>
</ol>
</nav>
<style>
.breadcrumb-list {
display: flex;
list-style: none;
padding: 10px 0;
margin: 0;
font-size: 14px;
}
.breadcrumb-item:not(:last-child)::after {
content: "›";
margin: 0 10px;
color: #999;
}
.breadcrumb-item a {
color: #666;
text-decoration: none;
}
.breadcrumb-item a:hover {
color: #333;
text-decoration: underline;
}
.breadcrumb-item:last-child {
color: #333;
font-weight: 500;
}
</style>
공지사항 세로롤링 위젯¶
자동 롤링 공지사항¶
<!-- 공지사항 세로롤링 위젯 만들기 -->
{@
// 공지사항 가져오기
$args = new stdClass();
$args->module_srl = 123; // 공지사항 게시판
$args->list_count = 10;
$args->sort_index = 'regdate';
$args->order_type = 'desc';
$args->is_notice = 'Y'; // 공지사항만
$output = executeQuery('document.getDocumentList', $args);
$notices = $output->data;
}
<div class="notice-roller">
<div class="roller-header">
<h4>공지사항</h4>
<a href="{getUrl('', 'mid', 'notice')}" class="more">더보기</a>
</div>
<div class="roller-container">
<ul class="roller-list">
<li loop="$notices=>$notice" class="roller-item">
<a href="{getUrl('', 'mid', 'notice', 'document_srl', $notice->document_srl)}">
<span class="label">공지</span>
<span class="title">{$notice->getTitle()}</span>
<span class="date">{$notice->getRegdate('m.d')}</span>
</a>
</li>
</ul>
</div>
<!-- 컨트롤 버튼 -->
<div class="roller-controls">
<button type="button" class="btn-prev" title="이전">
<i class="xi-angle-up"></i>
</button>
<button type="button" class="btn-pause" title="일시정지">
<i class="xi-pause"></i>
</button>
<button type="button" class="btn-next" title="다음">
<i class="xi-angle-down"></i>
</button>
</div>
</div>
<script>
(function($) {
var $roller = $('.roller-list');
var $items = $roller.find('.roller-item');
var itemHeight = $items.first().outerHeight();
var currentIndex = 0;
var isPaused = false;
var timer;
// 자동 롤링
function startRolling() {
timer = setInterval(function() {
if(!isPaused) {
rollNext();
}
}, 3000);
}
// 다음 항목으로 롤링
function rollNext() {
currentIndex = (currentIndex + 1) % $items.length;
$roller.animate({
marginTop: -currentIndex * itemHeight
}, 500);
}
// 이전 항목으로 롤링
function rollPrev() {
currentIndex = currentIndex > 0 ? currentIndex - 1 : $items.length - 1;
$roller.animate({
marginTop: -currentIndex * itemHeight
}, 500);
}
// 컨트롤 버튼
$('.btn-next').click(function() {
clearInterval(timer);
rollNext();
startRolling();
});
$('.btn-prev').click(function() {
clearInterval(timer);
rollPrev();
startRolling();
});
$('.btn-pause').click(function() {
isPaused = !isPaused;
$(this).find('i').toggleClass('xi-pause xi-play');
});
// 마우스 오버시 일시정지
$('.roller-container').hover(
function() { isPaused = true; },
function() { isPaused = false; }
);
// 시작
startRolling();
})(jQuery);
</script>
<style>
.notice-roller {
border: 1px solid #ddd;
border-radius: 5px;
overflow: hidden;
}
.roller-container {
height: 40px;
overflow: hidden;
position: relative;
}
.roller-list {
list-style: none;
margin: 0;
padding: 0;
}
.roller-item {
height: 40px;
line-height: 40px;
padding: 0 15px;
}
.roller-item .label {
background: #ff4444;
color: white;
padding: 2px 6px;
border-radius: 3px;
font-size: 12px;
margin-right: 10px;
}
.roller-controls {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
display: flex;
gap: 5px;
}
.roller-controls button {
width: 24px;
height: 24px;
border: 1px solid #ddd;
background: white;
border-radius: 3px;
cursor: pointer;
}
</style>
모범 사례¶
- 성능 최적화: 필요한 데이터만 쿼리
- 캐싱 활용: 자주 사용되는 데이터는 캐싱
- 에러 처리: 데이터가 없을 때 처리
- 반응형 대응: 모바일에서도 잘 보이도록
- 접근성: ARIA 레이블, 키보드 네비게이션
다음 단계¶
위젯 시스템을 마스터했다면, 메타태그와 SEO를 학습하세요.