헤더와 푸터 만들기

헤더와 푸터 만들기

헤더 구조

헤더는 일반적으로 3단 구조로 구성됩니다:

  1. 상단 (Top Bar): 유틸리티 메뉴, 언어 선택, SNS 링크
  2. 중단 (Main Header): 로고, 메인 메뉴, 검색
  3. 하단 (Sub Header): 서브메뉴, 브레드크럼

기본 헤더 구조

<header id="header">
    <!-- 상단 영역 -->
    <div class="header-top">
        <div class="container">
            <!-- 좌측: 연락처 정보 -->
            <div class="contact-info">
                <span><i class="icon-phone"></i> 02-1234-5678</span>
                <span><i class="icon-email"></i> info@example.com</span>
            </div>

            <!-- 우측: 유틸리티 메뉴 -->
            <div class="utility-menu">
                <!--@if(!$is_logged)-->
                    <a href="{getUrl('act', 'dispMemberLoginForm')}">로그인</a>
                    <a href="{getUrl('act', 'dispMemberSignUpForm')}">회원가입</a>
                <!--@else-->
                    <a href="{getUrl('act', 'dispMemberInfo')}">마이페이지</a>
                    <a href="{getUrl('act', 'dispMemberLogout')}">로그아웃</a>
                <!--@endif-->

                <!-- 언어 선택 -->
                <div class="lang-select" cond="count($lang_supported) > 1">
                    <select onchange="doChangeLangType(this.value)">
                        <option loop="$lang_supported => $key, $val" 
                                value="{$key}" 
                                selected="selected"|cond="$key == $lang_type">
                            {$val}
                        </option>
                    </select>
                </div>
            </div>
        </div>
    </div>

    <!-- 메인 헤더 -->
    <div class="header-main">
        <div class="container">
            <!-- 로고 -->
            <div class="logo">
                <a href="{getUrl()}">
                    <!--@if($layout_info->logo_image)-->
                        <img src="{$layout_info->logo_image}" alt="{$layout_info->site_title}" />
                    <!--@else-->
                        <h1>{$layout_info->site_title}</h1>
                    <!--@endif-->
                </a>
            </div>

            <!-- 메인 메뉴 -->
            <nav class="main-nav">
                <ul class="gnb">
                    <li loop="$main_menu->list => $key1, $val1" 
                        class="depth1 {$val1['selected'] ? 'active' : ''}">
                        <a href="{$val1['href']}" 
                           target="_blank"|cond="$val1['open_window'] == 'Y'">
                            {$val1['link']}
                        </a>

                        <!-- 2차 메뉴 -->
                        <div class="submenu" cond="$val1['list']">
                            <ul class="depth2">
                                <li loop="$val1['list'] => $key2, $val2"
                                    class="{$val2['selected'] ? 'active' : ''}">
                                    <a href="{$val2['href']}">{$val2['link']}</a>

                                    <!-- 3차 메뉴 -->
                                    <ul class="depth3" cond="$val2['list']">
                                        <li loop="$val2['list'] => $key3, $val3">
                                            <a href="{$val3['href']}">{$val3['link']}</a>
                                        </li>
                                    </ul>
                                </li>
                            </ul>
                        </div>
                    </li>
                </ul>
            </nav>

            <!-- 검색 -->
            <div class="header-search">
                <form action="{getUrl()}" method="get">
                    <input type="hidden" name="mid" value="{$mid}" />
                    <input type="hidden" name="act" value="IS" />
                    <input type="text" name="is_keyword" value="{$is_keyword}" 
                           placeholder="검색어 입력" />
                    <button type="submit"><i class="icon-search"></i></button>
                </form>
            </div>

            <!-- 모바일 메뉴 버튼 -->
            <button class="mobile-menu-toggle">
                <span></span>
                <span></span>
                <span></span>
            </button>
        </div>
    </div>

    <!-- 서브 헤더 (선택적) -->
    <div class="header-sub" cond="!$is_index">
        <div class="container">
            <!-- 페이지 타이틀 -->
            <h2 class="page-title">{$module_info->browser_title}</h2>

            <!-- 브레드크럼 -->
            <nav class="breadcrumb">
                <a href="{getUrl()}"><i class="icon-home"></i></a>
                <block loop="$main_menu->list => $key1, $val1" cond="$val1['selected']">
                    <span class="separator">/</span>
                    <a href="{$val1['href']}">{$val1['link']}</a>
                    <block loop="$val1['list'] => $key2, $val2" cond="$val2['selected']">
                        <span class="separator">/</span>
                        <a href="{$val2['href']}">{$val2['link']}</a>
                    </block>
                </block>
            </nav>
        </div>
    </div>
