포털메뉴 (멀티메뉴) 구현

포털메뉴 (멀티메뉴) 구현

포털메뉴는 하나의 레이아웃에서 여러 개의 독립적인 메뉴를 관리할 수 있는 기능입니다. 최대 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;
}

포털메뉴 활용 팁

  1. 메뉴별 권한 설정: 각 포털메뉴는 독립적으로 권한 설정 가능
  2. 다국어 지원: 메뉴명을 다국어로 설정 가능
  3. 캐싱: 메뉴는 자주 변경되지 않으므로 캐싱 적용 권장
  4. 모바일 대응: 포털메뉴가 많은 경우 모바일에서는 아코디언 형태로 변환

고급 활용

조건부 포털메뉴

<!-- 로그인 상태에 따른 메뉴 분기 -->
<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);
            }
        });
    }
});