컴포넌트/툴바

ResponsiveNav (반응형 내비게이션)

VanillaFront 2026. 9. 29. 23:29

 

Va.ResponsiveNav — 자동 반응형 내비게이션 바 컴포넌트

뷰포트 크기에 따라 자동으로 레이아웃을 전환하는 반응형 내비게이션 컴포넌트입니다. 데스크톱/태블릿에서는 헤더에 메뉴가 가로로 나열되고, 모바일에서는 자동으로 햄버거 버튼 방식으로 접힙니다. 화면 크기에 맞춰 자식 컴포넌트가 menuDiv ↔ menuPop 사이를 자동 이동하므로, 반응형 처리를 별도 코드로 짤 필요가 없습니다. overlay 모드, 상단/하단 위치, 외부 클릭 자동 닫힘까지 지원합니다.


클래스 정보

  • Va.ResponsiveNav extends Va.Component
  • tagName: 'responsiveNav'
  • isContainer: true
  • 기본 HTML: <nav> 요소 (aria-labelledby 자동 부여)
  • 파일: va_component.js

기본 사용법

{
    ref: 'refNavi',
    tagName: 'responsiveNav',
    title: 'Title',
    appearance: 'primary',
    border: 'neutral',
    menuWidth: 200,
    style: { width: '100%', padding: '5px' },
    tags: [
        // 타이틀 영역
        { tagName: 'div', innerHTML: 'Title', style: 'width:200px', tagPosition: 'title' },
        
        // 메뉴 항목 (데스크톱에선 가로, 모바일에선 햄버거 안)
        { tagName: 'responsiveMenuButton', text: 'Menu1', appearance: 'primary', popWidth: 200,
          tags: [
              { tagName: 'menuItem', text: 'Menu11' },
              { tagName: 'menuItem', text: 'Menu12' }
          ]
        },
        { tagName: 'responsiveMenuButton', text: 'Menu2', appearance: 'primary', popWidth: 200,
          tags: [
              { tagName: 'menuItem', text: 'Menu21' },
              { tagName: 'menuItem', text: 'Menu22' }
          ]
        },
        { tagName: 'button', text: 'Menu3', appearance: 'primary' }
    ]
}

핵심: tags에 넣은 자식 메뉴는 브라우저 폭에 따라 자동으로 위치가 바뀝니다. 개발자는 별도의 미디어쿼리 로직을 짤 필요가 없습니다.


자동 반응형 동작 원리

내부에서 Va.addMediaQueryEvent로 뷰포트 변화를 구독해 자식 컴포넌트를 이동시킵니다.

뷰포트컨테이너 이동햄버거 버튼메뉴 팝

mobile 자식을 menuPop으로 이동 표시 접힘/펼침 가능
tablet 자식을 menuDiv로 이동 숨김 항상 접힘
desktop 자식을 menuDiv로 이동 숨김 항상 접힘

이 자동 이동 덕분에 하나의 config로 모든 화면 폭에서 동작하는 내비게이션을 만들 수 있습니다.


핸들러는 반드시 문자열 참조 (JSON 원칙)

VanillaFront는 화면 정의를 JSON으로 유지하는 철학입니다. 모든 이벤트 핸들러는 문자열로 View 메서드 이름을 지정합니다.

{ onTitleClick: 'onTitleClick', onMenuClick: 'onMenuClick', onExpand: 'onExpand', onCollapse: 'onCollapse' }

인라인 function 사용 금지 — JSON 직렬화/에디터/저장 불가.


주요 속성

속성타입기본값설명

title String — 좌측 타이틀 텍스트 (textContent — HTML 삽입 시 tagPosition: 'title' 컴포넌트 활용)
appearance String — 시각 스타일 — 'primary', 'subtle', 'accent' 등
position String 'top' 메뉴 팝 위치 — 'top'(헤더 아래), 'bottom'(헤더 위)
overlay Boolean false 메뉴 팝을 오버레이(팝업)로 띄울지 여부
expanded Boolean false 메뉴 팝 열림 상태
border String — 테두리 스타일 — 'neutral' 등
menuWidth Number — 메뉴 팝 폭 (px)
menuButton Object — 자동 생성되는 햄버거 버튼 옵션 오버라이드