</header>

스크롤 시 헤더를 고정시키는 기능:

// layout.js
jQuery(function($) {
    var header = $('#header');
    var headerHeight = header.outerHeight();
    var scrolled = false;

    $(window).scroll(function() {
        var scrollTop = $(window).scrollTop();

        if (scrollTop > headerHeight && !scrolled) {
            header.addClass('fixed');
            $('body').css('padding-top', headerHeight);
            scrolled = true;
        } else if (scrollTop <= headerHeight && scrolled) {
            header.removeClass('fixed');
            $('body').css('padding-top', 0);
            scrolled = false;
        }
    });
});
/* layout.css */
#header.fixed {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

푸터 구조

푸터는 일반적으로 다음 요소들을 포함합니다:

기본 푸터 구조

<footer id="footer">
    <!-- 푸터 위젯 영역 -->
    <div class="footer-widgets">
        <div class="container">
            <div class="row">
                <!-- 회사 정보 -->
                <div class="col">
                    <h3>회사 소개</h3>
                    <p>{$layout_info->footer_about}</p>

                    <!-- SNS 링크 -->
                    <div class="social-links">
                        <a href="{$layout_info->facebook_url}" cond="$layout_info->facebook_url">
                            <i class="icon-facebook"></i>
                        </a>
                        <a href="{$layout_info->twitter_url}" cond="$layout_info->twitter_url">
                            <i class="icon-twitter"></i>
                        </a>
                        <a href="{$layout_info->instagram_url}" cond="$layout_info->instagram_url">
                            <i class="icon-instagram"></i>
                        </a>
                    </div>
                </div>

                <!-- 빠른 링크 -->
                <div class="col">
                    <h3>빠른 링크</h3>
                    <ul class="footer-links">
                        <li loop="$footer_menu->list => $key, $val">
                            <a href="{$val['href']}">{$val['link']}</a>
                        </li>
                    </ul>
                </div>

                <!-- 최근 게시물 -->
                <div class="col">
                    <h3>최근 게시물</h3>
                    {@
                        // 최근 게시물 가져오기
                        $args = new stdClass();
                        $args->module_srl = $layout_info->recent_module_srl;
                        $args->list_count = 5;
                        $recent_documents = executeQuery('document.getDocumentList', $args)->data;
                    }
                    <ul class="recent-posts">
                        <li loop="$recent_documents => $doc">
                            <a href="{getUrl('document_srl', $doc->document_srl)}">
                                {cut_str($doc->title, 30)}
                            </a>
                            <span class="date">{zdate($doc->regdate, 'Y.m.d')}</span>
                        </li>
                    </ul>
                </div>

                <!-- 연락처 정보 -->
                <div class="col">
                    <h3>연락처</h3>
                    <ul class="contact-list">
                        <li><i class="icon-location"></i> {$layout_info->address}</li>
                        <li><i class="icon-phone"></i> {$layout_info->phone}</li>
                        <li><i class="icon-email"></i> {$layout_info->email}</li>
                        <li><i class="icon-time"></i> {$layout_info->business_hours}</li>
                    </ul>
                </div>
            </div>
        </div>
    </div>

    <!-- 저작권 영역 -->
    <div class="footer-bottom">
        <div class="container">
            <div class="copyright">
                <p>
                    © {date('Y')} {$layout_info->site_title}. 
                    All rights reserved. 
                    <!--@if($layout_info->company_name)-->
                    | {$layout_info->company_name}
                    <!--@endif-->
                    <!--@if($layout_info->business_number)-->
                    | 사업자등록번호: {$layout_info->business_number}
                    <!--@endif-->
                </p>
            </div>

            <!-- 푸터 메뉴 -->
            <nav class="footer-nav">
                <a href="{getUrl('mid', 'privacy')}">개인정보처리방침</a>
                <a href="{getUrl('mid', 'terms')}">이용약관</a>
                <a href="{getUrl('mid', 'sitemap')}">사이트맵</a>
                <!--@if($logged_info->is_admin == 'Y')-->
                <a href="{getUrl('', 'module', 'admin')}">관리자</a>
                <!--@endif-->
            </nav>
        </div>
    </div>

    <!-- 맨 위로 버튼 -->
    <button id="back-to-top" title="맨 위로">
        <i class="icon-arrow-up"></i>
    </button>
