포털메뉴 (멀티메뉴) 구현¶
포털메뉴는 하나의 레이아웃에서 여러 개의 독립적인 메뉴를 관리할 수 있는 기능입니다. 최대 5개까지 설정 가능합니다.
포털메뉴 설정¶
info.xml 설정¶
<?xml version="1.0" encoding="UTF-8"?>
<layout version="0.2">
<!-- 기본 정보 -->
<title xml:lang="ko">포털 레이아웃</title>
<!-- 메뉴 설정 -->
<menus>
<!-- 메인 메뉴 -->
<menu name="main_menu" maxdepth="3" default="true">
<title xml:lang="ko">메인 메뉴</title>
</menu>
<!-- 포털 메뉴 1~5 -->
<menu name="portal_menu1" maxdepth="2">
<title xml:lang="ko">회사소개</title>
</menu>
<menu name="portal_menu2" maxdepth="2">
<title xml:lang="ko">제품/서비스</title>
</menu>
<menu name="portal_menu3" maxdepth="2">
<title xml:lang="ko">고객지원</title>
</menu>
<menu name="portal_menu4" maxdepth="2">
<title xml:lang="ko">커뮤니티</title>
</menu>
<menu name="portal_menu5" maxdepth="1">
<title xml:lang="ko">푸터 메뉴</title>
</menu>
</menus>
</layout>
포털메뉴 활용 예제¶
1. 섹션별 독립 메뉴¶
<!-- 메인 내비게이션 -->
<nav class="main-navigation">
<ul class="nav-sections">
<!-- 회사소개 섹션 -->
<li class="nav-section" cond="$portal_menu1">
<h3 class="section-title">회사소개</h3>
<ul class="section-menu">
<li loop="$portal_menu1->list => $key, $val">
<a href="{$val['href']}">{$val['link']}</a>
</li>
</ul>
</li>
<!-- 제품/서비스 섹션 -->
<li class="nav-section" cond="$portal_menu2">
<h3 class="section-title">제품/서비스</h3>
<ul class="section-menu">
<li loop="$portal_menu2->list => $key, $val">
<a href="{$val['href']}">{$val['link']}</a>
</li>
</ul>
</li>
<!-- 고객지원 섹션 -->
<li class="nav-section" cond="$portal_menu3">
<h3 class="section-title">고객지원</h3>
<ul class="section-menu">
<li loop="$portal_menu3->list => $key, $val">
<a href="{$val['href']}">{$val['link']}</a>
</li>
</ul>
</li>
</ul>
</nav>
2. 메가 드롭다운 메뉴¶
<nav class="mega-dropdown">
<ul class="main-nav">
<!-- 각 포털메뉴를 메인 메뉴 항목으로 -->
<li class="has-mega" cond="$portal_menu1">
<a href="#" class="nav-trigger">회사소개</a>
<div class="mega-content">
<div class="mega-inner">
<!-- 2단 구조로 표시 -->
<div class="mega-column" loop="$portal_menu1->list => $key1, $val1">
<h4><a href="{$val1['href']}">{$val1['link']}</a></h4>
<ul cond="$val1['list']">
<li loop="$val1['list'] => $key2, $val2">
<a href="{$val2['href']}">{$val2['link']}</a>
</li>
</ul>
</div>
</div>
</div>
</li>
<li class="has-mega" cond="$portal_menu2">
<a href="#" class="nav-trigger">제품/서비스</a>
<div class="mega-content">
<div class="mega-inner">
<div class="mega-column" loop="$portal_menu2->list => $key1, $val1">
<h4><a href="{$val1['href']}">{$val1['link']}</a></h4>
<ul cond="$val1['list']">
<li loop="$val1['list'] => $key2, $val2">
<a href="{$val2['href']}">{$val2['link']}</a>
</li>
</ul>
</div>
</div>
</div>
</li>
</ul>
</nav>
3. 탭 형식 포털메뉴¶
<div class="portal-tabs">
<!-- 탭 헤더 -->
<ul class="tab-nav">
<li class="tab-item active" cond="$portal_menu1">
<a href="#tab1" data-toggle="tab">회사소개</a>
</li>
<li class="tab-item" cond="$portal_menu2">
<a href="#tab2" data-toggle="tab">제품/서비스</a>
</li>
<li class="tab-item" cond="$portal_menu3">
<a href="#tab3" data-toggle="tab">고객지원</a>
</li>
<li class="tab-item" cond="$portal_menu4">
<a href="#tab4" data-toggle="tab">커뮤니티</a>
</li>
</ul>
<!-- 탭 콘텐츠 -->
<div class="tab-content">
<!-- 회사소개 탭 -->
<div id="tab1" class="tab-pane active" cond="$portal_menu1">
<div class="menu-grid">
<div class="menu-item" loop="$portal_menu1->list => $key, $val">
<h3><a href="{$val['href']}">{$val['link']}</a></h3>
<ul cond="$val['list']">
<li loop="$val['list'] => $subkey, $subval">
<a href="{$subval['href']}">{$subval['link']}</a>
</li>
</ul>
</div>
</div>
</div>
<!-- 제품/서비스 탭 -->
<div id="tab2" class="tab-pane" cond="$portal_menu2">
<div class="menu-grid">
<div class="menu-item" loop="$portal_menu2->list => $key, $val">
<h3><a href="{$val['href']}">{$val['link']}</a></h3>
<ul cond="$val['list']">
<li loop="$val['list'] => $subkey, $subval">
<a href="{$subval['href']}">{$subval['link']}</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
4. 사이트맵 스타일¶
<div class="sitemap">
<div class="sitemap-container">
<!-- 포털메뉴별 섹션 -->
<div class="sitemap-section" cond="$portal_menu1">
<h2 class="section-heading">회사소개</h2>
<div class="section-content">
<ul class="sitemap-list">
<li loop="$portal_menu1->list => $key1, $val1" class="sitemap-item">
<a href="{$val1['href']}" class="sitemap-link primary">
{$val1['link']}
</a>
<ul class="sitemap-sublist" cond="$val1['list']">
<li loop="$val1['list'] => $key2, $val2">
<a href="{$val2['href']}">{$val2['link']}</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
<div class="sitemap-section" cond="$portal_menu2">
<h2 class="section-heading">제품/서비스</h2>
<div class="section-content">
<ul class="sitemap-list">
<li loop="$portal_menu2->list => $key1, $val1" class="sitemap-item">
<a href="{$val1['href']}" class="sitemap-link primary">
{$val1['link']}
</a>
<ul class="sitemap-sublist" cond="$val1['list']">
<li loop="$val1['list'] => $key2, $val2">
<a href="{$val2['href']}">{$val2['link']}</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
동적 포털메뉴¶
JavaScript를 활용한 인터랙션¶
jQuery(function($) {
// 포털메뉴 초기화
var PortalMenu = {
init: function() {
this.bindEvents();
this.initTabs();
this.initMegaMenu();
},
bindEvents: function() {
// 탭 전환
$('.tab-nav a').on('click', function(e) {
e.preventDefault();
var target = $(this).attr('href');
// 탭 활성화
$(this).parent().addClass('active')
.siblings().removeClass('active');
// 콘텐츠 전환
$(target).addClass('active')
.siblings().removeClass('active');
});
},
initTabs: function() {
// URL 해시에 따른 탭 활성화
var hash = window.location.hash;
if (hash) {
$('.tab-nav a[href="' + hash + '"]').click();
}
},
initMegaMenu: function() {
var timeout;
$('.has-mega').hover(
function() {
var $this = $(this);
clearTimeout(timeout);
// 메가메뉴 표시
$this.find('.mega-content').stop(true, true).fadeIn(200);
},
function() {
var $this = $(this);
timeout = setTimeout(function() {
$this.find('.mega-content').stop(true, true).fadeOut(200);
}, 300);
}
);
}
};
PortalMenu.init();
});
푸터 포털메뉴¶
<footer class="site-footer">
<div class="footer-menus">
<div class="container">
<div class="footer-row">
<!-- 회사 정보 -->
<div class="footer-col" cond="$portal_menu1">
<h4>회사소개</h4>
<ul class="footer-links">
<li loop="$portal_menu1->list => $key, $val">
<a href="{$val['href']}">{$val['link']}</a>
</li>
</ul>
</div>
<!-- 고객 지원 -->
<div class="footer-col" cond="$portal_menu3">
<h4>고객지원</h4>
<ul class="footer-links">
<li loop="$portal_menu3->list => $key, $val">
<a href="{$val['href']}">{$val['link']}</a>
</li>
</ul>
</div>
<!-- 빠른 링크 -->
<div class="footer-col" cond="$portal_menu5">
<h4>빠른 링크</h4>
<ul class="footer-links">
<li loop="$portal_menu5->list => $key, $val">
<a href="{$val['href']}">{$val['link']}</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</footer>
CSS 스타일링¶
/* 메가 드롭다운 */
.mega-dropdown {
position: relative;
}
.main-nav {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.has-mega {
position: static;
}
.nav-trigger {
display: block;
padding: 20px 30px;
color: #333;
text-decoration: none;
font-weight: 500;
}
.mega-content {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: #fff;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
display: none;
z-index: 1000;
}
.mega-inner {
display: flex;
padding: 40px;
gap: 40px;
}
.mega-column {
flex: 1;
}
.mega-column h4 {
margin: 0 0 15px;
font-size: 18px;
}
.mega-column ul {
list-style: none;
padding: 0;
}
.mega-column li {
margin-bottom: 8px;
}
.mega-column a {
color: #666;
text-decoration: none;
}
.mega-column a:hover {
color: #007bff;
}
/* 탭 스타일 */
.portal-tabs {
background: #f8f9fa;
border-radius: 8px;
overflow: hidden;
}
.tab-nav {
display: flex;
list-style: none;
margin: 0;
padding: 0;
border-bottom: 2px solid #e9ecef;
}
.tab-item a {
display: block;
padding: 15px 30px;
color: #666;
text-decoration: none;
transition: all 0.3s;
}
.tab-item.active a {
color: #007bff;
background: #fff;
border-bottom: 2px solid #007bff;
margin-bottom: -2px;
}
.tab-content {
padding: 40px;
background: #fff;
}
.tab-pane {
display: none;
}
.tab-pane.active {
display: block;
}
.menu-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 30px;
}
/* 사이트맵 스타일 */
.sitemap {
padding: 60px 0;
background: #f8f9fa;
}
.sitemap-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 40px;
}
.section-heading {
margin: 0 0 20px;
padding-bottom: 10px;
border-bottom: 2px solid #007bff;
font-size: 24px;
}
.sitemap-link.primary {
font-weight: 600;
font-size: 16px;
color: #333;
}
.sitemap-sublist {
margin-top: 10px;
margin-left: 20px;
}
.sitemap-sublist a {
color: #666;
font-size: 14px;
}
포털메뉴 활용 팁¶
- 메뉴별 권한 설정: 각 포털메뉴는 독립적으로 권한 설정 가능
- 다국어 지원: 메뉴명을 다국어로 설정 가능
- 캐싱: 메뉴는 자주 변경되지 않으므로 캐싱 적용 권장
- 모바일 대응: 포털메뉴가 많은 경우 모바일에서는 아코디언 형태로 변환
고급 활용¶
조건부 포털메뉴¶
<!-- 로그인 상태에 따른 메뉴 분기 -->
<nav class="conditional-menu">
<!--@if(!$is_logged)-->
<!-- 비회원용 메뉴 -->
<ul cond="$portal_menu1">
<li loop="$portal_menu1->list => $key, $val">
<a href="{$val['href']}">{$val['link']}</a>
</li>
</ul>
<!--@else-->
<!-- 회원용 메뉴 -->
<ul cond="$portal_menu2">
<li loop="$portal_menu2->list => $key, $val">
<a href="{$val['href']}">{$val['link']}</a>
</li>
</ul>
<!--@endif-->
</nav>
AJAX 로드 메뉴¶
// 대용량 메뉴를 필요시 로드
$('.nav-trigger').on('mouseenter', function() {
var $trigger = $(this);
var menuId = $trigger.data('menu-id');
var $content = $trigger.siblings('.mega-content');
if (!$content.data('loaded')) {
$.ajax({
url: request_uri,
data: {
module: 'layout',
act: 'getPortalMenu',
menu_id: menuId
},
success: function(response) {
$content.html(response.html);
$content.data('loaded', true);
}
});
}
});