주요 이벤트

이벤트콜백발생 시점

onExpand 'onExpand' 메뉴 팝이 열릴 때
onCollapse 'onCollapse' 메뉴 팝이 닫힐 때
onMenuClick 'onMenuClick' 햄버거 버튼 클릭 시
onTitleClick 'onTitleClick' 타이틀 영역 클릭
onAutoHide (내부) 외부 클릭·스크롤·resize로 자동 닫힘

주요 메서드

  • expand() — 메뉴 팝 열기
  • collapse() — 메뉴 팝 닫기
  • setTitle(title) — 타이틀 텍스트 동적 변경

tagPosition — 자식 배치 위치

tagPosition배치 영역반응형 동작

'title' titleDiv 모든 폭에서 헤더 좌측 고정
'menu' menuDiv 데스크톱/태블릿 헤더, 모바일에선 팝으로 자동 이동
(없음) menuDiv (기본 컨테이너) 'menu'와 동일 동작
'menuPop' menuPop 항상 팝 영역에 유지

로고 배치 예:

{
    tagName: 'div',
    innerHTML: '<img src="/assets/img/logo.png" height="20"/> VanillaFront',
    style: 'width:200px',
    tagPosition: 'title'
}

overlay 모드 — 팝을 오버레이로 띄우기

overlay: true로 지정하면 메뉴 팝이 레이아웃을 밀어내지 않고 헤더 위로 떠서 표시됩니다. 외부 클릭 시 자동으로 닫힙니다(Va.addAutoHide).

{
    tagName: 'responsiveNav',
    overlay: true,      // ← 오버레이 모드
    title: 'Title',
    appearance: 'primary',
    menuWidth: 200,
    tags: [ ... ]
}

모드동작

overlay: false (기본) 메뉴 팝이 헤더 아래에 밀어내며 배치 (레이아웃 재계산됨)
overlay: true 절대위치로 헤더 위에 떠서 표시 (본문에 영향 없음)

오버레이 모드는 자동 닫힘 지원: 외부 클릭, window resize, 스크롤 발생 시 자동으로 collapse됩니다.


position — 메뉴 팝 위치

메뉴 팝이 헤더의 위쪽에 뜰지 아래쪽에 뜰지 지정합니다.

{
    tagName: 'responsiveNav',
    position: 'bottom',   // 헤더가 하단에 있고 팝은 위로 뜨는 배치
    overlay: true,
    ...
}

position헤더 위치팝 열림 방향

'top' (기본) 화면 상단 아래로 펼침
'bottom' 화면 하단 위로 펼침

모바일 앱처럼 화면 하단에 내비게이션을 배치할 때 position: 'bottom' + overlay: true 조합이 유용합니다.


responsiveMenuButton — 반응형 서브메뉴

ResponsiveNav 안에서 서브메뉴가 필요하면 responsiveMenuButton을 사용합니다. 데스크톱에선 드롭다운으로, 모바일에선 아코디언 방식으로 자동 전환됩니다.

{
    tagName: 'responsiveMenuButton',
    text: 'Menu1',
    appearance: 'primary',
    popWidth: 200,
    textAlign: 'left',
    tags: [
        { tagName: 'menuItem', text: 'Menu11',
          tags: [
              { tagName: 'menuItem', text: 'Menu111' },
              { tagName: 'menuItem', text: 'Menu112' }
          ]
        },
        { tagName: 'menuItem', text: 'Menu12' }
    ]
}
  • 다단 중첩 지원 — menuItem 안에 다시 tags를 넣어 깊이 있는 메뉴 트리 구성 가능
  • popWidth — 팝 폭 지정 (px)
  • textAlign: 'left' — 모바일 팝에서 좌측 정렬 권장

종합 예시 — 로고 + 반응형 메뉴 + 오버레이