</footer>

맨 위로 버튼 스크립트

// 맨 위로 버튼
jQuery(function($) {
    var $backToTop = $('#back-to-top');

    // 스크롤 시 버튼 표시/숨김
    $(window).scroll(function() {
        if ($(this).scrollTop() > 300) {
            $backToTop.fadeIn();
        } else {
            $backToTop.fadeOut();
        }
    });

    // 클릭 시 맨 위로 스크롤
    $backToTop.click(function() {
        $('html, body').animate({
            scrollTop: 0
        }, 600);
        return false;
    });
});

반응형 헤더/푸터

모바일 메뉴

<!-- 모바일 메뉴 -->
<div class="mobile-menu-wrapper">
    <div class="mobile-menu-header">
        <span class="title">메뉴</span>
        <button class="close-menu">&times;</button>
    </div>

    <nav class="mobile-menu">
        <ul>
            <li loop="$main_menu->list => $key1, $val1">
                <a href="{$val1['href']}" class="has-sub"|cond="$val1['list']">
                    {$val1['link']}
                    <span class="arrow" cond="$val1['list']">▼</span>
                </a>

                <ul class="sub-menu" cond="$val1['list']">
                    <li loop="$val1['list'] => $key2, $val2">
                        <a href="{$val2['href']}">{$val2['link']}</a>
                    </li>
                </ul>
            </li>
        </ul>
    </nav>
</div>

반응형 CSS

/* 태블릿 */
@media (max-width: 991px) {
    .header-top {
        display: none;
    }

    .main-nav {
        display: none;
    }

    .mobile-menu-toggle {
        display: block;
    }

    .footer-widgets .col {
        width: 50%;
        margin-bottom: 30px;
    }
}

/* 모바일 */
@media (max-width: 767px) {
    .header-main {
        padding: 10px 0;
    }

    .logo h1 {
        font-size: 24px;
    }

    .footer-widgets .col {
        width: 100%;
    }

    .footer-bottom {
        text-align: center;
    }

    .footer-nav {
        margin-top: 10px;
    }
}

헤더/푸터 설정 변수

info.xml에 추가할 설정 변수들:

<extra_vars>
    <!-- 헤더 설정 -->
    <var name="logo_image" type="image">
        <title xml:lang="ko">로고 이미지</title>
    </var>
    <var name="sticky_header" 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>

    <!-- 푸터 설정 -->
    <var name="footer_about" type="textarea">
        <title xml:lang="ko">회사 소개</title>
    </var>
    <var name="address" type="text">
        <title xml:lang="ko">주소</title>
    </var>
    <var name="phone" type="text">
        <title xml:lang="ko">전화번호</title>
    </var>
    <var name="email" type="text">
        <title xml:lang="ko">이메일</title>
    </var>
    <var name="business_hours" type="text">
        <title xml:lang="ko">영업시간</title>
    </var>

    <!-- SNS 링크 -->
    <var name="facebook_url" type="text">
        <title xml:lang="ko">Facebook URL</title>
    </var>
    <var name="twitter_url" type="text">
        <title xml:lang="ko">Twitter URL</title>
    </var>
    <var name="instagram_url" type="text">
        <title xml:lang="ko">Instagram URL</title>
    </var>
</extra_vars>