헤더·네비게이션·메가 메뉴: 대규모 이커머스 사이트 네비게이션 설계

UX 디자인

네비게이션메가 메뉴헤더 설계이커머스 UX정보 구조

이 글은 누구를 위한 것인가

  • 카테고리가 많아져서 메뉴 구조가 복잡해진 이커머스 팀
  • 모바일에서 햄버거 메뉴 클릭율이 너무 낮은 팀
  • 메가 메뉴를 처음 설계하는 UX 디자이너·개발자

들어가며

네비게이션은 "유저가 원하는 것을 빠르게 찾게 돕는" 것이 전부다. 메뉴 항목이 8개 이상이면 메가 메뉴를 고려해야 하고(메가 메뉴가 잘 작동하는 이유), 모바일에서 햄버거 메뉴는 클릭율이 낮으므로 대안을 찾아야 한다. 키보드 동작은 ARIA menubar 패턴을 기준으로 구현한다.

이 글은 bluefoxdev.kr의 이커머스 네비게이션 설계 를 참고하여 작성했습니다.


1. 네비게이션 패턴 선택 가이드

[카테고리 수별 네비게이션 패턴]

5개 이하:
  패턴: 수평 탭 네비게이션
  예: 홈 | 상품 | 세일 | 브랜드 | 마이페이지

6-12개:
  패턴: 드롭다운 메뉴
  각 카테고리에 서브메뉴

13개 이상:
  패턴: 메가 메뉴 (2-3단 열 구조)
  카테고리 + 서브카테고리 + 브랜드/추천

[메가 메뉴 내부 구조]
  1열: 주요 카테고리 목록 (세로)
  2열: 선택된 카테고리의 서브카테고리
  3열 (선택): 브랜드, 추천 상품, 배너

[모바일 네비게이션 대안]
  ❌ 햄버거 메뉴: 클릭율 낮음, 발견 어려움
  ✅ 탭 바 (5개 이하): 하단 고정, 항상 보임
  ✅ 검색 우선: 검색창을 전면 배치
  ✅ 카테고리 퀵링크: 홈 화면에 카테고리 아이콘

2. 메가 메뉴 구현

"use client";
import { useState, useRef, useCallback } from "react";

interface Category {
  id: string;
  name: string;
  slug: string;
  subcategories: Subcategory[];
  featuredBrands?: string[];
}

function MegaMenu({ categories }: { categories: Category[] }) {
  const [activeCategory, setActiveCategory] = useState<string | null>(null);
  const closeTimer = useRef<NodeJS.Timeout>();

  const handleMouseEnter = useCallback((categoryId: string) => {
    if (closeTimer.current) clearTimeout(closeTimer.current);
    setActiveCategory(categoryId);
  }, []);

  const handleMouseLeave = useCallback(() => {
    // 약간의 지연으로 실수로 메뉴가 닫히는 것 방지
    closeTimer.current = setTimeout(() => {
      setActiveCategory(null);
    }, 150);
  }, []);

  const activeData = categories.find((c) => c.id === activeCategory);

  return (
    <nav className="relative" onMouseLeave={handleMouseLeave}>
      {/* 카테고리 탭 */}
      <ul className="flex items-center gap-1">
        {categories.map((category) => (
          <li key={category.id}>
            <a
              href={`/category/${category.slug}`}
              className={`px-4 py-5 text-sm font-medium hover:text-blue-600 block ${
                activeCategory === category.id ? "text-blue-600 border-b-2 border-blue-600" : "text-gray-700"
              }`}
              onMouseEnter={() => handleMouseEnter(category.id)}
            >
              {category.name}
            </a>
          </li>
        ))}
      </ul>

      {/* 메가 메뉴 패널 */}
      {activeData && (
        <div
          className="absolute top-full left-0 right-0 bg-white border-t shadow-lg z-50"
          onMouseEnter={() => handleMouseEnter(activeData.id)}
        >
          <div className="max-w-7xl mx-auto px-4 py-6 grid grid-cols-4 gap-8">
            {/* 서브카테고리 그리드 */}
            <div className="col-span-3 grid grid-cols-3 gap-4">
              {activeData.subcategories.map((sub) => (
                <div key={sub.id}>
                  <h3 className="font-semibold text-sm mb-2 text-gray-900">
                    <a href={`/category/${sub.slug}`} className="hover:text-blue-600">
                      {sub.name}
                    </a>
                  </h3>
                  <ul className="space-y-1">
                    {sub.children?.map((child) => (
                      <li key={child.id}>
                        <a
                          href={`/category/${child.slug}`}
                          className="text-sm text-gray-500 hover:text-blue-600"
                        >
                          {child.name}
                        </a>
                      </li>
                    ))}
                  </ul>
                </div>
              ))}
            </div>

            {/* 추천 배너 */}
            <div className="col-span-1">
              <div className="bg-gray-50 rounded-lg p-4">
                <p className="text-xs text-gray-500 mb-2">이번 주 추천</p>
                <a href={`/category/${activeData.slug}/sale`} className="block">
                  <img
                    src={activeData.bannerUrl}
                    alt="추천 배너"
                    className="w-full rounded-lg"
                  />
                </a>
              </div>
            </div>
          </div>
        </div>
      )}
    </nav>
  );
}

마무리

메가 메뉴 설계에서 가장 중요한 것은 "마우스 딜레이 존"이다. 사용자가 대각선으로 마우스를 이동할 때 메뉴가 닫혀버리면 답답하다. 150ms 지연으로 이 문제를 해결하고, 메뉴 호버 영역을 패널까지 연결해야 한다. 모바일에서는 메가 메뉴 대신 검색과 카테고리 퀵링크로 대체하는 것이 전환율에 더 좋다.