config() {
    return {
        tagName: 'page',
        tags: [{
            ref: 'refNavi',
            tagName: 'responsiveNav',
            appearance: 'subtle',
            border: 'neutral',
            overlay: true,
            menuWidth: 220,
            style: { width: '100%' },
            tags: [
                // 로고 (모든 폭에서 좌측 고정)
                {
                    tagName: 'div',
                    innerHTML: '<img src="/assets/img/logo.png" height="20" style="margin-right:10px"/> VanillaFront',
                    style: 'width:200px',
                    tagPosition: 'title'
                },
                
                // 반응형 메뉴 (데스크톱 가로 / 모바일 햄버거)
                {
                    tagName: 'responsiveMenuButton',
                    text: '제품', appearance: 'subtle',
                    popWidth: 200, textAlign: 'left',
                    tags: [
                        { tagName: 'menuItem', text: 'Grid' },
                        { tagName: 'menuItem', text: 'Chart' },
                        {
                            tagName: 'menuItem', text: 'Diagram',
                            tags: [
                                { tagName: 'menuItem', text: 'Flow' },
                                { tagName: 'menuItem', text: 'Org Chart' }
                            ]
                        }
                    ]
                },
                {
                    tagName: 'responsiveMenuButton',
                    text: '학습', appearance: 'subtle',
                    popWidth: 200, textAlign: 'left',
                    tags: [
                        { tagName: 'menuItem', text: '튜토리얼' },
                        { tagName: 'menuItem', text: 'API 문서' }
                    ]
                },
                { tagName: 'button', text: '가격',   appearance: 'subtle', textAlign: 'left' },
                { tagName: 'button', text: '커뮤니티', appearance: 'subtle', textAlign: 'left' }
            ],
            onTitleClick: 'onLogoClick',
            onMenuClick:  'onMenuClick',
            onExpand:     'onExpand',
            onCollapse:   'onCollapse'
        }, {
            // 본문
            tagName: 'panel',
            style: { flex: 1 },
            tags: [ ... ]
        }]
    };
}

onLogoClick() { Va.Router.push('/'); }
onExpand()    { console.log('메뉴 열림'); }
onCollapse()  { console.log('메뉴 닫힘'); }

자동 닫힘(autoHide) 동작

overlay: true 모드에서 아래 상황 발생 시 메뉴가 자동으로 닫힙니다.

  • 팝 외부 mousedown — 다른 영역 클릭
  • window resize — 창 크기 변경
  • window scroll — 스크롤 발생

Va.addAutoHide(this.menuPopElement)가 자동으로 등록됩니다. 별도 코드 필요 없음.


NavigationBar vs NavigationMenuBar vs ResponsiveNav — 차이점

항목NavigationBarNavigationMenuBarResponsiveNav

반응형 자동 전환 ❌ ❌ ✅ (모바일↔데스크톱 자동)
햄버거 버튼 ❌ ✅ (항상) ✅ (모바일만)
서브메뉴 스타일 드롭다운 (menuButton) 아코디언 (expandButton) 반응형 (responsiveMenuButton)
오버레이 모드 ❌ ❌ ✅ (overlay)
position 지정 ❌ ❌ ✅ (top/bottom)
autoHide (외부 클릭 닫힘) ❌ ❌ ✅ (overlay 시)
적합한 상황 데스크톱 전용 항상 접힘 UI 데스크톱+모바일 통합

주의사항

  • 기본 컨테이너는 뷰포트에 따라 바뀜 — 모바일에선 menuPop, 그 외엔 menuDiv. tagPosition 명시가 없으면 자동 이동 대상
  • title은 textContent — HTML 태그가 필요하면 tagPosition: 'title' 컴포넌트로 넣기
  • 서브메뉴는 responsiveMenuButton 권장 — 일반 menuButton은 반응형 전환에서 어색할 수 있음
  • overlay 모드는 자동 닫힘 지원 — 별도 처리 필요 없음. 스크롤/resize에도 자동 닫힘
  • menuWidth, popWidth 활용 — 팝 폭이 너무 좁으면 서브메뉴 텍스트가 잘림
  • 핸들러는 문자열 참조만 — 인라인 function은 JSON 원칙 위반
  • 미디어쿼리 브레이크포인트는 프레임워크 기본값 사용 — Va.addMediaQueryEvent가 관리. 커스텀 브레이크포인트는 별도 설정 필요

'컴포넌트 > 툴바' 카테고리의 다른 글

NavigationMenuBar (햄버거 메뉴 바)  (0) 2026.09.29
NavigationBar (내비게이션 바)  (0) 2026.09.29
Toolbar (툴바)  (0) 2026.09.20